indieweb-endpoints 0.7.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.reek.yml +2 -0
- data/CHANGELOG.md +6 -2
- data/README.md +13 -6
- data/lib/indieweb/endpoints.rb +9 -11
- data/lib/indieweb/endpoints/client.rb +5 -1
- data/lib/indieweb/endpoints/concerns/registerable.rb +15 -0
- data/lib/indieweb/endpoints/parsers.rb +12 -78
- data/lib/indieweb/endpoints/parsers/redirect_uri_parser.rb +2 -32
- data/lib/indieweb/endpoints/parsers/webmention_parser.rb +5 -14
- data/lib/indieweb/endpoints/services/http_request_service.rb +39 -0
- data/lib/indieweb/endpoints/services/response_body_parser_service.rb +14 -0
- data/lib/indieweb/endpoints/services/response_headers_parser_service.rb +16 -0
- data/lib/indieweb/endpoints/version.rb +1 -1
- metadata +7 -5
- data/lib/indieweb/endpoints/http_request.rb +0 -29
- data/lib/indieweb/endpoints/registerable.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d94a9697335de7e9dbc24727805991f0a6831195a9bc81c44d2b3b63c750c976
|
4
|
+
data.tar.gz: 62b63c314f17152b2934c8fe740dd0a986a400be3694ecceb64f07670a961de4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 286f0df5a086a15e6bbde85cc24a71fc4cbb175f6be98dc15fe52d7b8bc138bcffee098c3572db007b536c27d1c1575e951f371a77a7c46b8b2baf0493454e22
|
7
|
+
data.tar.gz: a4bd418e746511772973b50216962978350b34bf433c525b8cf44e03726e6d30d10939d60879518cf4000c4e1cf5245024606a52f7cf4f58b03a92f863e9058b
|
data/.reek.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.0.0 / 2019-07-08
|
4
|
+
|
5
|
+
- Refactor gem code using service objects approach (78511d7 and 9d2fee0)
|
6
|
+
|
3
7
|
## 0.7.0 / 2019-07-03
|
4
8
|
|
5
9
|
- Update runtime and development dependencies (d99214b and 7692ab3)
|
@@ -25,13 +29,13 @@
|
|
25
29
|
|
26
30
|
## 0.3.0 / 2019-04-30
|
27
31
|
|
28
|
-
- `IndieWeb::Endpoints::Client#endpoints` returns an `OpenStruct` instead of a `Hash (c209b0b).
|
32
|
+
- `IndieWeb::Endpoints::Client#endpoints` returns an `OpenStruct` instead of a `Hash` (c209b0b).
|
29
33
|
|
30
34
|
## 0.2.0 / 2019-04-25
|
31
35
|
|
32
36
|
- Subclass exceptions under `IndieWeb::Endpoints::Error` (667eec7)
|
33
37
|
- Refactor parsers and `Registerable` module (3b96858)
|
34
|
-
- Refactor Client#response method (c36fda3)
|
38
|
+
- Refactor `Client#response` method (c36fda3)
|
35
39
|
|
36
40
|
## 0.1.0 / 2019-04-24
|
37
41
|
|
data/README.md
CHANGED
@@ -47,10 +47,10 @@ require 'indieweb/endpoints'
|
|
47
47
|
|
48
48
|
endpoints = IndieWeb::Endpoints.get('https://aaronparecki.com')
|
49
49
|
|
50
|
-
puts endpoints # => #<OpenStruct
|
50
|
+
puts endpoints # => #<OpenStruct authorization_endpoint="https://aaronparecki.com/auth", micropub="https://aaronparecki.com/micropub", microsub="https://aperture.p3k.io/microsub/1", redirect_uri=nil, token_endpoint="https://aaronparecki.com/auth/token", webmention="https://webmention.io/aaronpk/webmention">
|
51
51
|
```
|
52
52
|
|
53
|
-
This example will search `https://aaronparecki.com` for valid IndieAuth, Micropub, and Webmention endpoints. In this case, the program returns an `OpenStruct` with the following attributes:
|
53
|
+
This example will search `https://aaronparecki.com` for valid IndieAuth, Micropub, and Webmention endpoints. In this case, the program returns an `OpenStruct` with the following attributes (represented below as a `Hash`):
|
54
54
|
|
55
55
|
```ruby
|
56
56
|
{
|
@@ -72,11 +72,18 @@ Should the need arise, you may work with the `IndieWeb::Endpoints::Client` class
|
|
72
72
|
```ruby
|
73
73
|
require 'indieweb/endpoints'
|
74
74
|
|
75
|
-
client = IndieWeb::Endpoints.
|
75
|
+
client = IndieWeb::Endpoints::Client.new('https://aaronparecki.com')
|
76
76
|
|
77
|
-
puts client.response
|
78
|
-
|
79
|
-
|
77
|
+
puts client.response # => #<HTTP::Response/1.1 200 OK {…}>
|
78
|
+
|
79
|
+
endpoints = client.endpoints
|
80
|
+
|
81
|
+
puts endpoints.authorization_endpoint # => 'https://aaronparecki.com/auth'
|
82
|
+
puts endpoints.micropub # => 'https://aaronparecki.com/micropub'
|
83
|
+
puts endpoints.microsub # => 'https://aperture.p3k.io/microsub/1'
|
84
|
+
puts endpoints.redirect_uri # => nil
|
85
|
+
puts endpoints.token_endpoint # => 'https://aaronparecki.com/auth/token'
|
86
|
+
puts endpoints.webmention # => 'https://webmention.io/aaronpk/webmention'
|
80
87
|
```
|
81
88
|
|
82
89
|
### Exception Handling
|
data/lib/indieweb/endpoints.rb
CHANGED
@@ -9,11 +9,15 @@ require 'nokogiri'
|
|
9
9
|
require 'indieweb/endpoints/version'
|
10
10
|
require 'indieweb/endpoints/exceptions'
|
11
11
|
|
12
|
-
require 'indieweb/endpoints/
|
13
|
-
|
14
|
-
require 'indieweb/endpoints/
|
12
|
+
require 'indieweb/endpoints/concerns/registerable'
|
13
|
+
|
14
|
+
require 'indieweb/endpoints/services/http_request_service'
|
15
|
+
require 'indieweb/endpoints/services/response_body_parser_service'
|
16
|
+
require 'indieweb/endpoints/services/response_headers_parser_service'
|
15
17
|
|
18
|
+
require 'indieweb/endpoints/client'
|
16
19
|
require 'indieweb/endpoints/parsers'
|
20
|
+
|
17
21
|
require 'indieweb/endpoints/parsers/authorization_endpoint_parser'
|
18
22
|
require 'indieweb/endpoints/parsers/micropub_parser'
|
19
23
|
require 'indieweb/endpoints/parsers/microsub_parser'
|
@@ -23,14 +27,8 @@ require 'indieweb/endpoints/parsers/webmention_parser'
|
|
23
27
|
|
24
28
|
module IndieWeb
|
25
29
|
module Endpoints
|
26
|
-
|
27
|
-
|
28
|
-
Client.new(url)
|
29
|
-
end
|
30
|
-
|
31
|
-
def get(url)
|
32
|
-
client(url).endpoints
|
33
|
-
end
|
30
|
+
def self.get(url)
|
31
|
+
Client.new(url).endpoints
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
module IndieWeb
|
2
2
|
module Endpoints
|
3
3
|
module Parsers
|
4
|
-
extend Registerable
|
4
|
+
extend Concerns::Registerable
|
5
5
|
|
6
6
|
class BaseParser
|
7
|
-
attr_reader :response
|
8
|
-
|
9
7
|
def initialize(response)
|
10
8
|
raise ArgumentError, "response must be an HTTP::Response (given #{response.class.name})" unless response.is_a?(HTTP::Response)
|
11
9
|
|
@@ -13,93 +11,29 @@ module IndieWeb
|
|
13
11
|
end
|
14
12
|
|
15
13
|
def results
|
16
|
-
|
14
|
+
mapped_results.shift
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :response
|
17
20
|
|
18
|
-
|
21
|
+
def mapped_results
|
22
|
+
@mapped_results ||= results_from_http_request.map { |endpoint| Absolutely.to_abs(base: response.uri.to_s, relative: endpoint) }.uniq.sort
|
19
23
|
rescue Absolutely::InvalidURIError => exception
|
20
24
|
raise InvalidURIError, exception
|
21
25
|
end
|
22
26
|
|
23
|
-
private
|
24
|
-
|
25
27
|
def results_from_body
|
26
|
-
|
28
|
+
@results_from_body ||= Services::ResponseBodyParserService.new.parse(response, self.class.identifier)
|
27
29
|
end
|
28
30
|
|
29
31
|
def results_from_headers
|
30
|
-
|
32
|
+
@results_from_headers ||= Services::ResponseHeadersParserService.new.parse(response, self.class.identifier)
|
31
33
|
end
|
32
34
|
|
33
35
|
def results_from_http_request
|
34
|
-
@results_from_http_request ||= results_from_headers
|
35
|
-
end
|
36
|
-
|
37
|
-
class BaseLinkElementParser
|
38
|
-
attr_reader :identifier, :response
|
39
|
-
|
40
|
-
def initialize(response, identifier)
|
41
|
-
@response = response
|
42
|
-
@identifier = identifier
|
43
|
-
end
|
44
|
-
|
45
|
-
def results
|
46
|
-
link_element['href'] if response_is_html && link_element
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def doc
|
52
|
-
@doc ||= Nokogiri::HTML(response.body.to_s)
|
53
|
-
end
|
54
|
-
|
55
|
-
def link_element
|
56
|
-
# Return first `link` element with valid `rel` attribute
|
57
|
-
# https://www.w3.org/TR/indieauth/#discovery-1
|
58
|
-
# https://www.w3.org/TR/micropub/#endpoint-discovery
|
59
|
-
@link_element ||= link_elements.shift
|
60
|
-
end
|
61
|
-
|
62
|
-
def link_elements
|
63
|
-
@link_elements ||= doc.css(link_elements_css_selector)
|
64
|
-
end
|
65
|
-
|
66
|
-
def link_elements_css_selector
|
67
|
-
@link_elements_css_selector ||= %(link[rel~="#{identifier}"][href]:not([href*="#"]))
|
68
|
-
end
|
69
|
-
|
70
|
-
def response_is_html
|
71
|
-
@response_is_html ||= response.mime_type == 'text/html'
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class BaseLinkHeaderParser
|
76
|
-
attr_reader :identifier, :response
|
77
|
-
|
78
|
-
def initialize(response, identifier)
|
79
|
-
@response = response
|
80
|
-
@identifier = identifier
|
81
|
-
end
|
82
|
-
|
83
|
-
def results
|
84
|
-
return unless link_headers
|
85
|
-
|
86
|
-
link_headers.shift.target_uri
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def link_headers
|
92
|
-
@link_headers ||= begin
|
93
|
-
return unless parsed_link_headers
|
94
|
-
|
95
|
-
# Reject endpoints that contain a fragment identifier
|
96
|
-
parsed_link_headers.reject { |parsed_link_header| Addressable::URI.parse(parsed_link_header.target_uri).fragment }
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def parsed_link_headers
|
101
|
-
@parsed_link_headers ||= LinkHeaderParser.parse(response.headers.get('link'), base: response.uri.to_s).by_relation_type[identifier]
|
102
|
-
end
|
36
|
+
@results_from_http_request ||= [results_from_headers, results_from_body].flatten.compact
|
103
37
|
end
|
104
38
|
end
|
105
39
|
end
|
@@ -9,39 +9,9 @@ module IndieWeb
|
|
9
9
|
Parsers.register(self)
|
10
10
|
|
11
11
|
def results
|
12
|
-
return unless
|
12
|
+
return unless mapped_results.any?
|
13
13
|
|
14
|
-
|
15
|
-
rescue Absolutely::InvalidURIError => exception
|
16
|
-
raise InvalidURIError, exception
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def results_from_body
|
22
|
-
RedirectUriLinkElementParser.new(response, self.class.identifier).results
|
23
|
-
end
|
24
|
-
|
25
|
-
def results_from_headers
|
26
|
-
RedirectUriLinkHeaderParser.new(response, self.class.identifier).results
|
27
|
-
end
|
28
|
-
|
29
|
-
def results_from_http_request
|
30
|
-
@results_from_http_request ||= [results_from_headers, results_from_body].flatten.compact
|
31
|
-
end
|
32
|
-
|
33
|
-
class RedirectUriLinkElementParser < BaseLinkElementParser
|
34
|
-
def results
|
35
|
-
link_elements.map { |element| element['href'] } if response_is_html && link_elements.any?
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class RedirectUriLinkHeaderParser < BaseLinkHeaderParser
|
40
|
-
def results
|
41
|
-
return unless link_headers
|
42
|
-
|
43
|
-
link_headers.map(&:target_uri)
|
44
|
-
end
|
14
|
+
mapped_results
|
45
15
|
end
|
46
16
|
end
|
47
17
|
end
|
@@ -10,22 +10,13 @@ module IndieWeb
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
-
def
|
14
|
-
|
13
|
+
def results_for_node(node)
|
14
|
+
Services::ResponseBodyParserService.new.parse(response, self.class.identifier, node)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
def link_element
|
21
|
-
# Return first `a` or `link` element with valid `rel` attribute
|
22
|
-
# https://www.w3.org/TR/webmention/#sender-discovers-receiver-webmention-endpoint
|
23
|
-
@link_element ||= link_elements.find { |element| %w[a link].include?(element.name) }
|
24
|
-
end
|
25
|
-
|
26
|
-
def link_elements_css_selector
|
27
|
-
@link_elements_css_selector ||= %([rel~="#{identifier}"][href]:not([href*="#"]))
|
28
|
-
end
|
17
|
+
# https://www.w3.org/TR/webmention/#sender-discovers-receiver-webmention-endpoint
|
18
|
+
def results_from_body
|
19
|
+
@results_from_body ||= results_for_node('link') + results_for_node('a')
|
29
20
|
end
|
30
21
|
end
|
31
22
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module IndieWeb
|
2
|
+
module Endpoints
|
3
|
+
module Services
|
4
|
+
class HttpRequestService
|
5
|
+
# Defaults derived from Webmention specification examples
|
6
|
+
# https://www.w3.org/TR/webmention/#limits-on-get-requests
|
7
|
+
HTTP_CLIENT_OPTS = {
|
8
|
+
follow: {
|
9
|
+
max_hops: 20
|
10
|
+
},
|
11
|
+
headers: {
|
12
|
+
accept: '*/*',
|
13
|
+
user_agent: 'IndieWeb Endpoint Discovery (https://rubygems.org/gems/indieweb-endpoints)'
|
14
|
+
},
|
15
|
+
timeout_options: {
|
16
|
+
connect_timeout: 5,
|
17
|
+
read_timeout: 5
|
18
|
+
}
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@client = HTTP::Client.new(HTTP_CLIENT_OPTS)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(uri)
|
26
|
+
client.request(:get, uri)
|
27
|
+
rescue HTTP::ConnectionError,
|
28
|
+
HTTP::TimeoutError,
|
29
|
+
HTTP::Redirector::TooManyRedirectsError => exception
|
30
|
+
raise IndieWeb::Endpoints.const_get(exception.class.name.split('::').last), exception
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_accessor :client
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module IndieWeb
|
2
|
+
module Endpoints
|
3
|
+
module Services
|
4
|
+
class ResponseBodyParserService
|
5
|
+
def parse(response, identifier, node = 'link')
|
6
|
+
return unless response.mime_type == 'text/html'
|
7
|
+
|
8
|
+
# Reject endpoints that contain a fragment identifier
|
9
|
+
Nokogiri::HTML(response.body.to_s).css(%(#{node}[rel~="#{identifier}"][href]:not([href*="#"]))).map { |element| element['href'] }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module IndieWeb
|
2
|
+
module Endpoints
|
3
|
+
module Services
|
4
|
+
class ResponseHeadersParserService
|
5
|
+
def parse(response, identifier)
|
6
|
+
headers = LinkHeaderParser.parse(response.headers.get('link'), base: response.uri.to_s).by_relation_type[identifier]
|
7
|
+
|
8
|
+
return unless headers
|
9
|
+
|
10
|
+
# Reject endpoints that contain a fragment identifier
|
11
|
+
headers.reject { |header| Addressable::URI.parse(header.target_uri).fragment }.map(&:target_uri)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: indieweb-endpoints
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Garber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-07-
|
11
|
+
date: 2019-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -233,8 +233,8 @@ files:
|
|
233
233
|
- indieweb-endpoints.gemspec
|
234
234
|
- lib/indieweb/endpoints.rb
|
235
235
|
- lib/indieweb/endpoints/client.rb
|
236
|
+
- lib/indieweb/endpoints/concerns/registerable.rb
|
236
237
|
- lib/indieweb/endpoints/exceptions.rb
|
237
|
-
- lib/indieweb/endpoints/http_request.rb
|
238
238
|
- lib/indieweb/endpoints/parsers.rb
|
239
239
|
- lib/indieweb/endpoints/parsers/authorization_endpoint_parser.rb
|
240
240
|
- lib/indieweb/endpoints/parsers/micropub_parser.rb
|
@@ -242,14 +242,16 @@ files:
|
|
242
242
|
- lib/indieweb/endpoints/parsers/redirect_uri_parser.rb
|
243
243
|
- lib/indieweb/endpoints/parsers/token_endpoint_parser.rb
|
244
244
|
- lib/indieweb/endpoints/parsers/webmention_parser.rb
|
245
|
-
- lib/indieweb/endpoints/
|
245
|
+
- lib/indieweb/endpoints/services/http_request_service.rb
|
246
|
+
- lib/indieweb/endpoints/services/response_body_parser_service.rb
|
247
|
+
- lib/indieweb/endpoints/services/response_headers_parser_service.rb
|
246
248
|
- lib/indieweb/endpoints/version.rb
|
247
249
|
homepage: https://github.com/indieweb/indieweb-endpoints-ruby
|
248
250
|
licenses:
|
249
251
|
- MIT
|
250
252
|
metadata:
|
251
253
|
bug_tracker_uri: https://github.com/indieweb/indieweb-endpoints-ruby/issues
|
252
|
-
changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/blob/
|
254
|
+
changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/blob/v1.0.0/CHANGELOG.md
|
253
255
|
post_install_message:
|
254
256
|
rdoc_options: []
|
255
257
|
require_paths:
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module IndieWeb
|
2
|
-
module Endpoints
|
3
|
-
class HttpRequest
|
4
|
-
# Defaults derived from Webmention specification examples
|
5
|
-
# https://www.w3.org/TR/webmention/#limits-on-get-requests
|
6
|
-
HTTP_CLIENT_OPTS = {
|
7
|
-
follow: {
|
8
|
-
max_hops: 20
|
9
|
-
},
|
10
|
-
headers: {
|
11
|
-
accept: '*/*',
|
12
|
-
user_agent: 'IndieWeb Endpoint Discovery (https://rubygems.org/gems/indieweb-endpoints)'
|
13
|
-
},
|
14
|
-
timeout_options: {
|
15
|
-
connect_timeout: 5,
|
16
|
-
read_timeout: 5
|
17
|
-
}
|
18
|
-
}.freeze
|
19
|
-
|
20
|
-
def self.get(uri)
|
21
|
-
HTTP::Client.new(HTTP_CLIENT_OPTS).request(:get, uri)
|
22
|
-
rescue HTTP::ConnectionError,
|
23
|
-
HTTP::TimeoutError,
|
24
|
-
HTTP::Redirector::TooManyRedirectsError => exception
|
25
|
-
raise IndieWeb::Endpoints.const_get(exception.class.name.split('::').last), exception
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|