Skip to content
Extraits de code Groupes Projets
Valider d55be67d rédigé par Benjamin Neff's avatar Benjamin Neff
Parcourir les fichiers

handle existing guids on receive

parent f3466bcf
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
...@@ -20,15 +20,13 @@ module Workers ...@@ -20,15 +20,13 @@ module Workers
DiasporaFederation::Salmon::InvalidAlgorithm, DiasporaFederation::Salmon::InvalidAlgorithm,
DiasporaFederation::Salmon::InvalidEncoding, DiasporaFederation::Salmon::InvalidEncoding,
Diaspora::Federation::AuthorIgnored, Diaspora::Federation::AuthorIgnored,
Diaspora::Federation::InvalidAuthor,
# TODO: deprecated # TODO: deprecated
DiasporaFederation::Salmon::MissingMagicEnvelope, DiasporaFederation::Salmon::MissingMagicEnvelope,
DiasporaFederation::Salmon::MissingAuthor, DiasporaFederation::Salmon::MissingAuthor,
DiasporaFederation::Salmon::MissingHeader, DiasporaFederation::Salmon::MissingHeader,
DiasporaFederation::Salmon::InvalidHeader => e DiasporaFederation::Salmon::InvalidHeader => e
logger.warn "don't retry for error: #{e.class}" logger.warn "don't retry for error: #{e.class}"
rescue ActiveRecord::RecordInvalid => e
logger.warn "failed to save received object: #{e.record.errors.full_messages}"
raise e unless e.message.include? "already been taken"
end end
end end
end end
...@@ -8,6 +8,10 @@ module Diaspora ...@@ -8,6 +8,10 @@ module Diaspora
# Raised, if author is ignored by the relayable parent author # Raised, if author is ignored by the relayable parent author
class AuthorIgnored < RuntimeError class AuthorIgnored < RuntimeError
end end
# Raised, if the author of the existing object doesn't match the received author
class InvalidAuthor < RuntimeError
end
end end
end end
......
module Diaspora module Diaspora
module Federation module Federation
module Receive module Receive
extend Diaspora::Logging
def self.account_deletion(entity) def self.account_deletion(entity)
AccountDeletion.new( AccountDeletion.new(
person: author_of(entity), person: author_of(entity),
...@@ -9,13 +11,16 @@ module Diaspora ...@@ -9,13 +11,16 @@ module Diaspora
end end
def self.comment(entity) def self.comment(entity)
Comment.new( author = author_of(entity)
author: author_of(entity), ignore_existing_guid(Comment, entity.guid, author) do
guid: entity.guid, Comment.new(
created_at: entity.created_at, author: author,
text: entity.text, guid: entity.guid,
commentable: Post.find_by(guid: entity.parent_guid) created_at: entity.created_at,
).tap {|comment| save_relayable(comment, entity) } text: entity.text,
commentable: Post.find_by(guid: entity.parent_guid)
).tap {|comment| save_relayable(comment, entity) }
end
end end
def self.contact(entity) def self.contact(entity)
...@@ -31,29 +36,37 @@ module Diaspora ...@@ -31,29 +36,37 @@ module Diaspora
end end
def self.conversation(entity) def self.conversation(entity)
Conversation.new( author = author_of(entity)
author: author_of(entity), try_load_existing_guid(Conversation, entity.guid, author) do
guid: entity.guid, Conversation.new(
subject: entity.subject, author: author,
created_at: entity.created_at, guid: entity.guid,
participant_handles: entity.participants subject: entity.subject,
).tap do |conversation| created_at: entity.created_at,
conversation.messages = entity.messages.map {|message| build_message(message) } participant_handles: entity.participants
conversation.save! ).tap do |conversation|
conversation.messages = entity.messages.map {|message| build_message(message) }
conversation.save!
end
end end
end end
def self.like(entity) def self.like(entity)
Like.new( author = author_of(entity)
author: author_of(entity), ignore_existing_guid(Like, entity.guid, author) do
guid: entity.guid, Like.new(
positive: entity.positive, author: author,
target: entity.parent_type.constantize.find_by(guid: entity.parent_guid) guid: entity.guid,
).tap {|like| save_relayable(like, entity) } positive: entity.positive,
target: entity.parent_type.constantize.find_by(guid: entity.parent_guid)
).tap {|like| save_relayable(like, entity) }
end
end end
def self.message(entity) def self.message(entity)
build_message(entity).tap(&:save!) ignore_existing_guid(Message, entity.guid, author_of(entity)) do
build_message(entity).tap(&:save!)
end
end end
def self.participation(entity) def self.participation(entity)
...@@ -73,14 +86,17 @@ module Diaspora ...@@ -73,14 +86,17 @@ module Diaspora
end end
def self.poll_participation(entity) def self.poll_participation(entity)
PollParticipation.new( author = author_of(entity)
author: author_of(entity), ignore_existing_guid(PollParticipation, entity.guid, author) do
guid: entity.guid, PollParticipation.new(
poll: Poll.find_by(guid: entity.parent_guid) author: author,
).tap do |poll_participation| guid: entity.guid,
poll_participation.poll_answer_guid = entity.poll_answer_guid poll: Poll.find_by(guid: entity.parent_guid)
).tap do |poll_participation|
poll_participation.poll_answer_guid = entity.poll_answer_guid
save_relayable(poll_participation, entity) save_relayable(poll_participation, entity)
end
end end
end end
...@@ -115,27 +131,29 @@ module Diaspora ...@@ -115,27 +131,29 @@ module Diaspora
end end
def self.status_message(entity) def self.status_message(entity)
StatusMessage.new( author = author_of(entity)
author: author_of(entity), try_load_existing_guid(StatusMessage, entity.guid, author) do
guid: entity.guid, StatusMessage.new(
raw_message: entity.raw_message, author: author,
public: entity.public, guid: entity.guid,
created_at: entity.created_at, raw_message: entity.raw_message,
provider_display_name: entity.provider_display_name public: entity.public,
).tap do |status_message| created_at: entity.created_at,
status_message.photos = entity.photos.map {|photo| build_photo(photo) } provider_display_name: entity.provider_display_name
status_message.location = build_location(entity.location) if entity.location ).tap do |status_message|
status_message.poll = build_poll(entity.poll) if entity.poll status_message.photos = entity.photos.map {|photo| build_photo(photo) }
status_message.location = build_location(entity.location) if entity.location
status_message.poll = build_poll(entity.poll) if entity.poll
status_message.save! status_message.save!
end
end end
end end
private
def self.author_of(entity) def self.author_of(entity)
Person.find_by(diaspora_handle: entity.author) Person.find_by(diaspora_handle: entity.author)
end end
private_class_method :author_of
def self.build_location(entity) def self.build_location(entity)
Location.new( Location.new(
...@@ -144,6 +162,7 @@ module Diaspora ...@@ -144,6 +162,7 @@ module Diaspora
lng: entity.lng lng: entity.lng
) )
end end
private_class_method :build_location
def self.build_message(entity) def self.build_message(entity)
Message.new( Message.new(
...@@ -154,6 +173,7 @@ module Diaspora ...@@ -154,6 +173,7 @@ module Diaspora
conversation_guid: entity.conversation_guid conversation_guid: entity.conversation_guid
) )
end end
private_class_method :build_message
def self.build_photo(entity) def self.build_photo(entity)
Photo.new( Photo.new(
...@@ -169,6 +189,7 @@ module Diaspora ...@@ -169,6 +189,7 @@ module Diaspora
width: entity.width width: entity.width
) )
end end
private_class_method :build_photo
def self.build_poll(entity) def self.build_poll(entity)
Poll.new( Poll.new(
...@@ -183,6 +204,7 @@ module Diaspora ...@@ -183,6 +204,7 @@ module Diaspora
end end
end end
end end
private_class_method :build_poll
def self.save_relayable(relayable, entity) def self.save_relayable(relayable, entity)
retract_if_author_ignored(relayable) retract_if_author_ignored(relayable)
...@@ -190,6 +212,7 @@ module Diaspora ...@@ -190,6 +212,7 @@ module Diaspora
relayable.author_signature = entity.author_signature if relayable.parent.author.local? relayable.author_signature = entity.author_signature if relayable.parent.author.local?
relayable.save! relayable.save!
end end
private_class_method :save_relayable
def self.retract_if_author_ignored(relayable) def self.retract_if_author_ignored(relayable)
parent_author = relayable.parent.author parent_author = relayable.parent.author
...@@ -199,6 +222,45 @@ module Diaspora ...@@ -199,6 +222,45 @@ module Diaspora
raise Diaspora::Federation::AuthorIgnored raise Diaspora::Federation::AuthorIgnored
end end
private_class_method :retract_if_author_ignored
# check if the object already exists, otherwise save it.
# if save fails (probably because of a second object received parallel),
# check again if an object with the same guid already exists, but maybe has a different author.
# @raise [InvalidAuthor] if the author of the existing object doesn't match
def self.ignore_existing_guid(klass, guid, author)
yield unless klass.where(guid: guid, author_id: author.id).exists?
rescue => e
raise e unless load_from_database(klass, guid, author)
logger.warn "ignoring error on receive #{klass}:#{guid}: #{e.class}: #{e.message}"
end
private_class_method :ignore_existing_guid
# try to load the object first from the DB and if not available, save it.
# if save fails (probably because of a second object received parallel),
# try again to load it, because it is possibly there now.
# @raise [InvalidAuthor] if the author of the existing object doesn't match
def self.try_load_existing_guid(klass, guid, author)
load_from_database(klass, guid, author) || yield
rescue Diaspora::Federation::InvalidAuthor => e
raise e # don't try loading from db twice
rescue => e
logger.warn "failed to save #{klass}:#{guid} (#{e.class}: #{e.message}) - try loading it from DB"
load_from_database(klass, guid, author).tap do |object|
raise e unless object
end
end
private_class_method :try_load_existing_guid
# @raise [InvalidAuthor] if the author of the loaded object doesn't match
def self.load_from_database(klass, guid, author)
klass.find_by(guid: guid).tap do |object|
if object && object.author_id != author.id
raise Diaspora::Federation::InvalidAuthor, "#{klass}:#{guid}: #{author.diaspora_handle}"
end
end
end
private_class_method :load_from_database
end end
end end
end end
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter