Erreur `NoMethodError in TopicsController#show` après la fusion des sujets

Priorité/Gravité: Faible Priorité / Haute Gravité

Plateforme: 3.5.0.beta8-dev avec Discourse Calendar (and Event) - 0.5 0162ed5

Description:

Après la fusion de sujets et la suppression d’un message fusionné, le sujet cible devient inaccessible en raison d’une erreur serveur ([NoMethodError: undefined method 'category_id' for nil).

Il est attendu que le sujet fonctionne normalement et ne provoque pas d’erreur serveur.

Trace complète
plugins/discourse-calendar/app/serializers/discourse_post_event/event_serializer.rb:137:in `category_id'
(eval at /home/discourse/.bundle/gems/ruby/3.3.0/gems/active_model_serializers-0.8.4/lib/active_model/serializer.rb:467):5:in `_fast_attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:468:in `attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:480:in `_serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:359:in `serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:347:in `as_json'
activesupport (7.2.2.1) lib/active_support/json/encoding.rb:23:in `encode'
activesupport (7.2.2.1) lib/active_support/json/encoding.rb:23:in `encode'
activesupport (7.2.2.1) lib/active_support/core_ext/object/json.rb:42:in `to_json'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:331:in `to_json'
multi_json (1.15.0) lib/multi_json/adapters/oj.rb:56:in `dump'
multi_json (1.15.0) lib/multi_json/adapters/oj.rb:56:in `dump'
multi_json (1.15.0) lib/multi_json/adapter.rb:25:in `dump'
multi_json (1.15.0) lib/multi_json.rb:139:in `dump'
app/controllers/topics_controller.rb:1383:in `block (2 levels) in perform_show_response'
actionpack (7.2.2.1) lib/action_controller/metal/mime_responds.rb:224:in `respond_to'
app/controllers/topics_controller.rb:1377:in `perform_show_response'
app/controllers/topics_controller.rb:191:in `show'
actionpack (7.2.2.1) lib/action_controller/metal/basic_implicit_render.rb:8:in `send_action'
actionpack (7.2.2.1) lib/abstract_controller/base.rb:226:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/rendering.rb:193:in `process_action'
actionpack (7.2.2.1) lib/abstract_controller/callbacks.rb:261:in `block in process_action'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:121:in `block in run_callbacks'
app/controllers/application_controller.rb:428:in `block in with_resolved_locale'
i18n (1.14.7) lib/i18n.rb:353:in `with_locale'
app/controllers/application_controller.rb:428:in `with_resolved_locale'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:130:in `block in run_callbacks'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:141:in `run_callbacks'
actionpack (7.2.2.1) lib/abstract_controller/callbacks.rb:260:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/rescue.rb:27:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/instrumentation.rb:77:in `block in process_action'
activesupport (7.2.2.1) lib/active_support/notifications.rb:210:in `block in instrument'
activesupport (7.2.2.1) lib/active_support/notifications/instrumenter.rb:58:in `instrument'
activesupport (7.2.2.1) lib/active_support/notifications.rb:210:in `instrument'
actionpack (7.2.2.1) lib/action_controller/metal/instrumentation.rb:76:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.2.2.1) lib/active_record/railties/controller_runtime.rb:39:in `process_action'
actionpack (7.2.2.1) lib/abstract_controller/base.rb:163:in `process'
actionview (7.2.2.1) lib/action_view/rendering.rb:40:in `process'
rack-mini-profiler (4.0.0) lib/mini_profiler/profiling_methods.rb:116:in `block in profile_method'
actionpack (7.2.2.1) lib/action_controller/metal.rb:252:in `dispatch'
actionpack (7.2.2.1) lib/action_controller/metal.rb:335:in `dispatch'
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:67:in `dispatch'
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:50:in `serve'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:53:in `block in serve'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:133:in `block in find_routes'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:126:in `each'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:126:in `find_routes'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:34:in `serve'
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:896:in `call'
lib/middleware/omniauth_bypass_middleware.rb:35:in `call'
rack (2.2.17) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.17) lib/rack/conditional_get.rb:27:in `call'
rack (2.2.17) lib/rack/head.rb:12:in `call'
actionpack (7.2.2.1) lib/action_dispatch/http/permissions_policy.rb:38:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/csp_script_nonce_injector.rb:12:in `call'
config/initializers/008-rack-cors.rb:14:in `call'
rack (2.2.17) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.17) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/cookies.rb:704:in `call'
activerecord (7.2.2.1) lib/active_record/migration.rb:674:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/callbacks.rb:31:in `block in call'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:101:in `run_callbacks'
actionpack (7.2.2.1) lib/action_dispatch/middleware/callbacks.rb:30:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/executor.rb:16:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/debug_exceptions.rb:31:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/show_exceptions.rb:32:in `call'
logster (2.20.1) lib/logster/middleware/reporter.rb:40:in `call'
lib/middleware/default_headers.rb:13:in `call'
railties (7.2.2.1) lib/rails/rack/logger.rb:41:in `call_app'
railties (7.2.2.1) lib/rails/rack/logger.rb:29:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/remote_ip.rb:96:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/request_id.rb:33:in `call'
rack (2.2.17) lib/rack/method_override.rb:24:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/executor.rb:16:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/static.rb:27:in `call'
rack (2.2.17) lib/rack/sendfile.rb:110:in `call'
lib/middleware/missing_avatars.rb:22:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/host_authorization.rb:143:in `call'
rack-mini-profiler (4.0.0) lib/mini_profiler.rb:334:in `call'
lib/middleware/processing_request.rb:12:in `call'
message_bus (4.4.1) lib/message_bus/rack/middleware.rb:60:in `call'
railties (7.2.2.1) lib/rails/engine.rb:535:in `call'
railties (7.2.2.1) lib/rails/railtie.rb:226:in `public_send'
railties (7.2.2.1) lib/rails/railtie.rb:226:in `method_missing'
rack (2.2.17) lib/rack/urlmap.rb:74:in `block in call'
rack (2.2.17) lib/rack/urlmap.rb:58:in `each'
rack (2.2.17) lib/rack/urlmap.rb:58:in `call'
unicorn (6.1.0) lib/unicorn/http_server.rb:634:in `process_client'
unicorn (6.1.0) lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn (6.1.0) lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn (6.1.0) lib/unicorn/http_server.rb:143:in `start'
unicorn (6.1.0) bin/unicorn:128:in `\u003ctop (required)\u003e'
bin/unicorn:96:in `load'
bin/unicorn:96:in `block in \u003cmain\u003e'
bin/unicorn:95:in `fork'
bin/unicorn:95:in `\u003cmain\u003e'

