Filtros de temas personalizados: parámetros URL no llegan a `TopicQuery.options`

Mientras construye un plugin con filtros de lista de temas personalizados (por ejemplo, por precio, ubicación) usando parámetros de consulta en la URL. La URL se actualiza correctamente (por ejemplo, ...?market_item_statuses=Available), pero los parámetros no aparecen en topic_query.options en el servidor.

Configuración:

  1. Registro de parámetros en el lado del cliente (tecenc-discovery-params.js):

    // tecenc-discovery-params.js
    import { apiInitializer } from "discourse/lib/api";
    
    export default apiInitializer("1.37.3", (api) => {
      const MARKET_PARAMS_KEYS = [
        "market_price_min", "market_price_max", "market_location",
        "market_conditions", "market_warranty", "market_item_statuses"
      ];
      MARKET_PARAMS_KEYS.forEach(paramKey => {
        api.addDiscoveryQueryParam(paramKey, { replace: true, refreshModel: true });
      });
    });
    
  2. Intento de inclusión en el lado del servidor (plugin.rb):

    # plugin.rb
    module ::Tecenc
      MARKET_PARAMS_KEYS = [
        :market_price_min, :market_price_max, :market_location,
        :market_conditions, :market_warranty, :market_item_statuses
      ].freeze
    end
    
    # after_initialize do
      if SiteSetting.tecenc_enabled?
        if defined?(::TopicQuery.add_custom_param_handler)
          ::Tecenc::MARKET_PARAMS_KEYS.each do |param_key|
            ::TopicQuery.add_custom_param_handler(param_key) { |value| value } # Simplificado para brevedad
          end
          Rails.logger.info "[Tecenc] Registrado con add_custom_param_handler."
        elsif defined?(::TopicQuery) && ::TopicQuery.methods.include?(:extra_options_whitelist)
          current_whitelist = ::TopicQuery.extra_options_whitelist || []
          new_whitelist = (current_whitelist + ::Tecenc::MARKET_PARAMS_KEYS).uniq
          ::TopicQuery.extra_options_whitelist(*new_whitelist)
          Rails.logger.info "[Tecenc] Ampliado el whitelist de opciones adicionales."
        else
          Rails.logger.warn "[Tecenc] ADVERTENCIA DEL PLUGIN: No se pudo encontrar un método para incluir en la lista blanca los parámetros para TopicQuery."
        end
    
  3. Lógica de filtrado en el lado del servidor (plugin.rb):

    # plugin.rb (dentro de after_initialize / si está habilitado)
    ::TopicQuery.add_custom_filter(:"tecenc_filters") do |topics, topic_query|
      opts = topic_query.options
      Rails.logger.info "[Tecenc_TopicQuery] Opciones: #{opts.inspect}" # LOG CRÍTICO
    
      # market_params_present = ::Tecenc::MARKET_PARAMS_KEYS.any? { |p| opts[p].present? }
      # if market_params_present
      #   # ... lógica de filtrado usando opts[key] ...
      # end
      topics # o topics filtrados
    end
    

El problema (Registros):

  1. Fallo en la inclusión de listas blancas de parámetros:

    [Tecenc] PLUGIN_WARN: No se pudo encontrar un método adecuado (add_custom_param_handler o extra_options_whitelist) para incluir en la lista blanca los parámetros personalizados para TopicQuery.
    
  2. opts en TopicQuery.add_custom_filter no contiene nuestros parámetros personalizados:
    Cuando la URL es ...?market_item_statuses=Available, la log muestra:

    [Tecenc_TopicQuery] Opciones: {:category=>5, :filter=>"default", :topic_ids=>nil, :category_id=>5}
    

Nuestros market_item_statuses (y otros parámetros personalizados) no están presentes.

Nuestro entorno:

Preguntas:

  1. ¿Cuál es la mejor práctica actual para garantizar que los parámetros personalizados en la URL lleguen a topic_query.options en versiones recientes de Discourse?
  2. ¿Por qué podrían estar fallando nuestros intentos de usar add_custom_param_handler o extra_options_whitelist con la advertencia “No se pudo encontrar un método adecuado”?
  3. ¿Existe un enfoque alternativo para el registro de parámetros con TopicQuery que deberíamos usar?

¡Cualquier ayuda será muy apreciada!

Acabo de notar que add_custom_param_handler ni siquiera está disponible como un método en TopicQuery. ¿Hay alguna otra forma de construir filtros personalizados para temas en las versiones más recientes de Discourse?

TopicQuery singleton_methods: [:add_custom_filter, :apply_custom_filters, :new_filter, :public_valid_options, :remove_custom_filter, :remove_muted_tags, :results_filter_callbacks, :results_filter_callbacks=, :tracked_filter, :unread_filter, :valid_options, :validate?, :validators, :yaml_tag]

