Filtros de Tópicos Personalizados: Parâmetros de URL Não Chegam ao `TopicQuery.options`

Ao construir um plugin com filtros personalizados na lista de tópicos (por exemplo, por preço, localização) usando parâmetros de consulta na URL. A URL atualiza corretamente (por exemplo, ...?market_item_statuses=Available), mas os parâmetros não aparecem em topic_query.options no servidor.

Configuração:

  1. Registro de parâmetros no lado do 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. Tentativa de whitelist do lado do 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 brevidade
          end
          Rails.logger.info "[Tecenc] Registrado com 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] expandida a whitelist de opções extras."
        else
          Rails.logger.warn "[Tecenc] AVISO_DO_PLUGIN: Não foi possível encontrar método para whitelist de parâmetros em TopicQuery."
        end
    
  3. Lógica de filtragem no servidor (plugin.rb):

    # plugin.rb (dentro de after_initialize / se habilitado)
    ::TopicQuery.add_custom_filter(:"tecenc_filters") do |topics, topic_query|
      opts = topic_query.options
      Rails.logger.info "[Tecenc_TopicQuery] Opts: #{opts.inspect}" # LOG CRÍTICO
    
      # market_params_present = ::Tecenc::MARKET_PARAMS_KEYS.any? { |p| opts[p].present? }
      # if market_params_present
      #   # ... lógica de filtragem usando opts[chave] ...
      # end
      topics # ou tópicos filtrados
    end
    

