トピックのマージ後にTopicsController#showでNoMethodErrorが発生

優先度/重大度: 低優先度 / 高重大度

プラットフォーム: 3.5.0.beta8-dev (Discourse Calendar (and Event) - 0.5 0162ed5 を使用)

説明:

トピックをマージし、マージされた投稿を削除した後、ターゲットトピックはサーバーエラー([NoMethodError: undefined method 'category_id' for nil])によりアクセス不能になります。

トピックは正常に機能し、サーバーエラーを引き起こさないことが期待されます。

完全なトレース
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 `rescue in attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:455: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'

再現手順:

Discourse Calendar (and Event) - 0.5 0162ed5 を有効にした 3.5.0.beta8-dev で:

  1. tl1 である user1 として、単一の投稿を持つ新しいトピック topic 1 - that will be deleted を作成します。
  2. 単一の投稿を持つ新しいトピック topic 2 - that will break を作成し、その投稿に Calendar Event が含まれていることを確認します。
  3. user1 として、topic 1 - that will be deleted の投稿を削除し、トピックをロックして投稿を「(topic deleted by author)」に置き換えます。
  4. moderator 権限を持つ user2 として:
  5. topic 1 - that will be deleted の投稿を選択し、最初の(現在削除されている)投稿を選択して、topic 2 - that will break に「マージ後に時系列順を保持する」をチェックした状態で移動します。
  6. user2 として、topic 2 - that will break の新しくマージされた最初の投稿(「(topic deleted by author)」と表示されている、ちょうどマージされた投稿)を削除します。これにより、topic 2 - that will break が破損し、上記のエラーが発生します。

回避策 - #calendar-and-event プラグインを無効にします。これにより、破損したトピックにアクセスして最初の投稿を復元できるようになります。その後、プラグインを再度有効にします。

議論:

これは、#calendar-and-event プラグインが無効な状態で上記を実行すると、トピックがリストに表示されなくなるという、わずかに重大度の低いバグも特定しました。詳細は別のバグレポートを参照してください。

「いいね!」 2

詳しい説明、ありがとうございます。確かにこれを整理した方が良いですね。とりあえず #pr-welcome を付けておきます。

「いいね!」 1

詳細なレポートとフォローアップ、両方ともありがとうございます。私もカレンダーイベントとトピックをマージした後、同様の問題に遭遇し、トピックが壊れる理由がわかりませんでした。これが文書化されていることを知って嬉しいです。

これに関するPRには注目しておきます。それまでの間、回避策としてプラグインを一時的に無効にすることが役立つことを知っておきます。テストでお手伝いできることがあればお知らせください。

参考までに、再現手順を簡略化できると思います。

  • カレンダー有効Discourse投稿イベント有効 の両方を有効にする
  • 管理者として、Topic1を作成する
  • 管理者として、プラス記号付きコンポーザーメニューの「イベントを作成」を使用して投稿イベントを含むTopic2を作成する
  • 管理者として、Topic1に移動し、トピックレンチを使用してOPを選択し、「マージ後の時系列順を保持する」にチェックを入れてTopic2にマージする

この時点で、投稿イベントはTopic2のトピックのOPではなくなります。

  • 管理者として、OPの投稿削除またはトピック削除オプションのいずれかを使用してTopic2を削除する

結果:壊れた。削除されたトピックにアクセスできません。/logsNoMethodError (undefined method ‘category_id’ for nil) というエラーが発生します。


ここでの修正は、イベントをOPとして置き換えるマージを防ぐことでしょうか?

イベント投稿(OPに正しく配置されたもの)を別のトピックにマージしようとすると、ログに次のように表示されてブロックされます。

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

(UIエラーはより一般的で、「投稿の移動中にエラーが発生しました。」となっており、より詳細だと良いのですが)

イベントをOPの位置から移動させるマージを防ぐために、同様のものを適用することはできますか?

「いいね!」 3