Skip to content

Commit

Permalink
Try fetching missing parent of relayables
Browse files Browse the repository at this point in the history
* Extract post fetching logic from Reshare into
  its own module
* raise proper error message when fetching fails
* raise proper error message when parent is still missing

We can't skip fetch failures or missing parents and
still need to retry them in case we're sent the parent
later on
  • Loading branch information
jhass committed Sep 7, 2014
1 parent 9c88fde commit 777e312
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 32 deletions.
31 changes: 6 additions & 25 deletions app/models/reshare.rb
Expand Up @@ -74,35 +74,16 @@ def absolute_root
private

def after_parse
root_author = Webfinger.new(@root_diaspora_id).fetch
root_author.save! unless root_author.persisted?

return if Post.exists?(:guid => self.root_guid)

fetched_post = self.class.fetch_post(root_author, self.root_guid)

if fetched_post
#Why are we checking for this?
if root_author.diaspora_handle != fetched_post.diaspora_handle
raise "Diaspora ID (#{fetched_post.diaspora_handle}) in the root does not match the Diaspora ID (#{root_author.diaspora_handle}) specified in the reshare!"
if root.blank?
self.root = Diaspora::Fetcher::Single.find_or_fetch_from_remote root_guid, @root_diaspora_id do |fetched_post, author|
# why do we check this?
if fetched_post.diaspora_handle != author.diaspora_handle
raise Diaspora::PostNotFetchable, "Diaspora ID (#{fetched_post.diaspora_handle}) in the root does not match the Diaspora ID (#{author.diaspora_handle}) specified in the reshare!"
end
end

fetched_post.save!
end
end

# Fetch a remote public post, used for receiving reshares of unknown posts
# @param [Person] author the remote post's author
# @param [String] guid the remote post's guid
# @return [Post] an unsaved remote post or false if the post was not found
def self.fetch_post author, guid
url = author.url + "/p/#{guid}.xml"
response = Faraday.get(url)
return false if response.status == 404 # Old pod, friendika
raise "Failed to get #{url}" unless response.success? # Other error, N/A for example
Diaspora::Parser.from_xml(response.body)
end

def root_must_be_public
if self.root && !self.root.public
errors[:base] << "Only posts which are public may be reshared."
Expand Down
7 changes: 6 additions & 1 deletion lib/diaspora/exceptions.rb
Expand Up @@ -16,7 +16,6 @@ class AccountClosed < StandardError
# that prevents further execution
class NotMine < StandardError
end


# Received a message without having a contact
class ContactRequiredUnlessRequest < StandardError
Expand All @@ -30,4 +29,10 @@ class RelayableObjectWithoutParent < StandardError
# original XML message
class AuthorXMLAuthorMismatch < StandardError
end

# Tried to fetch a post but it was deleted, not valid
# or the remote end doesn't support post fetching
class PostNotFetchable < StandardError
end

end
1 change: 1 addition & 0 deletions lib/diaspora/fetcher.rb
@@ -1,5 +1,6 @@
module Diaspora
module Fetcher
require 'diaspora/fetcher/public'
require 'diaspora/fetcher/single'
end
end
41 changes: 41 additions & 0 deletions lib/diaspora/fetcher/single.rb
@@ -0,0 +1,41 @@
module Diaspora
module Fetcher
module Single
module_function

This comment has been minimized.

Copy link
@Raven24

Raven24 Sep 7, 2014

don't you have to specify a symbol to module_function?

This comment has been minimized.

Copy link
@Raven24

Raven24 Sep 7, 2014

nvm, just wasn't sure what it does ;)


# Fetch and store a remote public post
# @param [String] guid the remote posts guid
# @param [String] author_id Diaspora ID of a user known to have the post,
# preferably the author
# @yield [Post, Person] If a block is given it is yielded the post
# and the author prior save
# @return a saved post
def find_or_fetch_from_remote guid, author_id
post = Post.where(guid: guid).first
return post if post

post_author = Webfinger.new(author_id).fetch
post_author.save! unless post_author.persisted?

if fetched_post = fetch_post(post_author, guid)
yield fetched_post, post_author if block_given?
raise Diaspora::PostNotFetchable unless fetched_post.save
end

fetched_post
end

