Add parsing of newer versions of NodeInfo
Always take the newest versions both nodes support
This commit is contained in:
parent
49ba740b45
commit
01f8f55dbb
3 changed files with 61 additions and 19 deletions
|
|
@ -3,7 +3,6 @@
|
||||||
class ConnectionTester
|
class ConnectionTester
|
||||||
include Diaspora::Logging
|
include Diaspora::Logging
|
||||||
|
|
||||||
NODEINFO_SCHEMA = "http://nodeinfo.diaspora.software/ns/schema/1.0"
|
|
||||||
NODEINFO_FRAGMENT = "/.well-known/nodeinfo"
|
NODEINFO_FRAGMENT = "/.well-known/nodeinfo"
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
@ -124,8 +123,11 @@ class ConnectionTester
|
||||||
def nodeinfo
|
def nodeinfo
|
||||||
with_http_connection do |http|
|
with_http_connection do |http|
|
||||||
ni_resp = http.get(NODEINFO_FRAGMENT)
|
ni_resp = http.get(NODEINFO_FRAGMENT)
|
||||||
nd_resp = http.get(find_nodeinfo_url(ni_resp.body))
|
ni_urls = find_nodeinfo_urls(ni_resp.body)
|
||||||
find_software_version(nd_resp.body)
|
raise NodeInfoFailure, "No supported NodeInfo version found" if ni_urls.empty?
|
||||||
|
|
||||||
|
version, url = ni_urls.max
|
||||||
|
find_software_version(version, http.get(url).body)
|
||||||
end
|
end
|
||||||
rescue Faraday::ClientError => e
|
rescue Faraday::ClientError => e
|
||||||
raise HTTPFailure, "#{e.class}: #{e.message}"
|
raise HTTPFailure, "#{e.class}: #{e.message}"
|
||||||
|
|
@ -180,20 +182,23 @@ class ConnectionTester
|
||||||
@result.ssl = (response.env.url.scheme == "https")
|
@result.ssl = (response.env.url.scheme == "https")
|
||||||
end
|
end
|
||||||
|
|
||||||
# walk the JSON document, get the actual document location
|
# walk the JSON document, get the actual document locations
|
||||||
def find_nodeinfo_url(body)
|
def find_nodeinfo_urls(body)
|
||||||
jrd = JSON.parse(body)
|
jrd = JSON.parse(body)
|
||||||
links = jrd.fetch("links")
|
links = jrd.fetch("links")
|
||||||
raise NodeInfoFailure, "invalid JRD: '#/links' is not an array!" unless links.is_a?(Array)
|
raise NodeInfoFailure, "invalid JRD: '#/links' is not an array!" unless links.is_a?(Array)
|
||||||
links.find { |entry|
|
|
||||||
entry.fetch("rel") == NODEINFO_SCHEMA
|
supported_rel_map = NodeInfo::VERSIONS.index_by {|v| "http://nodeinfo.diaspora.software/ns/schema/#{v}" }
|
||||||
}.fetch("href")
|
links.map {|entry|
|
||||||
|
version = supported_rel_map[entry.fetch("rel")]
|
||||||
|
[version, entry.fetch("href")] if version
|
||||||
|
}.compact.to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
# walk the JSON document, find the version string
|
# walk the JSON document, find the version string
|
||||||
def find_software_version(body)
|
def find_software_version(version, body)
|
||||||
info = JSON.parse(body)
|
info = JSON.parse(body)
|
||||||
JSON::Validator.validate!(NodeInfo.schema("1.0"), info)
|
JSON::Validator.validate!(NodeInfo.schema(version), info)
|
||||||
sw = info.fetch("software")
|
sw = info.fetch("software")
|
||||||
@result.software_version = "#{sw.fetch('name')} #{sw.fetch('version')}"
|
@result.software_version = "#{sw.fetch('name')} #{sw.fetch('version')}"
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ require "json-schema"
|
||||||
module NodeInfo
|
module NodeInfo
|
||||||
VERSIONS = %w[1.0 2.0 2.1].freeze
|
VERSIONS = %w[1.0 2.0 2.1].freeze
|
||||||
SCHEMAS = {} # rubocop:disable Style/MutableConstant
|
SCHEMAS = {} # rubocop:disable Style/MutableConstant
|
||||||
private_constant :VERSIONS, :SCHEMAS
|
private_constant :SCHEMAS
|
||||||
|
|
||||||
Document = Struct.new(:version, :software, :protocols, :services, :open_registrations, :usage, :metadata) do
|
Document = Struct.new(:version, :software, :protocols, :services, :open_registrations, :usage, :metadata) do
|
||||||
# rubocop:disable Lint/ConstantDefinitionInBlock
|
# rubocop:disable Lint/ConstantDefinitionInBlock
|
||||||
|
|
|
||||||
|
|
@ -106,24 +106,60 @@ describe ConnectionTester do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#nodeinfo" do
|
describe "#nodeinfo" do
|
||||||
let(:ni_wellknown) { {links: [{rel: ConnectionTester::NODEINFO_SCHEMA, href: "/nodeinfo"}]} }
|
def build_ni_document(version)
|
||||||
|
NodeInfo.build do |doc|
|
||||||
it "reads the version from the nodeinfo document" do
|
doc.version = version
|
||||||
ni_document = NodeInfo.build do |doc|
|
|
||||||
doc.version = "1.0"
|
|
||||||
doc.open_registrations = true
|
doc.open_registrations = true
|
||||||
doc.protocols.protocols << "diaspora"
|
doc.protocols.protocols << "diaspora"
|
||||||
doc.software.name = "diaspora"
|
doc.software.name = "diaspora"
|
||||||
doc.software.version = "a.b.c.d"
|
doc.software.version = "a.b.c.d"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
NodeInfo::VERSIONS.each do |version|
|
||||||
|
context "with version #{version}" do
|
||||||
|
let(:ni_wellknown) {
|
||||||
|
{links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/#{version}", href: "/nodeinfo/#{version}"}]}
|
||||||
|
}
|
||||||
|
|
||||||
|
it "reads the version from the nodeinfo document" do
|
||||||
|
ni_document = build_ni_document(version)
|
||||||
|
|
||||||
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
||||||
.to_return(status: 200, body: JSON.generate(ni_wellknown))
|
.to_return(status: 200, body: JSON.generate(ni_wellknown))
|
||||||
stub_request(:get, "#{url}/nodeinfo").to_return(status: 200, body: JSON.generate(ni_document.as_json))
|
stub_request(:get, "#{url}/nodeinfo/#{version}")
|
||||||
|
.to_return(status: 200, body: JSON.generate(ni_document.as_json))
|
||||||
|
|
||||||
tester.nodeinfo
|
tester.nodeinfo
|
||||||
expect(result.software_version).to eq("diaspora a.b.c.d")
|
expect(result.software_version).to eq("diaspora a.b.c.d")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses the latest commonly supported version" do
|
||||||
|
ni_wellknown = {links: [
|
||||||
|
{rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"},
|
||||||
|
{rel: "http://nodeinfo.diaspora.software/ns/schema/1.1", href: "/nodeinfo/1.1"},
|
||||||
|
{rel: "http://nodeinfo.diaspora.software/ns/schema/2.0", href: "/nodeinfo/2.0"},
|
||||||
|
{rel: "http://nodeinfo.diaspora.software/ns/schema/9.0", href: "/nodeinfo/9.0"}
|
||||||
|
]}
|
||||||
|
|
||||||
|
ni_document = build_ni_document("2.0")
|
||||||
|
|
||||||
|
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
||||||
|
.to_return(status: 200, body: JSON.generate(ni_wellknown))
|
||||||
|
stub_request(:get, "#{url}/nodeinfo/2.0").to_return(status: 200, body: JSON.generate(ni_document.as_json))
|
||||||
|
|
||||||
|
tester.nodeinfo
|
||||||
|
expect(result.software_version).to eq("diaspora a.b.c.d")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles no common version gracefully" do
|
||||||
|
ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.1", href: "/nodeinfo/1.1"}]}
|
||||||
|
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
||||||
|
.to_return(status: 200, body: JSON.generate(ni_wellknown))
|
||||||
|
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
|
||||||
|
end
|
||||||
|
|
||||||
it "fails the nodeinfo document is missing" do
|
it "fails the nodeinfo document is missing" do
|
||||||
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}").to_return(status: 404, body: "Not Found")
|
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}").to_return(status: 404, body: "Not Found")
|
||||||
|
|
@ -137,16 +173,17 @@ describe ConnectionTester do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles a invalid jrd document gracefully" do
|
it "handles a invalid jrd document gracefully" do
|
||||||
invalid_wellknown = {links: {rel: ConnectionTester::NODEINFO_SCHEMA, href: "/nodeinfo"}}
|
invalid_wellknown = {links: {rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}}
|
||||||
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
||||||
.to_return(status: 200, body: JSON.generate(invalid_wellknown))
|
.to_return(status: 200, body: JSON.generate(invalid_wellknown))
|
||||||
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
|
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles a invalid nodeinfo document gracefully" do
|
it "handles a invalid nodeinfo document gracefully" do
|
||||||
|
ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}]}
|
||||||
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
|
||||||
.to_return(status: 200, body: JSON.generate(ni_wellknown))
|
.to_return(status: 200, body: JSON.generate(ni_wellknown))
|
||||||
stub_request(:get, "#{url}/nodeinfo").to_return(status: 200, body: '{"software": "invalid nodeinfo"}')
|
stub_request(:get, "#{url}/nodeinfo/1.0").to_return(status: 200, body: '{"software": "invalid nodeinfo"}')
|
||||||
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
|
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue