토픽 병합 후 `NoMethodError in TopicsController#show` 오류 발생

우선순위/심각도: 낮은 우선순위 / 높은 심각도

플랫폼: 3.5.0.beta8-devDiscourse 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 `<top (required)>'
bin/unicorn:96:in `load'
bin/unicorn:96:in `block in <main>'
bin/unicorn:95:in `fork'
bin/unicorn:95:in `<main>'

재현 단계:

Discourse Calendar (and Event) - 0.5 0162ed5가 활성화된 3.5.0.beta8-dev 환경에서:

  1. tl1user1로 단일 게시물로 이루어진 새 토픽 topic 1 - that will be deleted를 생성합니다.
  2. 단일 게시물로 이루어진 새 토픽 topic 2 - that will break를 생성하고, 이 게시물에 Calendar Event가 포함되도록 합니다.
  3. user1topic 1 - that will be deleted의 게시물을 삭제하여 토픽을 잠그고, 게시물을 "(topic deleted by author)"으로 대체합니다.
  4. moderator 권한을 가진 user2로:
  5. topic 1 - that will be deleted의 게시물을 선택하고, 첫 번째(이제 삭제된) 게시물을 선택하여 > 이동 > topic 2 - that will break로 이동하되 preserve chronological order after merging을 선택합니다.
  6. user2topic 2 - that will break의 새로 병합된 첫 번째 게시물(방금 병합되어 "(topic deleted by author)"라고 표시된 게시물)을 삭제합니다. 이렇게 하면 topic 2 - that will break가 손상되고 위의 오류가 발생합니다.

우회 방법 - #calendar-and-event 플러그인을 비활성화합니다. 그러면 손상된 토픽에 방문하여 첫 번째 게시물을 복구(undelete)할 수 있습니다. 그런 다음 플러그인을 다시 활성화합니다.

토론:

이것은 또한 #calendar-and-event 플러그인이 비활성화된 상태에서 위의 작업을 수행하면 토픽이 목록에서 제외(unlisted)되는, 약간 심각도가 낮은 Discourse 버그를 식별했습니다 - 별도 버그 보고서 참조:

2개의 좋아요

Thanks heaps for the detailed write up. Agree we should clean this up, going to put a pr-welcome on this for now.

1개의 좋아요

Thanks both for the detailed report and the follow-up. I just ran into a similar issue after merging topics with calendar events and couldn’t figure out why the topic was breaking. Glad to see this is documented.

I’ll keep an eye on any PRs for this. In the meantime, disabling the plugin temporarily as a workaround is helpful to know. Let me know if there’s anything I can help test.

FWIW I think I can simplify the repro:

  • Enable both Calendar enabled and Discourse post event enabled
  • As admin, create Topic1
  • As admin, create Topic2 and include a post event using ‘Create event’ from the circle-with-a-plus composer menu
  • As admin, go to Topic1 and use the topic wrench to select the OP and merge into Topic2 with ‘preserve chronological order after merging` checked

At this stage the post event is now no longer the OP of the topic for Topic2

  • As admin, delete Topic2 using either the post delete of the OP or the topic delete option

Result: borkity bork. Deleted topic inaccessible. Error in /logs of NoMethodError (undefined method ‘category_id’ for nil)


Would the fix here be to prevent the merge that would replace the event as the OP?

When trying to merge an event post (one correctly placed in an OP) into another topic it’s blocked with this in the logs:

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

(The UI error is more generic, ‘There was an error moving posts.`, which would be nice if it was more verbose)

Could something similar be applied to prevent a merge moving the event out of the OP position?

3개의 좋아요

이 문제는 수정되었으며, 다른 유사한 시나리오에 대한 추가적인 가드도 추가되었습니다

1개의 좋아요