Пользовательские фильтры тем: параметры URL не доходят до `TopicQuery.options`

При создании плагина с пользовательскими фильтрами списка тем (например, по цене, местоположению) с использованием параметров запроса URL. URL обновляется корректно (например, ...?market_item_statuses=Available), но параметры не отображаются в topic_query.options на сервере.

Настройка:

  1. Регистрация параметров на стороне клиента (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. Попытка добавления в белый список на стороне сервера (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 } # Упрощено для краткости
          end
          Rails.logger.info "[Tecenc] Registered with 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] Extended extra_options_whitelist."
        else
          Rails.logger.warn "[Tecenc] PLUGIN_WARN: Could not find method to whitelist params for TopicQuery."
        end
    
  3. Логика фильтрации на стороне сервера (plugin.rb):

    # plugin.rb (внутри after_initialize / если включено)
    ::TopicQuery.add_custom_filter(:"tecenc_filters") do |topics, topic_query|
      opts = topic_query.options
      Rails.logger.info "[Tecenc_TopicQuery] Opts: #{opts.inspect}" # КРИТИЧЕСКИЙ ЛОГ
    
      # market_params_present = ::Tecenc::MARKET_PARAMS_KEYS.any? { |p| opts[p].present? }
      # if market_params_present
      #   # ... логика фильтрации с использованием opts[key] ...
      # end
      topics # или modified_topics
    end
    

Проблема (логи):

  1. Добавление параметров в белый список не удается:

    [Tecenc] PLUGIN_WARN: Could not find a suitable method (add_custom_param_handler or extra_options_whitelist) to whitelist custom params for TopicQuery.
    
  2. В opts внутри TopicQuery.add_custom_filter отсутствуют наши пользовательские параметры:
    Когда URL выглядит как ...?market_item_statuses=Available, в логе отображается:

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

    Наши market_item_statuses (и другие пользовательские параметры) отсутствуют.

Наша среда:

Вопросы:

  1. Какова текущая лучшая практика для обеспечения того, чтобы пользовательские параметры запроса URL попадали в topic_query.options в последних версиях Discourse?
  2. Почему наши попытки использовать add_custom_param_handler или extra_options_whitelist могут завершаться неудачей с предупреждением “Could not find a suitable method”?
  3. Существует ли альтернативный подход к регистрации параметров с TopicQuery, который нам следует использовать?

Любая помощь будет очень ценной!

Только что заметил, что add_custom_param_handler даже не доступен как метод в TopicQuery. Есть ли другой способ создавать пользовательские фильтры для тем в новых версиях Discourse?

Одиночные методы 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]

Отчет о прогрессе на данный момент:

  1. Подтверждено, что статические методы белых списков TopicQuery, такие как add_custom_param_handler или extra_options_whitelist, недоступны как методы класса в моей версии Discourse, поэтому от этих подходов пришлось отказаться.

  2. Внедрена патч-заплатка для ListController#build_topic_list_options, чтобы инжектировать мои пользовательские параметры URL (например, market_item_statuses, market_price_min) в хэш opts перед вызовом TopicQuery.new.

  3. Эта часть теперь работает! Когда я делаю запрос вроде /c/market/5/l/latest.json?filter=default&market_item_statuses=Available, логи моего сервера подтверждают инжекцию:

    [TecencMarket_ListControllerExt_BuildOptsV3] Injected into opts: market_item_statuses = Available
    
    

    Таким образом, TopicQuery.new(user, opts) теперь получает opts, содержащий мои пользовательские параметры.

Текущее препятствие: ошибка 500 и блок фильтра не вызывается

Несмотря на то, что параметры теперь корректно передаются в инициализатор TopicQuery, я все еще получаю ошибку 500 Internal Server Error, когда запрос включает эти пользовательские параметры рынка.

Чтобы изолировать проблему, я упростил блок TopicQuery.add_custom_filter(:"tecenc_market_filters") до абсолютного минимума.

