Add support to parse RFC 7033 WebFinger JSON
Also: * Fix date format when generating JRD document * Sort elements always with the same order
This commit is contained in:
parent
9d72c9855a
commit
6852f9ca36
4 changed files with 170 additions and 65 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -39,6 +39,63 @@ XML
|
|||
</XRD>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -15,6 +15,36 @@ module DiasporaFederation
|
|||
</XRD>
|
||||
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("<html></html>") }.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
|
||||
|
|
|
|||
Loading…
Reference in a new issue