Compare commits

...

26 Commits

Author SHA1 Message Date
dependabot[bot] d013213138
Bump rake from 12.0.0 to 12.3.3 (#14)
Bumps [rake](https://github.com/ruby/rake) from 12.0.0 to 12.3.3.
- [Release notes](https://github.com/ruby/rake/releases)
- [Changelog](https://github.com/ruby/rake/blob/master/History.rdoc)
- [Commits](https://github.com/ruby/rake/compare/v12.0.0...v12.3.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-03-19 18:21:48 +01:00
Eugen Rochko 4f585464ba
Update gempush.yml 2020-01-12 15:31:29 +01:00
Eugen Rochko 62d296bb98
Bump version to 2.1.1 (#12)
* Bump version to 2.1.1

* Update README.md
2020-01-12 15:25:22 +01:00
Eugen Rochko 4c57f5978d
Create gempush.yml 2020-01-12 14:03:26 +01:00
dependabot[bot] d0f59cb65d Bump nokogiri from 1.8.5 to 1.10.5 (#11)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.8.5 to 1.10.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.8.5...v1.10.5)

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-14 13:48:16 +01:00
Tom Scott 2deb9da0ab Upgrade to HTTP.rb v4 (#9)
This allows Goldfinger to run against the HTTP gem v4.x, by updating the
`HTTP.timeout` syntax used in `Goldfinger::Request`.
2019-01-14 09:23:20 +00:00
Eugen Rochko 1fca7e670e Update dependencies and bump to 2.1.0 2018-01-23 05:23:16 +01:00
MIYAGI Hikaru d5f16ebeca Enable to pass options to HTTP.rb, Allow insecure request (#6)
* Enable to pass options to HTTP.rb, Allow insecure request

* Move SSL option to the hash

* fix a bug
2018-01-22 00:11:46 +01:00
Eugen Rochko 114c45f62d Bump version to 2.0.1 2017-08-05 14:51:57 +02:00
Yamagishi Kazutoshi 29dce59043 Cast HTTP response body for JSON to string (regression from #4) (#5)
Oj uses `readpartial` instead of `to_s` if the value given for the arguments has a `readpartial` method.
Also, HTTP.rb splits the response if `Transfer-Encoding: chunked` is returned in the HTTP headers.

Will use `to_s` like `JSON.parse` which was used previously.
2017-08-04 14:05:28 +02:00
Eugen Rochko 13d8b032fd Update dependencies, do not mask HTTP/OpenSSL exceptions, do not fallback to HTTP (#4)
if HTTPS is not available
2017-07-19 23:36:11 +02:00
Eugen Rochko 36434d836e No longer compatible with Ruby 2.0 2017-05-09 14:59:44 +02:00
Eugen Rochko b6eeb547b3 Update readme links 2017-05-09 14:57:48 +02:00
Eugen Rochko cf17716d4c Update dependencies and bump to 1.2.1 2017-05-09 14:56:21 +02:00
Eugen Rochko eddf330c22 The Webfinger spec now mandates a standard URL. Check that URL first, instead of
checking host-meta first (which may actually not be present). Bump to 1.2.0
2017-04-27 16:25:20 +02:00
Eugen Rochko 73a44507c6 Making content-type checking less strict (allow text/xml) 2016-10-12 20:58:17 +02:00
Eugen Rochko 3fae9826cf Catch XML syntax errors 2016-09-30 23:06:11 +02:00
Eugen Rochko 868cfbe0d8 Finally actually update httprb 2016-09-27 23:03:29 +02:00
Eugen Rochko 592248e4a7 Add timeouts to requests, update dependencies, bump to 1.0.3 2016-09-20 01:09:05 +02:00
Eugen Rochko f0a7a9baf3 Goldfinger aborts if webfinger response mime type is an unexpected value it can't parse 2016-03-07 12:23:48 +01:00
Eugen Rochko b2b1b5c252 Fix wrong dependency group for pry 2016-02-18 01:49:55 +01:00
Eugen Rochko 8eb4e350b1 Fix readme badges
[ci skip]
2016-02-18 01:46:25 +01:00
Eugen Rochko 237a9c2e81 Adding travis CI 2016-02-18 01:44:36 +01:00
Eugen Rochko c376932f31 Update Gemfile.lock 2016-02-17 23:33:08 +01:00
Eugen Rochko f127a1f30f Implement RFC 7033, bump version to 1.0.0, add some documentation 2016-02-17 23:16:08 +01:00
Eugen Rochko 75f1b1faf9 Add handling for SSL errors, bump version to 0.1.1 2016-02-17 16:59:57 +01:00
23 changed files with 630 additions and 146 deletions

41
.github/workflows/gempush.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Ruby Gem
on:
push:
tags:
- v*
jobs:
build:
name: Build + Publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
version: 2.6.x
- name: Publish to GPR
run: |
mkdir -p $HOME/.gem
touch $HOME/.gem/credentials
chmod 0600 $HOME/.gem/credentials
printf -- "---\n:github: Bearer ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
gem build *.gemspec
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
env:
GEM_HOST_API_KEY: ${{secrets.GPR_AUTH_TOKEN}}
OWNER: tootsuite
- name: Publish to RubyGems
run: |
mkdir -p $HOME/.gem
touch $HOME/.gem/credentials
chmod 0600 $HOME/.gem/credentials
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
gem build *.gemspec
gem push *.gem
env:
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.gem

2
.rspec
View File

@ -1,3 +1,3 @@
--color
--require spec_helper
--format NyanCatFormatter
--format Fuubar

100
.rubocop.yml Normal file
View File

@ -0,0 +1,100 @@
AllCops:
TargetRubyVersion: 2.3
Exclude:
- 'spec/**/*'
- 'bin/*'
- 'Rakefile'
- 'vendor/**/*'
Bundler/OrderedGems:
Enabled: false
Layout/AccessModifierIndentation:
EnforcedStyle: indent
Layout/EmptyLineAfterMagicComment:
Enabled: false
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: space
Metrics/AbcSize:
Max: 100
Metrics/BlockNesting:
Max: 3
Metrics/ClassLength:
CountComments: false
Max: 200
Metrics/CyclomaticComplexity:
Max: 15
Metrics/LineLength:
AllowURI: true
Enabled: false
Metrics/MethodLength:
CountComments: false
Max: 55
Metrics/ModuleLength:
CountComments: false
Max: 200
Metrics/ParameterLists:
Max: 4
CountKeywordArgs: true
Metrics/PerceivedComplexity:
Max: 10
Rails:
Enabled: true
Rails/HasAndBelongsToMany:
Enabled: false
Rails/SkipsModelValidations:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/CollectionMethods:
Enabled: true
PreferredMethods:
find_all: 'select'
Style/Documentation:
Enabled: false
Style/DoubleNegation:
Enabled: true
Style/FrozenStringLiteralComment:
Enabled: true
Style/GuardClause:
Enabled: false
Style/Lambda:
Enabled: false
Style/PercentLiteralDelimiters:
PreferredDelimiters:
'%i': '()'
'%w': '()'
Style/PerlBackrefs:
AutoCorrect: false
Style/RegexpLiteral:
Enabled: false
Style/SymbolArray:
Enabled: false
Style/TrailingCommaInLiteral:
EnforcedStyleForMultiline: 'comma'

View File

@ -1,2 +1 @@
2.2.0
2.5.0

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
language: ruby
cache: bundler
notifications:
email: false
rvm:
- 2.3.1
- 2.4.1
bundler_args: --without development --retry=3 --jobs=3
script: bundle exec rake

View File

@ -1,11 +1,14 @@
source "https://rubygems.org"
gem 'pry'
group :development, :test do
gem 'pry', '>= 0.10.3'
end
group :test do
gem 'rspec', '>= 3.0'
gem 'nyan-cat-formatter'
gem 'fuubar'
gem 'webmock'
gem 'rake'
end
gemspec

View File

@ -1,60 +1,72 @@
PATH
remote: .
specs:
goldfinger (0.1.0)
addressable (~> 2.4)
http (~> 1.0)
nokogiri (~> 1.6)
goldfinger (2.1.1)
addressable (~> 2.5)
http (~> 4.0)
nokogiri (~> 1.8)
oj (~> 3.0)
GEM
remote: https://rubygems.org/
specs:
addressable (2.4.0)
coderay (1.1.0)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
coderay (1.1.1)
crack (0.4.3)
safe_yaml (~> 1.0.0)
diff-lcs (1.2.5)
domain_name (0.5.20160128)
diff-lcs (1.3)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
hashdiff (0.3.0)
http (1.0.2)
ffi (1.12.2)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
hashdiff (0.3.4)
http (4.3.0)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 1.0.1)
http_parser.rb (~> 0.6.0)
http-cookie (1.0.2)
http-form_data (~> 2.2)
http-parser (~> 1.2.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
http-form_data (1.0.1)
http_parser.rb (0.6.0)
http-form_data (2.2.0)
http-parser (1.2.1)
ffi-compiler (>= 1.0, < 2.0)
method_source (0.8.2)
mini_portile2 (2.0.0)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
nyan-cat-formatter (0.11)
rspec (>= 2.99, >= 2.14.2, < 4)
pry (0.10.1)
mini_portile2 (2.4.0)
nokogiri (1.10.8)
mini_portile2 (~> 2.4.0)
oj (3.10.2)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-core (3.4.2)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
public_suffix (3.0.3)
rake (12.3.3)
rspec (3.6.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-core (3.6.0)
rspec-support (~> 3.6.0)
rspec-expectations (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
rspec-support (~> 3.6.0)
rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
rspec-support (~> 3.6.0)
rspec-support (3.6.0)
ruby-progressbar (1.8.1)
safe_yaml (1.0.4)
slop (3.6.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
webmock (1.22.6)
unf_ext (0.0.7.6)
webmock (3.0.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
@ -63,9 +75,13 @@ PLATFORMS
ruby
DEPENDENCIES
bundler (~> 1.3)
bundler (~> 1.15)
fuubar
goldfinger!
nyan-cat-formatter
pry
pry (>= 0.10.3)
rake
rspec (>= 3.0)
webmock
BUNDLED WITH
1.16.1

View File

@ -1,11 +1,16 @@
Goldfinger, a Webfinger client for Ruby
Goldfinger, a WebFinger client for Ruby
=======================================
[![Gem Version](http://img.shields.io/gem/v/goldfinger.svg)][gem]
[![Build Status](http://img.shields.io/travis/tootsuite/goldfinger.svg)][travis]
[gem]: https://rubygems.org/gems/goldfinger
[travis]: https://travis-ci.org/tootsuite/goldfinger
A Webfinger client for Ruby. Supports `application/xrd+xml` and `application/jrd+json` responses. Raises `Goldfinger::Error::NotFound` on failure to fetch the Webfinger or XRD data.
A WebFinger client for Ruby. Supports `application/xrd+xml` and `application/jrd+json` responses. Raises `Goldfinger::NotFoundError` on failure to fetch the Webfinger or XRD data, can also raise `HTTP:Error` or `OpenSSL::SSL::SSLError` if something is wrong with the HTTPS connection it uses.
- **Does not** fall back to HTTP if HTTPS is not available
- **Does** check host-meta XRD, but *only* if the standard WebFinger path yielded no result
## Installation
@ -14,9 +19,16 @@ A Webfinger client for Ruby. Supports `application/xrd+xml` and `application/jrd
## Usage
data = Goldfinger.finger('acct:gargron@quitter.no')
data.link('http://schemas.google.com/g/2010#updates-from')[:href]
data.link('http://schemas.google.com/g/2010#updates-from').href
# => "https://quitter.no/api/statuses/user_timeline/7477.atom"
data.aliases
# => ["https://quitter.no/user/7477", "https://quitter.no/gargron"]
data.subject
# => "acct:gargron@quitter.no"
## RFC support
The gem only parses link data. It does not currently parse aliases, properties, or more complex structures.
The official WebFinger RFC is [7033](https://tools.ietf.org/html/rfc7033).

8
Rakefile Normal file
View File

@ -0,0 +1,8 @@
require 'rspec/core/rake_task'
require 'bundler/gem_tasks'
RSpec::Core::RakeTask.new(:spec) do |task|
task.rspec_opts = ['--color', '--format', 'documentation']
end
task :default => :spec

View File

@ -1,20 +1,23 @@
# frozen_string_literal: true
Gem::Specification.new do |s|
s.name = 'goldfinger'
s.version = '0.1.0'
s.version = '2.1.1'
s.platform = Gem::Platform::RUBY
s.required_ruby_version = '>= 2.0.0'
s.required_ruby_version = '>= 2.3.0'
s.date = '2016-02-17'
s.summary = "A Webfinger utility for Ruby"
s.description = "A Webfinger utility for Ruby"
s.authors = ["Eugen Rochko"]
s.summary = 'A Webfinger utility for Ruby'
s.description = 'A Webfinger utility for Ruby'
s.authors = ['Eugen Rochko']
s.email = 'eugen@zeonfederated.com'
s.files = `git ls-files lib LICENSE README.md`.split($RS)
s.homepage = 'https://github.com/Gargron/goldfinger'
s.homepage = 'https://github.com/tootsuite/goldfinger'
s.licenses = ['MIT']
s.add_dependency('http', '~> 1.0')
s.add_dependency('addressable', '~> 2.4')
s.add_dependency('nokogiri', '~> 1.6')
s.add_dependency('http', '~> 4.0')
s.add_dependency('addressable', '~> 2.5')
s.add_dependency('nokogiri', '~> 1.8')
s.add_dependency('oj', '~> 3.0')
s.add_development_dependency('bundler', '~> 1.3')
s.add_development_dependency('bundler', '~> 1.15')
end

View File

@ -1,15 +1,26 @@
# frozen_string_literal: true
require 'goldfinger/request'
require 'goldfinger/link'
require 'goldfinger/result'
require 'goldfinger/utils'
require 'goldfinger/client'
module Goldfinger
module Error
class NotFound < StandardError
end
class Error < StandardError
end
def self.finger(uri)
Goldfinger::Client.new(uri).finger
class NotFoundError < Error
end
# Returns result for the Webfinger query
#
# @raise [Goldfinger::NotFoundError] Error raised when the Webfinger resource could not be retrieved
# @raise [Goldfinger::SSLError] Error raised when there was a SSL error when fetching the resource
# @param uri [String] A full resource identifier in the format acct:user@example.com
# @param opts [Hash] Options passed to HTTP.rb client
# @return [Goldfinger::Result]
def self.finger(uri, opts = {})
Goldfinger::Client.new(uri, opts).finger
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'addressable'
require 'nokogiri'
@ -5,44 +7,54 @@ module Goldfinger
class Client
include Goldfinger::Utils
def initialize(uri)
def initialize(uri, opts = {})
@uri = uri
@ssl = opts.delete(:ssl) { true }
@scheme = @ssl ? 'https' : 'http'
@opts = opts
end
def finger
ssl = true
response = perform_get(standard_url, @opts)
begin
_, template = perform_get(url(ssl))
rescue HTTP::Error
if ssl
ssl = false
retry
else
raise Goldfinger::Error::NotFound
end
end
return finger_from_template if response.code != 200
headers, body = perform_get(url_from_template(template))
Goldfinger::Result.new(headers, body)
rescue HTTP::Error
raise Goldfinger::Error::NotFound
Goldfinger::Result.new(response)
rescue Addressable::URI::InvalidURIError
raise Goldfinger::NotFoundError, 'Invalid URI'
end
private
def url(ssl = true)
"http#{'s' if ssl}://#{domain}/.well-known/host-meta"
def finger_from_template
template = perform_get(url, @opts)
raise Goldfinger::NotFoundError, 'No host-meta on the server' if template.code != 200
response = perform_get(url_from_template(template.body), @opts)
raise Goldfinger::NotFoundError, 'No such user on the server' if response.code != 200
Goldfinger::Result.new(response)
end
def url
"#{@scheme}://#{domain}/.well-known/host-meta"
end
def standard_url
"#{@scheme}://#{domain}/.well-known/webfinger?resource=#{@uri}"
end
def url_from_template(template)
xml = Nokogiri::XML(template)
links = xml.xpath('//xmlns:Link[@rel="lrdd"]', xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0')
links = xml.xpath('//xmlns:Link[@rel="lrdd"]')
raise Goldfinger::Error::NotFound if links.empty?
raise Goldfinger::NotFoundError if links.empty?
url = Addressable::Template.new(links.first.attribute('template').value)
url.expand({ uri: @uri }).to_s
links.first.attribute('template').value.gsub('{uri}', @uri)
rescue Nokogiri::XML::XPath::SyntaxError
raise Goldfinger::Error, "Bad XML: #{template}"
end
def domain

58
lib/goldfinger/link.rb Normal file
View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
module Goldfinger
# @!attribute [r] href
# @return [String] The href the link points to
# @!attribute [r] template
# @return [String] The template the link contains
# @!attribute [r] type
# @return [String] The mime type of the link
# @!attribute [r] rel
# @return [String] The relation descriptor of the link
class Link
attr_reader :href, :template, :type, :rel
def initialize(a)
@href = a[:href]
@template = a[:template]
@type = a[:type]
@rel = a[:rel]
@titles = a[:titles]
@properties = a[:properties]
end
# The "titles" object comprises zero or more name/value pairs whose
# names are a language tag or the string "und". The string is
# human-readable and describes the link relation.
# @see #title
# @return [Array] Array form of the hash
def titles
@titles.to_a
end
# The "properties" object within the link relation object comprises
# zero or more name/value pairs whose names are URIs (referred to as
# "property identifiers") and whose values are strings or nil.
# Properties are used to convey additional information about the link
# relation.
# @see #property
# @return [Array] Array form of the hash
def properties
@properties.to_a
end
# Returns a title for a language
# @param lang [String]
# @return [String]
def title(lang)
@titles[lang]
end
# Returns a property for a key
# @param key [String]
# @return [String]
def property(key)
@properties[key]
end
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'http'
require 'addressable'
@ -10,14 +12,13 @@ module Goldfinger
end
def perform
response = http_client.request(@request_method, @uri.to_s, @options)
[response.headers, response.body]
http_client.request(@request_method, @uri.to_s, @options)
end
private
def http_client
HTTP
HTTP.timeout(write: 60, connect: 20, read: 60).follow
end
end
end

View File

@ -1,17 +1,61 @@
# frozen_string_literal: true
require 'oj'
module Goldfinger
# @!attribute [r] subject
# @return [String] URI that identifies the entity that the JRD describes.
# @!attribute [r] aliases
# @return [Array] Zero or more URI strings that identify the same entity as the "subject" URI.
class Result
def initialize(headers, body)
@mime_type = headers.get(HTTP::Headers::CONTENT_TYPE).first
@body = body
@links = {}
MIME_TYPES = [
'application/jrd+json',
'application/json',
'application/xrd+xml',
'application/xml',
'text/xml',
].freeze
attr_reader :subject, :aliases
def initialize(response)
@mime_type = response.mime_type
@body = response.body
@subject = nil
@aliases = []
@links = {}
@properties = {}
parse
end
# The "properties" object comprises zero or more name/value pairs whose
# names are URIs (referred to as "property identifiers") and whose
# values are strings or nil.
# @see #property
# @return [Array] Array form of the hash
def properties
@properties.to_a
end
# Returns a property for a key
# @param key [String]
# @return [String]
def property(key)
@properties[key]
end
# The "links" array has any number of member objects, each of which
# represents a link.
# @see #link
# @return [Array] Array form of the hash
def links
@links.to_a
end
# Returns a key for a relation
# @param key [String]
# @return [Goldfinger::Link]
def link(rel)
@links[rel]
end
@ -20,23 +64,49 @@ module Goldfinger
def parse
case @mime_type
when 'application/jrd+json'
when 'application/json'
when 'application/jrd+json', 'application/json'
parse_json
when 'application/xrd+xml'
when 'application/xrd+xml', 'application/xml', 'text/xml'
parse_xml
else
raise Goldfinger::Error, "Invalid response mime type: #{@mime_type}"
end
end
def parse_json
json = JSON.parse(@body)
json['links'].each { |link| @links[link['rel']] = Hash[link.keys.map { |key| [key.to_sym, link[key]] }] }
json = Oj.load(@body.to_s, mode: :null)
@subject = json['subject']
@aliases = json['aliases'] || []
@properties = json['properties'] || {}
json['links'].each do |link|
tmp = Hash[link.keys.map { |key| [key.to_sym, link[key]] }]
@links[link['rel']] = Goldfinger::Link.new(tmp)
end
end
def parse_xml
xml = Nokogiri::XML(@body)
links = xml.xpath('//xmlns:Link', xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0')
links.each { |link| @links[link.attribute('rel').value] = Hash[link.attributes.keys.map { |key| [key.to_sym, link.attribute(key).value] }] }
@subject = xml.at_xpath('//xmlns:Subject').content
@aliases = xml.xpath('//xmlns:Alias').map(&:content)
properties = xml.xpath('/xmlns:XRD/xmlns:Property')
properties.each { |prop| @properties[prop.attribute('type').value] = prop.attribute('nil') ? nil : prop.content }
xml.xpath('//xmlns:Link').each do |link|
rel = link.attribute('rel').value
tmp = Hash[link.attributes.keys.map { |key| [key.to_sym, link.attribute(key).value] }]
tmp[:titles] = {}
tmp[:properties] = {}
link.xpath('.//xmlns:Title').each { |title| tmp[:titles][title.attribute('lang').value] = title.content }
link.xpath('.//xmlns:Property').each { |prop| tmp[:properties][prop.attribute('type').value] = prop.attribute('nil') ? nil : prop.content }
@links[rel] = Goldfinger::Link.new(tmp)
end
end
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Goldfinger
module Utils
def perform_get(path, options = {})

View File

@ -1 +1,77 @@
{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]}
{
"subject": "acct:gargron@quitter.no",
"aliases": [
"https://quitter.no/user/7477",
"https://quitter.no/gargron"
],
"properties": {
"http://webfinger.example/ns/name": "Bob Smith"
},
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://quitter.no/gargron"
},
{
"rel": "http://gmpg.org/xfn/11",
"type": "text/html",
"href": "https://quitter.no/gargron"
},
{
"rel": "describedby",
"type": "application/rdf+xml",
"href": "https://quitter.no/gargron/foaf"
},
{
"rel": "http://apinamespace.org/atom",
"type": "application/atomsvc+xml",
"href": "https://quitter.no/api/statusnet/app/service/gargron.xml"
},
{
"rel": "http://apinamespace.org/twitter",
"href": "https://quitter.no/api/"
},
{
"rel": "http://specs.openid.net/auth/2.0/provider",
"href": "https://quitter.no/gargron"
},
{
"rel": "http://schemas.google.com/g/2010#updates-from",
"type": "application/atom+xml",
"href": "https://quitter.no/api/statuses/user_timeline/7477.atom"
},
{
"rel": "magic-public-key",
"href": "data:application/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"
},
{
"rel": "salmon",
"href": "https://quitter.no/main/salmon/user/7477"
},
{
"rel": "http://salmon-protocol.org/ns/salmon-replies",
"href": "https://quitter.no/main/salmon/user/7477"
},
{
"rel": "http://salmon-protocol.org/ns/salmon-mention",
"href": "https://quitter.no/main/salmon/user/7477"
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",
"template": "https://quitter.no/main/ostatussub?profile={uri}"
},
{
"rel": "http://spec.example.net/photo/1.0",
"type": "image/jpeg",
"href": "http://photos.example.com/gpburdell.jpg",
"titles": {
"en": "User Photo",
"de": "Benutzerfoto"
},
"properties": {
"http://spec.example.net/created/1.0": "1970-01-01"
}
}
]
}

View File

@ -1,18 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
<Subject>acct:gargron@quitter.no</Subject>
<Alias>https://quitter.no/user/7477</Alias>
<Alias>https://quitter.no/gargron</Alias>
<Link rel="http://webfinger.net/rel/profile-page" type="text/html" href="https://quitter.no/gargron"/>
<Link rel="http://gmpg.org/xfn/11" type="text/html" href="https://quitter.no/gargron"/>
<Link rel="describedby" type="application/rdf+xml" href="https://quitter.no/gargron/foaf"/>
<Link rel="http://apinamespace.org/atom" type="application/atomsvc+xml" href="https://quitter.no/api/statusnet/app/service/gargron.xml"/>
<Link rel="http://apinamespace.org/twitter" href="https://quitter.no/api/"/>
<Link rel="http://specs.openid.net/auth/2.0/provider" href="https://quitter.no/gargron"/>
<Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="https://quitter.no/api/statuses/user_timeline/7477.atom"/>
<Link rel="magic-public-key" href="data:application/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"/>
<Link rel="salmon" href="https://quitter.no/main/salmon/user/7477"/>
<Link rel="http://salmon-protocol.org/ns/salmon-replies" href="https://quitter.no/main/salmon/user/7477"/>
<Link rel="http://salmon-protocol.org/ns/salmon-mention" href="https://quitter.no/main/salmon/user/7477"/>
<Link rel="http://ostatus.org/schema/1.0/subscribe" template="https://quitter.no/main/ostatussub?profile={uri}"/>
<Subject>acct:gargron@quitter.no</Subject>
<Alias>https://quitter.no/user/7477</Alias>
<Alias>https://quitter.no/gargron</Alias>
<Property type="http://webfinger.example/ns/name">Bob Smith</Property>
<Link rel="http://webfinger.net/rel/profile-page" type="text/html" href="https://quitter.no/gargron" />
<Link rel="http://gmpg.org/xfn/11" type="text/html" href="https://quitter.no/gargron" />
<Link rel="describedby" type="application/rdf+xml" href="https://quitter.no/gargron/foaf" />
<Link rel="http://apinamespace.org/atom" type="application/atomsvc+xml" href="https://quitter.no/api/statusnet/app/service/gargron.xml" />
<Link rel="http://apinamespace.org/twitter" href="https://quitter.no/api/" />
<Link rel="http://specs.openid.net/auth/2.0/provider" href="https://quitter.no/gargron" />
<Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="https://quitter.no/api/statuses/user_timeline/7477.atom" />
<Link rel="magic-public-key" href="data:application/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB" />
<Link rel="salmon" href="https://quitter.no/main/salmon/user/7477" />
<Link rel="http://salmon-protocol.org/ns/salmon-replies" href="https://quitter.no/main/salmon/user/7477" />
<Link rel="http://salmon-protocol.org/ns/salmon-mention" href="https://quitter.no/main/salmon/user/7477" />
<Link rel="http://ostatus.org/schema/1.0/subscribe" template="https://quitter.no/main/ostatussub?profile={uri}" />
<Link rel="http://spec.example.net/photo/1.0" type="image/jpeg" href="http://photos.example.com/gpburdell.jpg">
<Title xml:lang="en">User Photo</Title>
<Title xml:lang="de">Benutzerfoto</Title>
<Property type="http://spec.example.net/created/1.0">1970-01-01</Property>
</Link>
</XRD>

View File

@ -2,8 +2,7 @@ describe Goldfinger::Client do
context 'with HTTPS available' do
describe '#finger' do
before do
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_return(body: fixture('quitter.no_.well-known_host-meta'))
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'))
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'), headers: { content_type: 'application/jrd+json' })
end
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
@ -11,21 +10,25 @@ describe Goldfinger::Client do
it 'returns a result' do
expect(subject.finger).to be_instance_of Goldfinger::Result
end
it 'performs a single HTTP request' do
subject.finger
expect(a_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no')).to have_been_made.once
end
end
end
context 'with only HTTP available' do
describe '#finger' do
before do
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
stub_request(:get, 'http://quitter.no/.well-known/host-meta').to_return(body: fixture('quitter.no_.well-known_host-meta'))
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'))
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_raise(HTTP::Error)
stub_request(:get, 'http://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'), headers: { content_type: 'application/jrd+json' })
end
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
it 'returns a result' do
expect(subject.finger).to be_instance_of Goldfinger::Result
it 'raises an error' do
expect { subject.finger }.to raise_error HTTP::Error
end
end
end
@ -33,6 +36,8 @@ describe Goldfinger::Client do
context 'with XRD missing' do
describe '#finger' do
before do
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_raise(HTTP::Error)
stub_request(:get, 'http://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_raise(HTTP::Error)
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
stub_request(:get, 'http://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
end
@ -40,7 +45,7 @@ describe Goldfinger::Client do
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
it 'raises an error' do
expect {subject.finger }.to raise_error(Goldfinger::Error::NotFound)
expect { subject.finger }.to raise_error(HTTP::Error)
end
end
end

View File

@ -6,8 +6,8 @@ describe Goldfinger::Request do
subject { Goldfinger::Request.new(:get, 'http://example.com').perform }
it 'returns the body' do
expect(subject.last.to_s).to eql 'OK'
it 'returns a http response' do
expect(subject).to be_a HTTP::Response
end
end
end

View File

@ -1,9 +1,6 @@
describe Goldfinger::Result do
context 'application/xrd+xml' do
let(:headers) { h = HTTP::Headers.new; h.set(HTTP::Headers::CONTENT_TYPE, 'application/xrd+xml'); h }
let(:body) { File.read(fixture_path('quitter.no_.well-known_webfinger.xml')) }
subject { Goldfinger::Result.new(headers, body) }
shared_examples 'a working finger result' do
subject { Goldfinger::Result.new(response) }
describe '#links' do
it 'returns a non-empty array' do
@ -14,28 +11,70 @@ describe Goldfinger::Result do
describe '#link' do
it 'returns a value for a given rel' do
expect(subject.link('http://webfinger.net/rel/profile-page')[:href]).to eql 'https://quitter.no/gargron'
expect(subject.link('http://webfinger.net/rel/profile-page').href).to eql 'https://quitter.no/gargron'
end
it 'returns nil if no such link exists' do
expect(subject.link('zzzz')).to be_nil
end
it 'returns titles map' do
expect(subject.link('http://spec.example.net/photo/1.0').title('en')).to eql 'User Photo'
end
it 'returns a properties map' do
expect(subject.link('http://spec.example.net/photo/1.0').property('http://spec.example.net/created/1.0')).to eql '1970-01-01'
end
end
describe '#subject' do
it 'returns the subject' do
expect(subject.subject).to eql 'acct:gargron@quitter.no'
end
end
describe '#aliases' do
it 'returns a non-empty array' do
expect(subject.aliases).to be_instance_of Array
expect(subject.aliases).to_not be_empty
end
end
describe '#properties' do
it 'returns an array' do
expect(subject.properties).to be_instance_of Array
expect(subject.properties).to_not be_empty
end
end
describe '#property' do
it 'returns the value for a key' do
expect(subject.property('http://webfinger.example/ns/name')).to eql 'Bob Smith'
end
it 'returns nil if no such property exists' do
expect(subject.property('zzzz')).to be_nil
end
end
end
context 'application/jrd+json' do
let(:headers) { h = HTTP::Headers.new; h.set(HTTP::Headers::CONTENT_TYPE, 'application/jrd+json'); h }
let(:body) { File.read(fixture_path('quitter.no_.well-known_webfinger.json')) }
subject { Goldfinger::Result.new(headers, body) }
describe '#links' do
it 'returns a non-empty array' do
expect(subject.links).to be_instance_of Array
expect(subject.links).to_not be_empty
end
context 'when the input mime type is application/xrd+xml' do
before do
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.xml'), headers: { content_type: 'application/xrd+xml' })
end
describe '#link' do
it 'returns a value for a given rel' do
expect(subject.link('http://webfinger.net/rel/profile-page')[:href]).to eql 'https://quitter.no/gargron'
end
let(:response) { HTTP.get('https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no') }
it_behaves_like 'a working finger result'
end
context 'when the input mime type is application/jrd+json' do
before do
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'), headers: { content_type: 'application/jrd+json' })
end
let(:response) { HTTP.get('https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no') }
it_behaves_like 'a working finger result'
end
end

View File

@ -5,7 +5,6 @@ require 'pry'
WebMock.disable_net_connect!
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
@ -13,7 +12,6 @@ RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
def fixture_path(path)