# Fetch a remote public post, used for receiving of unknown public posts
# @param [Person] author the remote post's author
# @param [String] guid the remote post's guid
# @return [Post] an unsaved remote post or false if the post was not found
def fetch_post author, guid
url = author.url + "/p/#{guid}.xml"
response = Faraday.get(url)
raise Diaspora::PostNotFetchable if response.status == 404 # Old pod, Friendika, deleted
raise "Failed to get #{url}" unless response.success? # Other error, N/A for example
Diaspora::Parser.from_xml(response.body)
end
end
end
end
17 changes: 15 additions & 2 deletions lib/diaspora/relayable.rb
Expand Up @@ -51,7 +51,8 @@ def parent_guid
end

def parent_guid= new_parent_guid
self.parent = parent_class.where(:guid => new_parent_guid).first
@parent_guid = new_parent_guid
self.parent = parent_class.where(guid: new_parent_guid).first
end

# @return [Array<Person>]
Expand All @@ -66,7 +67,7 @@ def subscribers(user)
end

def receive(user, person=nil)
comment_or_like = self.class.where(:guid => self.guid).first || self
comment_or_like = self.class.where(guid: self.guid).first || self

# Check to make sure the signature of the comment or like comes from the person claiming to author it
unless comment_or_like.parent_author == user.person || comment_or_like.verify_parent_author_signature
Expand Down Expand Up @@ -134,5 +135,17 @@ def parent
def parent= parent
raise NotImplementedError.new('you must override parent= in order to enable relayable on this model')
end

# ROXML hook ensuring our own hooks are called
def after_parse
if @parent_guid
self.parent ||= fetch_parent(@parent_guid)
end
end

# Childs should override this to support fetching a missing parent
# @param guid the parents guid
def fetch_parent guid
end
end
end
6 changes: 5 additions & 1 deletion lib/federated/relayable.rb
Expand Up @@ -38,5 +38,9 @@ def parent
def parent= parent
self.target = parent
end

def fetch_parent guid
Diaspora::Fetcher::Single.find_or_fetch_from_remote guid, diaspora_handle
end
end
end
end
9 changes: 9 additions & 0 deletions lib/postzord/receiver/public.rb
Expand Up @@ -57,6 +57,7 @@ def receive_relayable
def save_object
@object = Diaspora::Parser::from_xml(@salmon.parsed_data)
raise "Object is not public" if object_can_be_public_and_it_is_not?
raise Diaspora::RelayableObjectWithoutParent if object_must_have_parent_and_does_not?
raise Diaspora::AuthorXMLAuthorMismatch if author_does_not_match_xml_author?
@object.save! if @object && @object.respond_to?(:save!)
@object
Expand Down Expand Up @@ -88,4 +89,12 @@ def account_deletion_is_from_author
def object_can_be_public_and_it_is_not?
@object.respond_to?(:public) && !@object.public?
end

def object_must_have_parent_and_does_not?
if @object.respond_to?(:relayable?) # comment, like
@object.parent.nil?
else
false
end
end
end
7 changes: 7 additions & 0 deletions spec/models/comment_spec.rb
Expand Up @@ -96,6 +96,13 @@
it 'marshals the post' do
expect(@marshalled_comment.post).to eq(@post)
end

it 'tries to fetch a missing parent' do
guid = @post.guid
@post.destroy
expect_any_instance_of(Comment).to receive(:fetch_parent).with(guid).and_return(nil)
Comment.from_xml(@xml)
end
end
end

Expand Down
6 changes: 3 additions & 3 deletions spec/models/reshare_spec.rb
Expand Up @@ -83,7 +83,7 @@
rs1 = FactoryGirl.build(:reshare, :root=>@sm)
rs2 = FactoryGirl.build(:reshare, :root=>rs1)
@rs3 = FactoryGirl.build(:reshare, :root=>rs2)

sm = FactoryGirl.create(:status_message, :author => alice.person, :public => true)
rs1 = FactoryGirl.create(:reshare, :root => sm)
@of_deleted = FactoryGirl.build(:reshare, :root => rs1)
Expand Down Expand Up @@ -194,13 +194,13 @@
end

context "fetching post" do
it "doesn't error out if the post is not found" do
it "raises if the post is not found" do
allow(@response).to receive(:status).and_return(404)
expect(Faraday.default_connection).to receive(:get).and_return(@response)

expect {
Reshare.from_xml(@xml)
}.to_not raise_error
}.to raise_error(Diaspora::PostNotFetchable)
end

it "raises if there's another error receiving the post" do
Expand Down

0 comments on commit 777e312

Please sign in to comment.