# plugin.rb - Текущий упрощенный блок пользовательского фильтра
if ::TopicQuery.respond_to?(:add_custom_filter)
  ::TopicQuery.add_custom_filter(:"tecenc_market_filters") do |topics, topic_query|
    original_topics_relation = topics 
    opts = topic_query.options
    log_prefix_query = "[TecencMarket_SimplifiedFilter_V1.1_Test]" # Мой отладочный префикс

    Rails.logger.info "#{log_prefix_query} Opts received by TopicQuery: #{opts.inspect}"

    if opts[:market_item_statuses].present?
      Rails.logger.info "#{log_prefix_query} 'market_item_statuses' IS PRESENT in opts: #{opts[:market_item_statuses]}"
    else
      Rails.logger.info "#{log_prefix_query} 'market_item_statuses' IS NOT PRESENT in opts."
    end
    
    Rails.logger.info "#{log_prefix_query} Returning original topics relation."
    original_topics_relation # Неявный возврат
  end
  Rails.logger.info "[TecencMarket] Applied SIMPLIFIED custom filter (V1.1_Test)."
end

Наблюдения с этим упрощенным фильтром:

  • Запросы без моего пользовательского параметра фильтра (например, просто /c/market/5/l/latest.json?filter=default):

    • Блок упрощенного фильтра логируется корректно:

      [TecencMarket_SimplifiedFilter_V1.1_Test] Opts received by TopicQuery: {:category=>5, :filter=>"default", ...}
      [TecencMarket_SimplifiedFilter_V1.1_Test] 'market_item_statuses' IS NOT PRESENT in opts.
      [TecencMarket_SimplifiedFilter_V1.1_Test] Returning original topics relation.
      
      
    • Страница загружается с кодом 200 OK.

  • Запросы с моим пользовательским параметром фильтра (например, /c/market/5/l/latest.json?filter=default&market_item_statuses=Available):

    • Появляется лог патча ListController: [TecencMarket_ListControllerExt_BuildOptsV3] Injected into opts: market_item_statuses = Available

    • Сразу после этого происходит Completed 500 Internal Server Error in ...ms.

    • Критически важно: в логах для этого неудачного запроса НЕТ записей изнутри упрощенного блока фильтра (нет строк вида [TecencMarket_SimplifiedFilter_V1.1_Test]...).

Это указывает на то, что ошибка 500 возникает после инициализации TopicQuery с хэшем opts (который теперь содержит мои пользовательские параметры), но до или в момент, когда основной механизм Discourse TopicQuery#apply_custom_filters пытается выполнить мой зарегистрированный (и теперь крайне простой) блок фильтра.

Мне трудно изолировать конкретное исключение Ruby и полный стек вызовов из development.log, которые предшествуют строке «Completed 500…» для неудачных запросов (собранные мной фрагменты логов показывают саму строку 500, но не детальное сообщение об ошибке сразу перед ней).

Дополнительный вопрос:

Учитывая, что:

  1. Пользовательские параметры теперь успешно инжектируются в TopicQuery.options патчем ListController.

  2. Ошибка 500 возникает, когда эти пользовательские параметры присутствуют в opts.

  3. Эта ошибка 500 происходит до того, как даже крайне упрощенный блок пользовательского фильтра (который только логирует и возвращает исходное отношение) успевает выполнить первую строку своего кода для запроса с пользовательскими параметрами.

  4. Это происходит на версии Discourse 3.5.0.beta3-dev.

Что может вызывать ошибку в самом TopicQuery или в его методе apply_custom_filters перед вызовом зарегистрированного блока пользовательского фильтра, особенно когда хэш options содержит эти специфичные для плагина ключи? Может ли ранее подозреваемая LocalJumpError все еще быть актуальной на более низком уровне, в том, как apply_custom_filters обрабатывает итерацию или вызов блоков фильтров, даже если мой упрощенный блок просто содержит неявный возврат?

Любые рекомендации о том, что проверить дальше в поведении TopicQuery с такими пользовательскими опциями, или советы, как надежно получить полный стек вызовов для ошибки 500 в этой ситуации, были бы чрезвычайно полезны.