Filtres de sujet personnalisés : les paramètres URL n'atteignent pas `TopicQuery.options`

Lors de la création d’un plugin avec des filtres de liste de sujets personnalisés (par exemple, par prix, localisation) en utilisant des paramètres de requête URL. L’URL se met à jour correctement (par exemple, ...?market_item_statuses=Available), mais les paramètres n’apparaissent pas dans topic_query.options côté serveur.

Configuration :

  1. Enregistrement des paramètres côté client (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. Tentative de liste blanche côté serveur (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
    
    # après_initialisation
      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 } # Simplifié pour la clarté
          end
          Rails.logger.info "[Tecenc] Enregistré avec 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] Liste blanche étendue avec extra_options_whitelist."
        else
          Rails.logger.warn "[Tecenc] AVERTISSEMENT_PLUG : Impossible de trouver une méthode pour ajouter une liste blanche des paramètres pour TopicQuery."
        end
    
  3. Logique de filtrage côté serveur (plugin.rb) :

    # plugin.rb (dans after_initialize / si activé)
    ::TopicQuery.add_custom_filter(:"tecenc_filters") do |topics, topic_query|
      opts = topic_query.options
      Rails.logger.info "[Tecenc_TopicQuery] Options : #{opts.inspect}" # LOG CRITIQUE
    
      # market_params_present = ::Tecenc::MARKET_PARAMS_KEYS.any? { |p| opts[p].present? }
      # si les paramètres du marché sont présents
      #   # ... logique de filtrage utilisant opts[key] ...
      # fin
      topics # ou topics modifiés
    end
    

Problème (Logs) :

  1. Échec de la liste blanche des paramètres :

    [Tecenc] AVERTISSEMENT_PLUG : Impossible de trouver une méthode adaptée (add_custom_param_handler ou extra_options_whitelist) pour ajouter une liste blanche des paramètres personnalisés pour TopicQuery.
    
  2. opts dans TopicQuery.add_custom_filter ne contient pas nos paramètres personnalisés :
    Lorsqu’on ajoute ...?market_item_statuses=Available dans l’URL, le log montre :

    [Tecenc_TopicQuery] Options : {:category=>5, :filter=>"default", :topic_ids=>nil, :category_id=>5}
    ```Notre `market_item_statuses` (et d'autres paramètres personnalisés) ne sont pas présents.
    
    

Notre environnement :

Questions :

  1. Quelle est la meilleure pratique actuelle pour faire en sorte que les paramètres de requête URL personnalisés atteignent topic_query.options dans les versions récentes de Discourse ?
  2. Pourquoi nos tentatives d’utiliser add_custom_param_handler ou extra_options_whitelist échouent-elles avec l’avertissement “Could not find a suitable method” ?
  3. Existe-t-il une méthode alternative pour la registration des paramètres avec TopicQuery que nous devrions utiliser ?

Toute assistance serait grandement appréciée !

Je viens de remarquer que add_custom_param_handler n’est même pas disponible en tant que méthode sur TopicQuery. Existe-t-il une autre façon de créer des filtres personnalisés pour les sujets dans les versions plus récentes de Discourse ?

Méthodes 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]

Rapport d’avancement jusqu’à présent :

  1. Confirmation que les méthodes de liste blanche statiques TopicQuery telles que add_custom_param_handler ou extra_options_whitelist ne sont pas disponibles en tant que méthodes de classe dans ma version de Discourse, donc ces approches ont été abandonnées.

  2. Mise en œuvre d’un patch pour ListController#build_topic_list_options afin d’injecter mes paramètres d’URL personnalisés (par exemple, market_item_statuses, market_price_min) dans la hache opts avant que TopicQuery.new ne soit appelé.

  3. Cette partie fonctionne maintenant ! Lorsque je fais une requête comme /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available, mes logs serveur confirment l’injection :

    [TecencMarket_ListControllerExt_BuildOptsV3] Injecté dans opts : market_item_statuses = Available
    

    Donc, TopicQuery.new(user, opts) reçoit maintenant opts contenant mes paramètres personnalisés.

Point bloquant actuel : erreur 500 & blocage du filtre non atteint

Malgré le fait que les paramètres soient maintenant correctement passés à l’initialiseur de TopicQuery, je reçois toujours une erreur 500 Internal Server Error lorsque la requête inclut ces paramètres de marché personnalisés.

Pour isoler cela, j’ai simplifié mon bloc TopicQuery.add_custom_filter(:"tecenc_market_filters") au maximum.

# plugin.rb - Bloc de filtre personnalisé simplifié actuel
if ::TopicQuery.respond_to?(:add_custom_filter)
  ::TopicQuery.add_custom_filter(:"tecenc_market_filters") do |topics, topic_query|
    relation_orig = topics 
    opts = topic_query.options
    log_prefix_query = "[TecencMarket_SimplifiedFilter_V1.1_Test]" # Mon préfixe de débogage

    Rails.logger.info "#{log_prefix_query} Options reçues par TopicQuery : #{opts.inspect}"

    if opts[:market_item_statuses].present?
      Rails.logger.info "#{log_prefix_query} 'market_item_statuses' EST PRÉSENT dans opts : #{opts[:market_item_statuses]}"
    else
      Rails.logger.info "#{log_prefix_query} 'market_item_statuses' N'EST PAS PRÉSENT dans opts."
    end
    
    Rails.logger.info "#{log_prefix_query} Retourne la relation de sujets originale."
    relation_orig # Retour implicite
  end
  Rails.logger.info "[TecencMarket] Filtre personnalisé SIMPLIFIÉ (V1.1_Test) appliqué."
end

Observations avec ce filtre simplifié :

  • Requêtes sans mon paramètre de filtre personnalisé (par exemple, /c/market/5/l/latest.json?filter=default) :

    • Le bloc de filtre simplifié enregistre correctement :

      [TecencMarket_SimplifiedFilter_V1.1_Test] Options reçues par TopicQuery : {:category=>5, :filter=>"default", ...}
      [TecencMarket_SimplifiedFilter_V1.1_Test] 'market_item_statuses' N'EST PAS PRÉSENT dans opts.
      [TecencMarket_SimplifiedFilter_V1.1_Test] Retourne la relation de sujets originale.
      
    • La page se charge avec un 200 OK.

  • Requêtes avec mon paramètre de filtre personnalisé (par exemple, /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available) :

    • L’entrée de log du patch du ListController apparaît : [TecencMarket_ListControllerExt_BuildOptsV3] Injecté dans opts : market_item_statuses = Available

    {“content”: "* Une Completed 500 Internal Server Error in ...ms se produit immédiatement après.

    • De manière cruciale, AUCUN journal provenant de la zone de filtrage simplifiée (pas de lignes [TecencMarket_SimplifiedFilter_V1.1_Test]...) n’apparaît pour cette requête échouée.

Cela indique que l’erreur 500 se produit après que TopicQuery a été initialisé avec le hash opts (qui contient désormais mes paramètres personnalisés), mais avant ou juste au moment où le mécanisme central de Discourse TopicQuery#apply_custom_filters tente d’exécuter mon bloc de filtre enregistré (et maintenant extrêmement simple).

J’ai du mal à isoler l’exception Ruby spécifique et la trace complète dans development.log qui précèdent immédiatement la ligne “Completed 500…” pour les requêtes échouées (les extraits de journal que j’ai rassemblés montrent la ligne 500 elle-même, mais pas le message d’erreur détaillé juste avant).

Question de suivi :

Étant donné que :

  1. Les paramètres personnalisés sont maintenant injectés avec succès dans TopicQuery.options par la correction du ListController.

  2. Une erreur 500 se produit lorsque ces paramètres personnalisés sont présents dans opts.

  3. Cette erreur 500 se produit avant même qu’un bloc de filtre personnalisé extrêmement simplifié (qui ne fait que enregistrer et renvoyer la relation originale) ne commence à exécuter sa première ligne de code pour la requête avec ces paramètres.

  4. Cela concerne Discourse 3.5.0.beta3-dev.

Qu’est-ce qui pourrait faire que TopicQuery lui-même, ou sa méthode apply_custom_filters, plante avant d’appeler un bloc de filtre personnalisé enregistré, spécifiquement lorsque le hash options contient ces clés spécifiques au plugin ? Le LocalJumpError que nous soupçonnions pourrait-il encore être pertinent à un niveau inférieur dans la gestion de l’itération ou de l’appel des blocs de filtre par apply_custom_filters, même si mon bloc simplifié est simplement un retour implicite ?

Tout conseil sur ce qu’il faut vérifier ensuite dans le comportement de TopicQuery avec ces options personnalisées, ou des astuces pour obtenir de manière robuste la trace complète pour l’erreur 500 dans ce scénario, seraient très utiles."}