diff --git a/lib/diaspora_federation/discovery/web_finger.rb b/lib/diaspora_federation/discovery/web_finger.rb index 70f13f1..c16f634 100644 --- a/lib/diaspora_federation/discovery/web_finger.rb +++ b/lib/diaspora_federation/discovery/web_finger.rb @@ -119,8 +119,20 @@ module DiasporaFederation # @return [WebFinger] WebFinger instance # @raise [InvalidData] if the given XML string is invalid or incomplete def self.from_xml(webfinger_xml) - data = parse_xml_and_validate(webfinger_xml) + from_hash(parse_xml_and_validate(webfinger_xml)) + end + # Creates a WebFinger instance from the given JSON string + # @param [String] webfinger_json WebFinger JSON string + # @return [WebFinger] WebFinger instance + def self.from_json(webfinger_json) + from_hash(XrdDocument.json_data(webfinger_json)) + end + + # Creates a WebFinger instance from the given data + # @param [Hash] data WebFinger data hash + # @return [WebFinger] WebFinger instance + def self.from_hash(data) links = data[:links] new( diff --git a/lib/diaspora_federation/discovery/xrd_document.rb b/lib/diaspora_federation/discovery/xrd_document.rb index a77bf8c..354594c 100644 --- a/lib/diaspora_federation/discovery/xrd_document.rb +++ b/lib/diaspora_federation/discovery/xrd_document.rb @@ -83,10 +83,10 @@ module DiasporaFederation def to_json { subject: subject, - expires: expires, + expires: (expires.strftime(DATETIME_FORMAT) if expires.instance_of?(DateTime)), aliases: (aliases if aliases.any?), - links: (links if links.any?), - properties: (properties if properties.any?) + properties: (properties if properties.any?), + links: (links if links.any?) }.reject {|_, v| v.nil? } end @@ -115,6 +115,27 @@ module DiasporaFederation end end + # Parse the JRD document from the given string and create a hash containing + # the extracted data with symbolized keys. + # + # @param [String] jrd_doc JSON string + # @return [Hash] extracted data + # @raise [InvalidDocument] if the JRD is malformed + def self.json_data(jrd_doc) + json_hash = JSON.parse(jrd_doc) + + { + subject: json_hash["subject"], + expires: (DateTime.strptime(json_hash["expires"], DATETIME_FORMAT) if json_hash.key?("expires")), + aliases: json_hash["aliases"], + properties: json_hash["properties"], + links: symbolize_keys_for_links(json_hash["links"]) + }.reject {|_, v| v.nil? } + rescue JSON::JSONError => e + raise InvalidDocument, + "Not a JRD document: #{e.class}: #{e.message[0..255].encode(Encoding.default_external, undef: :replace)}" + end + private attr_reader :expires @@ -180,6 +201,17 @@ module DiasporaFederation end data[:links] = links unless links.empty? end + + # symbolize link keys from JSON hash, but only convert known keys + private_class_method def self.symbolize_keys_for_links(links) + links.map do |link| + {}.tap do |hash| + LINK_ATTRS.each do |attr| + hash[attr] = link[attr.to_s] if link.key?(attr.to_s) + end + end + end + end end end end diff --git a/spec/lib/diaspora_federation/discovery/web_finger_spec.rb b/spec/lib/diaspora_federation/discovery/web_finger_spec.rb index ce2a612..fb0a2a4 100644 --- a/spec/lib/diaspora_federation/discovery/web_finger_spec.rb +++ b/spec/lib/diaspora_federation/discovery/web_finger_spec.rb @@ -39,6 +39,63 @@ XML XML + let(:json) { <<-JSON } +{ + "subject": "#{acct}", + "aliases": [ + "#{person.alias_url}" + ], + "links": [ + { + "rel": "http://microformats.org/profile/hcard", + "type": "text/html", + "href": "#{person.hcard_url}" + }, + { + "rel": "http://joindiaspora.com/seed_location", + "type": "text/html", + "href": "#{person.url}" + }, + { + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html", + "href": "#{person.profile_url}" + }, + { + "rel": "http://schemas.google.com/g/2010#updates-from", + "type": "application/atom+xml", + "href": "#{person.atom_url}" + }, + { + "rel": "salmon", + "href": "#{person.salmon_url}" + }, + { + "rel": "http://ostatus.org/schema/1.0/subscribe", + "template": "http://somehost:3000/people?q={uri}" + } + ] +} +JSON + + let(:minimal_json) { <<-JSON } +{ + "subject": "#{acct}", + "links": [ + { + "rel": "http://microformats.org/profile/hcard", + "type": "text/html", + "href": "#{person.hcard_url}" + }, + { + "rel": "http://joindiaspora.com/seed_location", + "type": "text/html", + "href": "#{person.url}" + } + ] +} +JSON + let(:string) { "WebFinger:#{data[:acct_uri]}" } it_behaves_like "an Entity subclass" @@ -95,66 +152,11 @@ XML context "json" do it "creates a nice JSON document" do - json = <<-JSON -{ - "subject": "#{acct}", - "aliases": [ - "#{person.alias_url}" - ], - "links": [ - { - "rel": "http://microformats.org/profile/hcard", - "type": "text/html", - "href": "#{person.hcard_url}" - }, - { - "rel": "http://joindiaspora.com/seed_location", - "type": "text/html", - "href": "#{person.url}" - }, - { - "rel": "http://webfinger.net/rel/profile-page", - "type": "text/html", - "href": "#{person.profile_url}" - }, - { - "rel": "http://schemas.google.com/g/2010#updates-from", - "type": "application/atom+xml", - "href": "#{person.atom_url}" - }, - { - "rel": "salmon", - "href": "#{person.salmon_url}" - }, - { - "rel": "http://ostatus.org/schema/1.0/subscribe", - "template": "http://somehost:3000/people?q={uri}" - } - ] -} -JSON wf = Discovery::WebFinger.new(data, aliases: [person.alias_url]) expect(JSON.pretty_generate(wf.to_json)).to eq(json.strip) end it "creates minimal JSON document" do - minimal_json = <<-JSON -{ - "subject": "#{acct}", - "links": [ - { - "rel": "http://microformats.org/profile/hcard", - "type": "text/html", - "href": "#{person.hcard_url}" - }, - { - "rel": "http://joindiaspora.com/seed_location", - "type": "text/html", - "href": "#{person.url}" - } - ] -} -JSON wf = Discovery::WebFinger.new(minimal_data) expect(JSON.pretty_generate(wf.to_json)).to eq(minimal_json.strip) end @@ -167,6 +169,9 @@ JSON "#{person.alias_url}", "#{person.profile_url}" ], + "properties": { + "http://webfinger.example/ns/name": "Bob Smith" + }, "links": [ { "rel": "http://microformats.org/profile/hcard", @@ -191,10 +196,7 @@ JSON "rel": "http://openid.net/specs/connect/1.0/issuer", "href": "https://pod.example.tld/" } - ], - "properties": { - "http://webfinger.example/ns/name": "Bob Smith" - } + ] } JSON @@ -205,7 +207,7 @@ JSON end context "parsing" do - it "reads its own output" do + it "reads its own xml output" do wf = Discovery::WebFinger.from_xml(xml) expect(wf.acct_uri).to eq(acct) expect(wf.hcard_url).to eq(person.hcard_url) @@ -223,6 +225,24 @@ JSON expect(wf.seed_url).to eq(person.url) end + it "reads its own json output" do + wf = Discovery::WebFinger.from_json(json) + expect(wf.acct_uri).to eq(acct) + expect(wf.hcard_url).to eq(person.hcard_url) + expect(wf.seed_url).to eq(person.url) + expect(wf.profile_url).to eq(person.profile_url) + expect(wf.atom_url).to eq(person.atom_url) + expect(wf.salmon_url).to eq(person.salmon_url) + expect(wf.subscribe_url).to eq(person.subscribe_url) + end + + it "reads minimal json" do + wf = Discovery::WebFinger.from_json(minimal_json) + expect(wf.acct_uri).to eq(acct) + expect(wf.hcard_url).to eq(person.hcard_url) + expect(wf.seed_url).to eq(person.url) + end + it "is frozen after parsing" do wf = Discovery::WebFinger.from_xml(xml) expect(wf).to be_frozen diff --git a/spec/lib/diaspora_federation/discovery/xrd_document_spec.rb b/spec/lib/diaspora_federation/discovery/xrd_document_spec.rb index 5c885cb..3fe8a3b 100644 --- a/spec/lib/diaspora_federation/discovery/xrd_document_spec.rb +++ b/spec/lib/diaspora_federation/discovery/xrd_document_spec.rb @@ -15,6 +15,36 @@ module DiasporaFederation XML + let(:json) { <<-JSON } +{ + "subject": "http://blog.example.com/article/id/314", + "expires": "2010-01-30T09:30:00Z", + "aliases": [ + "http://blog.example.com/cool_new_thing", + "http://blog.example.com/steve/article/7" + ], + "properties": { + "http://blgx.example.net/ns/version": "1.3", + "http://blgx.example.net/ns/ext": null + }, + "links": [ + { + "rel": "author", + "type": "text/html", + "href": "http://blog.example.com/author/steve" + }, + { + "rel": "author", + "href": "http://example.com/author/john" + }, + { + "rel": "copyright", + "template": "http://example.com/copyright?id={uri}" + } + ] +} +JSON + let(:data) { { subject: "http://blog.example.com/article/id/314", @@ -72,7 +102,7 @@ XML describe "#to_json" do it "provides the hash for json" do - expect(doc.to_json).to eq(data) + expect(JSON.pretty_generate(doc.to_json)).to eq(json.strip) end end @@ -90,5 +120,16 @@ XML expect { Discovery::XrdDocument.xml_data("") }.to raise_error Discovery::InvalidDocument end end + + describe ".json_data" do + it "reads the json document" do + hash = Discovery::XrdDocument.json_data(json) + expect(hash).to eq(data) + end + + it "raises InvalidDocument when a JSON error occurs" do + expect { Discovery::XrdDocument.json_data("foo") }.to raise_error Discovery::InvalidDocument + end + end end end