From 3fe30cde125201d7b5c863b5e5a5e998e6bd7afa Mon Sep 17 00:00:00 2001 From: maxwell Date: Tue, 4 Jan 2011 14:16:26 -0800 Subject: [PATCH] removed message handler, postzord::dispatch is almost done, now i need to rip a bunch of methods out --- app/models/jobs/http_post.rb | 4 +- app/views/publics/hcard.html.haml | 50 +++++++++ app/views/publics/host_meta.html.erb | 9 ++ app/views/publics/webfinger.html.erb | 12 +++ config/application.rb | 1 - lib/postzord.rb | 7 ++ lib/postzord/dispatch.rb | 67 ++++++++++++ spec/lib/postzord/dispatch_spec.rb | 156 +++++++++++++++++++++++++++ spec/models/jobs/http_post_spec.rb | 5 +- 9 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 app/views/publics/hcard.html.haml create mode 100644 app/views/publics/host_meta.html.erb create mode 100644 app/views/publics/webfinger.html.erb create mode 100644 lib/postzord.rb create mode 100644 lib/postzord/dispatch.rb create mode 100644 spec/lib/postzord/dispatch_spec.rb diff --git a/app/models/jobs/http_post.rb b/app/models/jobs/http_post.rb index 5a63a4198..16764a579 100644 --- a/app/models/jobs/http_post.rb +++ b/app/models/jobs/http_post.rb @@ -2,9 +2,11 @@ module Jobs class HttpPost extend ResqueJobLogging @queue = :http + NUM_TRIES = 3 - def self.perform(url, body, tries_remaining) + def self.perform(url, body, tries_remaining = NUM_TRIES) begin + body = CGI::escape(body) RestClient.post(url, :xml => body){ |response, request, result, &block| if [301, 302, 307].include? response.code response.follow_redirection(request, result, &block) diff --git a/app/views/publics/hcard.html.haml b/app/views/publics/hcard.html.haml new file mode 100644 index 000000000..f6e4c4408 --- /dev/null +++ b/app/views/publics/hcard.html.haml @@ -0,0 +1,50 @@ +#content + %h1= @person.name + #content_inner + #i.entity_profile.vcard.author + %h2 User profile + + %dl.entity_nickname + %dt Nickname + %dd + %a.nickname.url.uid{:href=>@person.url, :rel=>'me'}= @person.name + + %dl.entity_given_name + %dt First name + %dd + %span.given_name= @person.profile.first_name + + %dl.entity_family_name + %dt Family name + %dd + %span.family_name= @person.profile.last_name + + %dl.entity_fn + %dt Full name + %dd + %span.fn= @person.name + + %dl.entity_url + %dt URL + %dd + %a#pod_location.url{:href=>@person.url, :rel=>'me'}= @person.url + + %dl.entity_photo + %dt Photo + %dd + %img.photo.avatar{:src=>image_or_default(@person), :width=>'300px', :height=>'300px'} + + %dl.entity_photo_medium + %dt Photo + %dd + %img.photo.avatar{:src=>image_or_default(@person, :thumb_medium), :width=>'100px', :height=>'100px'} + + %dl.entity_photo_small + %dt Photo + %dd + %img.photo.avatar{:src=>image_or_default(@person, :thumb_small), :width=>'50px', :height=>'50px'} + + %dl.entity_searchable + %dt Searchable + %dd + %span.searchable= @person.profile.searchable diff --git a/app/views/publics/host_meta.html.erb b/app/views/publics/host_meta.html.erb new file mode 100644 index 000000000..8b0ad3aa8 --- /dev/null +++ b/app/views/publics/host_meta.html.erb @@ -0,0 +1,9 @@ + + + <%= AppConfig[:pod_uri].host %> + + Resource Descriptor + + diff --git a/app/views/publics/webfinger.html.erb b/app/views/publics/webfinger.html.erb new file mode 100644 index 000000000..8c6d08657 --- /dev/null +++ b/app/views/publics/webfinger.html.erb @@ -0,0 +1,12 @@ + + + acct:<%=@person.diaspora_handle%> + "<%= @person.url %>" + + + + + + + + diff --git a/config/application.rb b/config/application.rb index 87498fa4d..4b1b83786 100644 --- a/config/application.rb +++ b/config/application.rb @@ -13,7 +13,6 @@ Bundler.require(:default, Rails.env) if defined?(Bundler) require File.expand_path('../../lib/mongo_mapper/bson_id', __FILE__) require File.expand_path('../../lib/log_overrider', __FILE__) -require File.expand_path('../../lib/message_handler', __FILE__) module Diaspora class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. diff --git a/lib/postzord.rb b/lib/postzord.rb new file mode 100644 index 000000000..0c652d738 --- /dev/null +++ b/lib/postzord.rb @@ -0,0 +1,7 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +class Postzord + +end diff --git a/lib/postzord/dispatch.rb b/lib/postzord/dispatch.rb new file mode 100644 index 000000000..f74c365da --- /dev/null +++ b/lib/postzord/dispatch.rb @@ -0,0 +1,67 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +class Postzord::Dispatch + def initialize(user, object) + unless object.respond_to? :to_diaspora_xml + raise 'this object does not respond_to? to_diaspora xml. try including Diaspora::Webhooks into your object' + end + @sender = user + @sender_person = @sender.person + @object = object + @xml = @object.to_diaspora_xml + @subscribers = @object.subscribers + @salmon_factory = Salmon::SalmonSlap.create(@sender, @xml) + end + + def post(opts = {}) + remote_people, local_people = @subscribers.partition{ |person| person.owner_id.nil? } + user_ids = [*local_people].map{|x| x.owner_id } + local_users = User.all(:id.in => user_ids) + self.socket_to_users(local_users) + self.deliver_to_remote(remote_people) + self.deliver_to_local(local_people) + self.deliver_to_services(opts[:url]) + end + + protected + def deliver_to_remote(people) + people.each do |person| + enc_xml = @salmon_factory.xml_for(person) + Rails.logger.info("event=push_to_person route=remote sender=#{@sender.person.diaspora_handle} recipient=#{person.diaspora_handle} payload_type=#{@object.class}") + Resque.enqueue(Jobs::HttpPost, person.receive_url, enc_xml) + end + end + + def deliver_to_local(people) + people.each do |person| + Rails.logger.info("event=push_to_local_person route=local sender=#{@sender_person.diaspora_handle} recipient=#{person.diaspora_handle} payload_type=#{@object.class}") + Resque.enqueue(Jobs::Receive, person.owner_id, @xml, @sender_person.id) + end + end + + def deliver_to_hub + Rails.logger.debug("event=post_to_service type=pubsub sender_handle=#{@sender.diaspora_handle}") + EventMachine::PubSubHubbub.new(AppConfig[:pubsub_server]).publish(@sender.public_url) + end + + def deliver_to_services(url) + if @object.respond_to?(:public) && @object.public + deliver_to_hub + if @object.respond_to?(:message) + @sender.services.each do |service| + service.post(@object, url) + end + end + end + end + + def socket_to_users(users) + if @object.respond_to?(:socket_to_uid) + users.each do |user| + @object.socket_to_uid(user) + end + end + end +end diff --git a/spec/lib/postzord/dispatch_spec.rb b/spec/lib/postzord/dispatch_spec.rb new file mode 100644 index 000000000..011940f71 --- /dev/null +++ b/spec/lib/postzord/dispatch_spec.rb @@ -0,0 +1,156 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +require File.join(Rails.root, 'lib/postzord') +require File.join(Rails.root, 'lib/postzord/dispatch') + +describe Postzord::Dispatch do + before do + @user = make_user + @sm = Factory(:status_message, :public => true) + @subscribers = [] + 5.times{@subscribers << Factory(:person)} + @sm.stub!(:subscribers) + @xml = @sm.to_diaspora_xml + end + + describe '.initialize' do + it 'takes an sender(User) and object (responds_to #subscibers) and sets then to @sender and @object' do + zord = Postzord::Dispatch.new(@user, @sm) + zord.instance_variable_get(:@sender).should == @user + zord.instance_variable_get(:@object).should == @sm + end + + it 'sets @subscribers from object' do + @sm.should_receive(:subscribers).and_return(@subscribers) + zord = Postzord::Dispatch.new(@user, @sm) + zord.instance_variable_get(:@subscribers).should == @subscribers + end + + it 'sets the @sender_person object' do + + zord = Postzord::Dispatch.new(@user, @sm) + zord.instance_variable_get(:@sender_person).should == @user.person + end + + it 'raises and gives you a helpful message if the object can not federate' do + proc{ Postzord::Dispatch.new(@user, []) + }.should raise_error /Diaspora::Webhooks/ + end + + it 'creates a salmon base object' do + zord = Postzord::Dispatch.new(@user, @sm) + zord.instance_variable_get(:@salmon_factory).should_not be nil + end + end + + context 'instance methods' do + before do + @local_user = make_user + @subscribers << @local_user.person + @remote_people, @local_people = @subscribers.partition{ |person| person.owner_id.nil? } + @sm.stub!(:subscribers).and_return @subscribers + @zord = Postzord::Dispatch.new(@user, @sm) + end + + describe '#post' do + before do + @zord.stub!(:socket_to_users) + end + it 'calls Array#partition on subscribers' do + @subscribers.should_receive(:partition).and_return([@remote_people, @local_people]) + @zord.post + end + + it 'calls #deliver_to_local with local people' do + @zord.should_receive(:deliver_to_local).with(@local_people) + @zord.post + end + + it 'calls #deliver_to_remote with remote people' do + @zord.should_receive(:deliver_to_remote).with(@remote_people) + @zord.post + end + + it 'calls socket_to_users with the local users' do + @zord.should_receive(:socket_to_users).with([@local_user]) + @zord.post + end + end + + describe '#deliver_to_remote' do + + before do + @remote_people = [] + @remote_people << @user.person + @mailman = Postzord::Dispatch.new(@user, @sm) + end + + it 'should queue an HttpPost job for each remote person' do + Resque.should_receive(:enqueue).with(Jobs::HttpPost, @user.person.receive_url, anything).once + @mailman.send(:deliver_to_remote, @remote_people) + end + + it 'calls salmon_for each remote person' do + salmon = @mailman.instance_variable_get(:@salmon_factory) + salmon.should_receive(:xml_for).with(@user.person) + @mailman.send(:deliver_to_remote, @remote_people) + end + end + + describe '#deliver_to_local' do + it 'sends each person an object' do + local_people = [] + local_people << @user.person + mailman = Postzord::Dispatch.new(@user, @sm) + Resque.should_receive(:enqueue).with(Jobs::Receive, @user.id, @xml, anything).once + mailman.send(:deliver_to_local, local_people) + end + end + + describe '#deliver_to_services' do + before do + @user.aspects.create(:name => "whatever") + @service = Services::Facebook.new(:access_token => "yeah") + @user.services << @service + end + + it 'calls post for each of the users services' do + @service.should_receive(:post).once + @zord.instance_variable_get(:@sender).should_receive(:services).and_return([@service]) + @zord.send(:deliver_to_services, nil) + end + + it 'notifies the hub' do + @zord.should_receive(:deliver_to_hub) + @zord.send(:deliver_to_services, nil) + end + + it 'only pushes to services if the object is public' do + mailman = Postzord::Dispatch.new(@user, Factory(:status_message)) + + mailman.should_not_receive(:deliver_to_hub) + mailman.instance_variable_get(:@user).should_not_receive(:services) + end + end + + describe '#socket_to_users' do + it 'should call object#socket_to_uid for each local user' do + @zord.instance_variable_get(:@object).should_receive(:socket_to_uid) + @zord.send(:socket_to_users, [@local_user]) + end + + it 'only tries to socket when the object responds to #socket_to_uid' do + f = Request.new + f.stub!(:subscribers) + users = [@user] + z = Postzord::Dispatch.new(@user, f) + users.should_not_receive(:each) # checking if each is called due to respond_to, actually trying to + z.send(:socket_to_users, users) + end + end + end +end diff --git a/spec/models/jobs/http_post_spec.rb b/spec/models/jobs/http_post_spec.rb index 18461996b..f5301d6cf 100644 --- a/spec/models/jobs/http_post_spec.rb +++ b/spec/models/jobs/http_post_spec.rb @@ -4,13 +4,14 @@ describe Jobs::HttpPost do before do @url = 'example.org/things/on/fire' @body = 'California' + @escaped_body = CGI::escape(@body) end it 'POSTs to a given URL' do - RestClient.should_receive(:post).with(@url, {:xml=>@body}).and_return(true) + RestClient.should_receive(:post).with(@url, {:xml=>@escaped_body}).and_return(true) Jobs::HttpPost.perform(@url, @body, 3) end it 'retries' do - RestClient.should_receive(:post).with(@url, {:xml=>@body}).and_raise(SocketError) + RestClient.should_receive(:post).with(@url, {:xml=>@escaped_body}).and_raise(SocketError) Resque.should_receive(:enqueue).with(Jobs::HttpPost, @url, @body, 1).once Jobs::HttpPost.perform(@url, @body, 2) end