Wenn eine API-Anfrage an die Route /posts.json mit der Eigenschaft external_id auf die external_id eines vorhandenen Themas gesetzt wird, wird ein Fehler ausgelöst, der diesem ähnelt:
ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR: duplicate key value violates unique constraint \"index_topics_on_external_id\" DETAIL: Key (external_id)=(obKopieren von Text in die Zwischenablage) already exists.)
Wenn die Anfrage mit dem Discourse API-Gem gestellt wird, ist die Antwort nur ein HTML-String:
#<DiscourseApi::Error:\"\u003c!DOCTYPE html\u003e\\n\u003chtml lang=\\\"en\\\"\u003e\\n\u003chead\u003e\\n \u003cmeta charset=\\\"utf-8\\\" /\u003e\\n ...
Wenn die Anfrage auf andere Weise gestellt wird, z. B. mit HTTParty, sieht das Antwortobjekt wie folgt aus:
response = HTTParty.post(url, body:, headers:)
add_note_to_db(title, response)
# Versuchen, das `response`-Objekt aus der Antwort zu erhalten
p response.response
# gibt aus:
#<Net::HTTPInternalServerError 500 Internal Server Error readbody=true>
Damit kann nicht viel gemacht werden.
Hier sind die Aufrufe, die das Problem auslösen:
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'
Ich denke, dass ein Fehler in TopicCreator#create ausgelöst und aus dem Transaktionsblock in PostCreator#create abgefangen werden muss, um dann im Posts-Controller behandelt zu werden.
Die ideale Lösung aus meiner Sicht wäre, einen Fehler zurückzugeben, der angibt, welches Thema die externe ID verwendet.
Fehler in TopicCreator#create auslösen:
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
#...
# Wahrscheinlich gibt es eine vorhandene Fehlerklasse, die dies behandeln könnte, aber das hier am Ende der Topic_creator-Klasse einzufügen, funktioniert für mich:
class DuplicateExternalIdError < StandardError
attr_reader :topic
def initialize(topic)
@topic = topic
end
end
Aus ihm in PostCreator#create abfangen:
def create
if valid?
begin
transaction do
build_post_stats
create_topic # hier passiert der Fehler
# ...
end
rescue TopicCreator::DuplicateExternalIdError => e
@errors.add(:base, "External ID must be unique. Existing topic ID: #{e.topic.id}")
end
end
Im Posts-Controller behandeln:
# 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
Das ist vielleicht zu spezifisch für meinen Anwendungsfall, um die richtige Lösung zu sein. Es hängt mit dem Problem von externen IDs und doppelten Themen zusammen, das hier erwähnt wird: API topic's external_ID can't be reused after deleting a topic and creating a new one - #2 by simon. Idealerweise könnte eine Anwendung, die an die Discourse-API postet, die Antwortnachricht verwenden, um das Thema, das bereits die externe ID verwendet hat, wiederherzustellen und zu aktualisieren.