Informe de progreso hasta ahora:

  1. Confirmé que los métodos de lista blanca estática de TopicQuery como add_custom_param_handler o extra_options_whitelist no están disponibles como métodos de clase en mi versión de Discourse, por lo que esos enfoques fueron descartados.

  2. Implementé un parche para ListController#build_topic_list_options para inyectar mis parámetros URL personalizados (por ejemplo, market_item_statuses, market_price_min) en el hash opts antes de que se llame a TopicQuery.new.

  3. ¡Esta parte ahora funciona! Cuando hago una solicitud como /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available, los registros de mi servidor confirman la inyección:

    [TecencMarket_ListControllerExt_BuildOptsV3] Inyectado en opts: market_item_statuses = Available
    
    

    Entonces, TopicQuery.new(user, opts) ahora recibe opts que contienen mis parámetros personalizados.

Punto de bloqueo actual: error 500 y filtro no alcanzado

A pesar de que los parámetros ahora se pasan correctamente a la inicialización de TopicQuery, todavía obtengo un error 500 Internal Server Error cuando la solicitud incluye estos parámetros de mercado personalizados.

Para aislar esto, simplifiqué mi bloque de TopicQuery.add_custom_filter(:"tecenc_market_filters") al mínimo absoluto.

# plugin.rb - Bloque de filtro personalizado simplificado actual
if ::TopicQuery.respond_to?(:add_custom_filter)
  ::TopicQuery.add_custom_filter(:"tecenc_market_filters") do |topics, topic_query|
    relación_original_topics = topics 
    opts = topic_query.options
    log_prefix_query = "[TecencMarket_SimplifiedFilter_V1.1_Test]" # Prefijo de depuración

    Rails.logger.info "#{log_prefix_query} Opciones recibidas por TopicQuery: #{opts.inspect}"

    if opts[:market_item_statuses].present?
      Rails.logger.info "#{log_prefix_query} 'market_item_statuses' ESTÁ PRESENTE en opts: #{opts[:market_item_statuses]}"
    else
      Rails.logger.info "#{log_prefix_query} 'market_item_statuses' NO ESTÁ PRESENTE en opts."
    end
    
    Rails.logger.info "#{log_prefix_query} Devolviendo la relación de temas original."
    relación_original_topics # Retorno implícito
  end
  Rails.logger.info "[TecencMarket] Se aplicó filtro personalizado SIMPLIFICADO (V1.1_Test)."
end

Observaciones con este filtro simplificado:

  • Solicitudes sin mi parámetro de filtro personalizado (por ejemplo, solo /c/market/5/l/latest.json?filter=default):

    • El registro del filtro simplificado se registra correctamente:

      [TecencMarket_SimplifiedFilter_V1.1_Test] Opciones recibidas por TopicQuery: {:category=>5, :filter=>"default", ...}
      [TecencMarket_SimplifiedFilter_V1.1_Test] 'market_item_statuses' NO ESTÁ PRESENTE en opts.
      [TecencMarket_SimplifiedFilter_V1.1_Test] Devolviendo la relación de temas original.
      
      
    • La página carga con un 200 OK.

  • Solicitudes con mi parámetro de filtro personalizado (por ejemplo, /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available):

    • El registro del parche de ListController aparece: [TecencMarket_ListControllerExt_BuildOptsV3] Inyectado en opts: market_item_statuses = Available

    {“type”:“object”,“properties”:{“translation”:"* Un Completed 500 Internal Server Error in ...ms ocurre inmediatamente después.

    • Esencialmente, NO aparecen registros del bloque de filtro simplificado interno (sin líneas [TecencMarket_SimplifiedFilter_V1.1_Test]...) para esta solicitud fallida.

Esto indica que el error 500 ocurre después de que TopicQuery se inicializa con el hash opts (que ahora contiene mis parámetros personalizados), pero antes o justo cuando el mecanismo principal de Discourse TopicQuery#apply_custom_filters intenta ejecutar mi bloque de filtro registrado (y ahora extremadamente simple).

Estoy teniendo dificultades para aislar la excepción Ruby específica y la pila de llamadas completa desde development.log que precede inmediatamente a la línea “Completed 500…” para las solicitudes que fallan (los fragmentos de registro que he recopilado muestran la línea 500 en sí, pero no el mensaje de error detallado justo antes).

Pregunta de seguimiento:

Dado que:

  1. Los parámetros personalizados ahora se inyectan con éxito en TopicQuery.options por el parche en ListController.

  2. Se produce un error 500 cuando estos parámetros personalizados están presentes en opts.

  3. Este error 500 ocurre antes de que incluso un bloque de filtro personalizado extremadamente simplificado (que solo registra y devuelve la relación original) llegue a ejecutar su primera línea de código en la solicitud con parámetros personalizados.

  4. Esto es en Discourse 3.5.0.beta3-dev.

¿Qué podría estar causando que TopicQuery en sí mismo, o su método apply_custom_filters, generen un error antes de invocar un bloque de filtro personalizado registrado, específicamente cuando el hash options contiene estas claves específicas del plugin? ¿El LocalJumpError que sospechamos aún podría ser relevante a un nivel más bajo en cómo apply_custom_filters maneja la iteración o la llamada a los bloques de filtro, incluso si mi bloque simplificado es solo un retorno implícito?

Cualquier guía sobre qué verificar a continuación en el comportamiento de TopicQuery con estos tipos de opciones personalizadas, o consejo sobre cómo obtener de forma robusta la pila de llamadas completa para el error 500 en este escenario, sería de gran ayuda."}