خطأ غير معالج PG::UniqueViolation لحقول المعرف الخارجي للموضوع

عند إجراء طلب API إلى المسار /posts.json مع تعيين الخاصية external_id إلى external_id لموضوع موجود، يحدث خطأ مشابه لهذا:

ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_topics_on_external_id" DETAIL: Key (external_id)=(obCopying text to the system clipboard) already exists.)

عند إجراء الطلب باستخدام Gem الخاص بـ Discourse API، يكون الرد مجرد سلسلة HTML:

`#<DiscourseApi::Error:"

...`

إذا تم إجراء الطلب بطريقة أخرى، على سبيل المثال باستخدام HTTParty، فإن كائن الاستجابة يبدو كالتالي:

response = HTTParty.post(url, body:, headers:)
add_note_to_db(title, response)
# Try getting the `response` object from the response
p response.response

# outputs:
#<Net::HTTPInternalServerError 500 Internal Server Error readbody=true>

لا يوجد الكثير مما يمكن فعله مع ذلك.

فيما يلي الاستدعاءات التي تسبب المشكلة:

lib/topic_creator.rb:237:in `save_topic'
lib/topic_creator.rb:58:in `create'
lib/post_creator.rb:490:in `create_topic'
lib/post_creator.rb:190:in `block in create'
lib/post_creator.rb:390:in `block in transaction'
lib/post_creator.rb:390:in `transaction'
lib/post_creator.rb:188:in `create'
lib/new_post_manager.rb:318:in `perform_create_post'
lib/new_post_manager.rb:252:in `perform'
app/controllers/posts_controller.rb:210:in `block in create'
lib/distributed_memoizer.rb:16:in `block in memoize'
lib/distributed_mutex.rb:53:in `block in synchronize'
lib/distributed_mutex.rb:49:in `synchronize'
lib/distributed_mutex.rb:49:in `synchronize'
lib/distributed_mutex.rb:34:in `synchronize'
lib/distributed_memoizer.rb:12:in `memoize'
app/controllers/posts_controller.rb:209:in `create'

أعتقد أنه يجب رفع خطأ في TopicCreator#create، وإنقاذه من كتلة المعاملة في PostCreator#create، ثم معالجته في وحدة تحكم المنشورات.
الحل المثالي من وجهة نظري هو إرجاع خطأ يشير إلى الموضوع الذي كان يستخدم external_id.

رفع خطأ في TopicCreator#create:

  def create
    topic = Topic.new(setup_topic_params)

    if topic.external_id.present? && Topic.with_deleted.exists?(external_id: topic.external_id)
      existing_topic = Topic.with_deleted.find_by(external_id: topic.external_id)
      raise DuplicateExternalIdError.new(existing_topic), "External ID must be unique. Existing topic ID: #{existing_topic.id}"
    end
#...

# There's probably an existing error class that could handle this, but sticking this at the bottom of the topic_creator class works for me:

   class DuplicateExternalIdError < StandardError
    attr_reader :topic

    def initialize(topic)
      @topic = topic
    end
  end

الإنقاذ منه في PostCreator#create:

  def create
    if valid?
      begin
        transaction do
          build_post_stats
          create_topic # this is where the error is happening
          # ...
        end
      rescue TopicCreator::DuplicateExternalIdError => e
        @errors.add(:base, "External ID must be unique. Existing topic ID: #{e.topic.id}")
      end
    end

معالجته في وحدة تحكم المنشورات:

# posts_controller.rb

  def handle_duplicate_external_id_error(exception)
    render json: { error: "External ID must be unique. Existing topic ID: #{exception.topic.id}" }, status: :unprocessable_entity
  end

هذا ربما يكون محددًا جدًا لحالتي ليكون الحل الصحيح. إنه متعلق بالمشكلة المتعلقة بمعرفات external_id والموضوعات المكررة المذكورة هنا: API topic's external_ID can't be reused after deleting a topic and creating a new one - #2 by simon. من الناحية المثالية، يمكن لتطبيق يقوم بالنشر إلى واجهة برمجة تطبيقات Discourse استخدام رسالة الاستجابة لإلغاء حذف وتحديث الموضوع الذي كان يستخدم external_id بالفعل.

إعجاب واحد (1)

نعم، سعيد بطلب سحب لتحسين رسالة الخطأ هذه!

عظيم! سأرى ما يمكنني فعله.

ربما لا يحتاج الأمر إلى أن يكون محددًا مثل ما كنت أقترحه بالأمس:

rescue TopicCreator::DuplicateExternalIdError => e
   @errors.add(:base, "External ID must be unique. Existing topic ID: #{e.topic.id}")

مجرد الحصول على استجابة بتفاصيل حول القيد الذي تم انتهاكه يجب أن يكون كافياً.