O Problema (Logs):

  1. Falha na whitelist de parâmetros:

    [Tecenc] AVISO_DO_PLUG: Não foi possível encontrar um método adequado (add_custom_param_handler ou extra_options_whitelist) para whitelist de parâmetros personalizados para TopicQuery.
    
  2. opts no TopicQuery.add_custom_filter está ausente dos nossos parâmetros personalizados:
    Quando a URL é ...?market_item_statuses=Available, o log mostra:

    [Tecenc_TopicQuery] Opts: {:category=>5, :filter=>"default", :topic_ids=>nil, :category_id=>5}
    ```Nossos `market_item_statuses` (e outros parâmetros personalizados) não estão presentes.
    
    

Nosso Ambiente:

Perguntas:

  1. Qual é a melhor prática atual para garantir que os parâmetros de consulta URL personalizados cheguem ao topic_query.options em versões recentes do Discourse?
  2. Por que nossas tentativas de usar add_custom_param_handler ou extra_options_whitelist podem estar falhando com o aviso “Could not find a suitable method”?
  3. Existe uma abordagem alternativa para o registro de parâmetros com TopicQuery que devemos usar?

Qualquer ajuda seria muito apreciada!

Acabei de notar que add_custom_param_handler nem mesmo está disponível como método em TopicQuery. Existe outra maneira de construir filtros personalizados para tópicos nas versões mais recentes do discourse?

Métodos singleton de TopicQuery: [: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]

Relatório de Progresso até agora:

  1. Confirmado que métodos estáticos de whitelisting de TopicQuery, como add_custom_param_handler ou extra_options_whitelist, não estão disponíveis como métodos de classe na minha versão do Discourse, então essas abordagens foram abandonadas.

  2. Implementado um patch para ListController#build_topic_list_options para injetar meus parâmetros de URL personalizados (por exemplo, market_item_statuses, market_price_min) no hash opts antes que TopicQuery.new seja chamado.

  3. Essa parte agora está funcionando! Quando faço uma solicitação como /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available, meus logs do servidor confirmam a injeção:

    [TecencMarket_ListControllerExt_BuildOptsV3] Injetado em opts: market_item_statuses = Available
    
    

    Portanto, TopicQuery.new(user, opts) agora está recebendo opts contendo meus parâmetros personalizados.

Ponto de bloqueio atual: Erro 500 & Filtro não alcançado

Apesar dos parâmetros agora estarem sendo passados corretamente para o inicializador de TopicQuery, ainda recebo um Erro Interno do Servidor 500 quando a requisição inclui esses parâmetros de mercado personalizados.

Para isolar isso, simplifiquei meu bloco de TopicQuery.add_custom_filter(:"tecenc_market_filters") ao máximo.

# plugin.rb - Bloco de filtro personalizado simplificado atual
if ::TopicQuery.respond_to?(:add_custom_filter)
  ::TopicQuery.add_custom_filter(:"tecenc_market_filters") do |topics, topic_query|
    relation_original = topics 
    opts = topic_query.options
    prefixo_log = "[TecencMarket_SimplifiedFilter_V1.1_Test]" # Meu prefixo de debug

    Rails.logger.info "#{prefixo_log} Opts recebidos pelo TopicQuery: #{opts.inspect}"

    if opts[:market_item_statuses].present?
      Rails.logger.info "#{prefixo_log} 'market_item_statuses' ESTÁ PRESENTE em opts: #{opts[:market_item_statuses]}"
    else
      Rails.logger.info "#{prefixo_log} 'market_item_statuses' NÃO ESTÁ PRESENTE em opts."
    end
    
    Rails.logger.info "#{prefixo_log} Retornando relação original de tópicos."
    relation_original # retorno implícito
  end
  Rails.logger.info "[TecencMarket] Aplicado filtro personalizado SIMPLIFICADO (V1.1_Test)."
end

Observações com esse filtro simplificado:

  • Requisições sem meu parâmetro de filtro personalizado (por exemplo, /c/market/5/l/latest.json?filter=default):

    • O log do filtro simplificado é exibido corretamente:

      [TecencMarket_SimplifiedFilter_V1.1_Test] Opts recebidos pelo TopicQuery: {:category=>5, :filter=>"default", ...}
      [TecencMarket_SimplifiedFilter_V1.1_Test] 'market_item_statuses' NÃO ESTÁ em opts.
      [TecencMarket_SimplifiedFilter_V1.1_Test] Retornando relação original de tópicos.
      
      
    • A página carrega com um 200 OK.

  • Requisições com meu parâmetro de filtro personalizado (por exemplo, /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available):

    • O log do patch do ListController aparece: [TecencMarket_ListControllerExt_BuildOptsV3] Injected into opts: market_item_statuses = Available

    • Um Completed 500 Internal Server Error in ...ms ocorre imediatamente após.

    • Crucialmente, NENHUM log do interior do bloco de filtro simplificado (sem as linhas [TecencMarket_SimplifiedFilter_V1.1_Test]...) aparece para essa requisição que falha.

Isso indica que o erro 500 acontece após o TopicQuery ser inicializado com o hash opts (que agora contém meus parâmetros personalizados), mas antes ou exatamente no momento em que o mecanismo central do Discourse TopicQuery#apply_custom_filters tenta executar meu bloco de filtro registrado (e agora extremamente simples).

Estou tendo dificuldade em isolar a exceção Ruby específica e o backtrace completo do development.log que precede imediatamente a linha “Completed 500…” para as requisições que falham (os trechos de log que reuni mostram apenas a linha 500, mas não a mensagem de erro detalhada anterior a ela).

Pergunta de acompanhamento:

Dado que:

  1. Parâmetros personalizados agora estão sendo injetados com sucesso em TopicQuery.options pelo patch do ListController.

  2. Um erro 500 ocorre quando esses parâmetros personalizados estão presentes em opts.

  3. Esse erro 500 acontece antes mesmo de o bloco de filtro personalizado extremamente simplificado (que apenas registra e retorna a relação original) executar sua primeira linha de código na requisição com parâmetros personalizados.

  4. Isso está na versão 3.5.0.beta3-dev do Discourse.

O que poderia estar causando o TopicQuery ou o método apply_custom_filters a apresentarem erro antes de invocar um filtro personalizado registrado, especificamente quando o hash options contém essas chaves específicas do plugin? A erro LocalJumpError que suspeitávamos ainda pode ser relevante em um nível inferior na forma como o apply_custom_filters lida com a iteração ou chamada dos blocos de filtro, mesmo que meu bloco simplificado seja apenas um retorno implícito?

Qualquer orientação sobre o que verificar a seguir no comportamento do TopicQuery com esses tipos de opções personalizadas, ou conselhos sobre como obter de forma robusta o backtrace completo para o erro 500 nesse cenário, seria imensamente útil.