Étapes reproductibles:

Sur 3.5.0.beta8-dev avec Discourse Calendar (and Event) - 0.5 0162ed5 activé :

  1. Créez un nouveau sujet sujet 1 - qui sera supprimé avec un seul message par utilisateur1 qui est tl1
  2. Créez un nouveau sujet sujet 2 - qui va planter avec un seul message, assurez-vous que Calendar Event fait partie de ce message.
  3. En tant que utilisateur1, supprimez votre message dans sujet 1 - qui sera supprimé, ce qui verrouille le sujet et remplace votre message par “(sujet supprimé par l’auteur)”
  4. En tant que utilisateur2 qui a les permissions de modérateur :
  5. Sélectionnez les messages dans sujet 1 - qui sera supprimé et sélectionnez le premier message (maintenant supprimé)
  6. Déplacez vers
  7. sujet 2 - qui va planter avec l’option préserver l'ordre chronologique après la fusion cochée.
  8. En tant que utilisateur2, supprimez le premier message nouvellement fusionné dans sujet 2 - qui va planter (le message qui vient d’être fusionné et qui indique “(sujet supprimé par l’auteur)”), cela fera planter sujet 2 - qui va planter et provoquera l’erreur ci-dessus.

Une solution de contournement consiste à désactiver le plugin calendar-and-event. Cela vous permettra de visiter le sujet planté et de restaurer le premier message. Réactivez ensuite le plugin.

Discussion:

Cela a également identifié un bug légèrement moins grave dans Discourse où l’exécution des étapes ci-dessus avec le plugin calendar-and-event désactivé rend le sujet non répertorié - voir le rapport de bug séparé

2 « J'aime »

Merci beaucoup pour cette explication détaillée. D’accord, nous devrions nettoyer cela, je vais mettre un pr-welcome là-dessus pour l’instant.

1 « J'aime »

Merci à tous les deux pour le rapport détaillé et le suivi. Je viens de rencontrer un problème similaire après avoir fusionné des sujets avec des événements de calendrier et je n’arrivais pas à comprendre pourquoi le sujet se cassait. Heureux de voir que c’est documenté.

Je garderai un œil sur les PR pour cela. En attendant, désactiver temporairement le plugin comme solution de contournement est utile à savoir. Faites-moi savoir s’il y a quelque chose que je peux aider à tester.

Pour information, je pense pouvoir simplifier la reproduction :

  • Activer Calendar enabled et Discourse post event enabled
  • En tant qu’admin, créer Topic1
  • En tant qu’admin, créer Topic2 et inclure un événement de publication en utilisant ‘Create event’ depuis le menu du compositeur avec le cercle plus
  • En tant qu’admin, aller sur Topic1 et utiliser la clé à molette du sujet pour sélectionner l’OP et fusionner dans Topic2 avec ‘preserve chronological order after merging` coché

À ce stade, l’événement de publication n’est plus l’OP du sujet pour Topic2

  • En tant qu’admin, supprimer Topic2 en utilisant la suppression de publication de l’OP ou l’option de suppression de sujet

Résultat : problème majeur. Sujet supprimé inaccessible. Erreur dans /logs de NoMethodError (undefined method ‘category_id’ for nil)


La correction ici serait-elle d’empêcher la fusion qui remplacerait l’événement comme OP ?

Lorsqu’on essaie de fusionner une publication d’événement (une correctement placée dans un OP) dans un autre sujet, elle est bloquée avec ceci dans les logs :

Failed to process hijacked response correctly : ActiveRecord::RecordNotSaved : An event can only be in the first post of a topic

(L’erreur de l’interface utilisateur est plus générique, ‘There was an error moving posts.`, ce qui serait bien si elle était plus détaillée)

Quelque chose de similaire pourrait-il être appliqué pour empêcher une fusion qui déplacerait l’événement hors de la position d’OP ?

3 « J'aime »