shaf_client 0.7.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/bin/shaf_client +30 -0
- data/lib/shaf_client.rb +13 -9
- data/lib/shaf_client/alps/descriptor.rb +60 -0
- data/lib/shaf_client/alps/extension.rb +35 -0
- data/lib/shaf_client/alps_json.rb +44 -0
- data/lib/shaf_client/base_resource.rb +6 -2
- data/lib/shaf_client/content_type_map.rb +53 -0
- data/lib/shaf_client/hal_form.rb +1 -1
- data/lib/shaf_client/hypertext_cache_strategy.rb +0 -4
- data/lib/shaf_client/link.rb +2 -2
- data/lib/shaf_client/mime_types.rb +2 -0
- data/lib/shaf_client/problem_json.rb +1 -1
- data/lib/shaf_client/resource.rb +30 -8
- data/lib/shaf_client/resource_extension.rb +36 -0
- data/lib/shaf_client/resource_extension/alps_http_method.rb +67 -0
- data/lib/shaf_client/resource_extension/base.rb +13 -0
- data/lib/shaf_client/resource_mapper.rb +84 -12
- data/lib/shaf_client/shaf_form.rb +1 -1
- data/lib/shaf_client/test/stubbing.rb +16 -0
- data/lib/shaf_client/unknown_resource.rb +2 -2
- data/lib/shaf_client/version.rb +1 -1
- metadata +47 -24
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11ccc3070c77ca9f04b3a54599437752022c2de7b6bf51f5d3cf7d6bf25bb147
|
4
|
+
data.tar.gz: 73658cc5c27287a677e82a935ced861e8775355ee84df02bc10631e33a13a2e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 322b0537cb301169f77dc76b078c11d74d60b156718b9bdef1cf019d3983fb2c6e3aab01750bd309b6e7e672e0fa7f987b74d2ddd6d0edac21ff03088ddb724e
|
7
|
+
data.tar.gz: 3d8f6b58ed02af7892425fa39eb1bfb9f52845cba81cff2a8e9204e2dd1780ceafb8bf6b13f2b12f77a7ee6a74ecafcca60a631dacf28dd66e06c7d98a3b4726
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/bin/shaf_client
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(__dir__, '../lib/')
|
4
|
+
|
5
|
+
require 'irb'
|
6
|
+
require 'optparse'
|
7
|
+
require 'shaf_client'
|
8
|
+
|
9
|
+
url = ARGV.shift || String(ENV['SHAF_CLIENT_URL'])
|
10
|
+
options = {
|
11
|
+
user: ENV['SHAF_CLIENT_USER'],
|
12
|
+
password: ENV['SHAF_CLIENT_PASSWORD']
|
13
|
+
}
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: shaf_client [url]"
|
17
|
+
|
18
|
+
opts.on("-u", "--user user", "Username used for authentication") do |user|
|
19
|
+
options[:user] = user
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-p", "--password password", "Password used for authentication") do |pass|
|
23
|
+
options[:password] = pass
|
24
|
+
end
|
25
|
+
end.parse! ARGV
|
26
|
+
|
27
|
+
client = ShafClient.new(url, **options) unless url.empty?
|
28
|
+
TOPLEVEL_BINDING.local_variable_set(:client, client)
|
29
|
+
|
30
|
+
IRB.start
|
data/lib/shaf_client.rb
CHANGED
@@ -16,6 +16,7 @@ require 'shaf_client/shaf_form'
|
|
16
16
|
require 'shaf_client/hal_form'
|
17
17
|
require 'shaf_client/api_error'
|
18
18
|
require 'shaf_client/problem_json'
|
19
|
+
require 'shaf_client/alps_json'
|
19
20
|
require 'shaf_client/empty_resource'
|
20
21
|
require 'shaf_client/unknown_resource'
|
21
22
|
require 'shaf_client/hypertext_cache_strategy'
|
@@ -25,6 +26,12 @@ class ShafClient
|
|
25
26
|
include MimeTypes
|
26
27
|
|
27
28
|
DEFAULT_ADAPTER = :net_http
|
29
|
+
DEFAULT_ACCEPT_HEADER = [
|
30
|
+
MIME_TYPE_HAL,
|
31
|
+
MIME_TYPE_PROBLEM_JSON,
|
32
|
+
MIME_TYPE_ALPS_JSON,
|
33
|
+
'*/*;q=0.8',
|
34
|
+
].join(', ')
|
28
35
|
|
29
36
|
def initialize(root_uri, **options)
|
30
37
|
@root_uri = root_uri.dup
|
@@ -55,11 +62,6 @@ class ShafClient
|
|
55
62
|
end
|
56
63
|
end
|
57
64
|
|
58
|
-
def stubs
|
59
|
-
return unless @adapter == :test
|
60
|
-
@stubs ||= Faraday::Adapter::Test::Stubs.new
|
61
|
-
end
|
62
|
-
|
63
65
|
private
|
64
66
|
|
65
67
|
attr_reader :options, :auth_header
|
@@ -67,7 +69,7 @@ class ShafClient
|
|
67
69
|
def setup_default_headers
|
68
70
|
@default_headers = {
|
69
71
|
'Content-Type' => options.fetch(:content_type, MIME_TYPE_JSON),
|
70
|
-
'Accept' => options.fetch(:accept,
|
72
|
+
'Accept' => options.fetch(:accept, DEFAULT_ACCEPT_HEADER)
|
71
73
|
}
|
72
74
|
return unless token = options[:auth_token]
|
73
75
|
|
@@ -105,9 +107,11 @@ class ShafClient
|
|
105
107
|
end
|
106
108
|
|
107
109
|
def connect_adapter(connection)
|
108
|
-
|
109
|
-
|
110
|
-
|
110
|
+
connection.adapter(*adapter_args)
|
111
|
+
end
|
112
|
+
|
113
|
+
def adapter_args
|
114
|
+
[@adapter]
|
111
115
|
end
|
112
116
|
|
113
117
|
def request(method:, uri:, payload: nil, opts: {})
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'shaf_client/alps/extension'
|
2
|
+
|
3
|
+
class ShafClient
|
4
|
+
module Alps
|
5
|
+
class Descriptor
|
6
|
+
attr_reader :id, :href, :name, :type, :doc, :ext
|
7
|
+
|
8
|
+
def initialize(id:, **kwargs)
|
9
|
+
@id = id.to_sym
|
10
|
+
@href = kwargs[:href]
|
11
|
+
@name = kwargs[:name]
|
12
|
+
@type = kwargs[:type]
|
13
|
+
@doc = kwargs[:doc]
|
14
|
+
@ext = parse_extentions(kwargs[:ext])
|
15
|
+
end
|
16
|
+
|
17
|
+
alias extensions ext
|
18
|
+
|
19
|
+
def to_h
|
20
|
+
{
|
21
|
+
id: id,
|
22
|
+
href: href,
|
23
|
+
name: name,
|
24
|
+
type: type,
|
25
|
+
doc: doc,
|
26
|
+
ext: extensions.map(&:to_h),
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def semantic?
|
31
|
+
type == 'semantic'
|
32
|
+
end
|
33
|
+
|
34
|
+
def safe?
|
35
|
+
type == 'safe'
|
36
|
+
end
|
37
|
+
|
38
|
+
def idempotent?
|
39
|
+
type == 'idempotent'
|
40
|
+
end
|
41
|
+
|
42
|
+
def unsafe?
|
43
|
+
type == 'unsafe'
|
44
|
+
end
|
45
|
+
|
46
|
+
def extension(id)
|
47
|
+
extensions.find { |ext| ext.id == id.to_sym }
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def parse_extentions(extensions)
|
53
|
+
extensions ||= []
|
54
|
+
extensions = [extensions] unless extensions.is_a? Array
|
55
|
+
extensions.map { |ext| Extension.new(**ext.transform_keys(&:to_sym)) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class ShafClient
|
2
|
+
module Alps
|
3
|
+
class Extension
|
4
|
+
attr_reader :id, :href, :value
|
5
|
+
|
6
|
+
def initialize(id:, href: nil, value: nil)
|
7
|
+
@id = id.to_sym
|
8
|
+
@href = href
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
{
|
14
|
+
id: id,
|
15
|
+
href: href,
|
16
|
+
value: value,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def method_missing(method_name, *args, &block)
|
23
|
+
name = method_name.to_s
|
24
|
+
return super unless name.end_with? '?'
|
25
|
+
|
26
|
+
id.to_s == name[0..-2]
|
27
|
+
end
|
28
|
+
|
29
|
+
def respond_to_missing?(method_name, include_private = false)
|
30
|
+
return true if method_name.to_s.end_with? '?'
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'shaf_client/alps/descriptor'
|
2
|
+
|
3
|
+
class ShafClient
|
4
|
+
class AlpsJson < Resource
|
5
|
+
content_type MIME_TYPE_ALPS_JSON
|
6
|
+
|
7
|
+
attr_reader :descriptors
|
8
|
+
|
9
|
+
def initialize(_client, payload, status = nil, headers = {})
|
10
|
+
super
|
11
|
+
|
12
|
+
@links = {}.freeze
|
13
|
+
@curies = {}.freeze
|
14
|
+
@embedded_resources = {}.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
attributes.merge(
|
19
|
+
descriptors: descriptors.map(&:to_h)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def descriptor(id)
|
24
|
+
descriptors.find { |desc| desc.id == id.to_sym }
|
25
|
+
end
|
26
|
+
|
27
|
+
def each_descriptor(&block)
|
28
|
+
descriptors.each(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse
|
34
|
+
alps = payload&.dig('alps') || {}
|
35
|
+
@attributes = {
|
36
|
+
version: alps['version'],
|
37
|
+
doc: alps['doc'],
|
38
|
+
}
|
39
|
+
@descriptors = alps.fetch('descriptor', []).map do |desc|
|
40
|
+
Alps::Descriptor.new(**desc.transform_keys(&:to_sym))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -154,13 +154,17 @@ class ShafClient
|
|
154
154
|
embedded.each do |key, value|
|
155
155
|
@embedded_resources[key.to_sym] =
|
156
156
|
if value.is_a? Array
|
157
|
-
value.map { |d|
|
157
|
+
value.map { |d| build_embedded_resource(d) }
|
158
158
|
else
|
159
|
-
|
159
|
+
build_embedded_resource(value)
|
160
160
|
end
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
+
def build_embedded_resource(payload)
|
165
|
+
BaseResource.new(payload)
|
166
|
+
end
|
167
|
+
|
164
168
|
def method_missing(method_name, *args, &block)
|
165
169
|
return super unless attributes.key?(method_name)
|
166
170
|
attribute(method_name)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class ShafClient
|
4
|
+
class ContentTypeMap
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@map, :default, :default=, :keys, :values, :each
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@map = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](content_type, profile = nil)
|
14
|
+
key = key_for(content_type, profile)
|
15
|
+
map.fetch(key) do
|
16
|
+
key = key_for(content_type, nil)
|
17
|
+
map[key]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(content_type, profile = nil, value)
|
22
|
+
key = key_for(content_type, profile)
|
23
|
+
map[key] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def key?(content_type, profile = nil)
|
27
|
+
key = key_for(content_type, profile)
|
28
|
+
map.key? key
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(content_type, profile = nil)
|
32
|
+
key = key_for(content_type, profile)
|
33
|
+
map.delete(key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def key_for(content_type, profile)
|
37
|
+
return unless content_type
|
38
|
+
|
39
|
+
key = content_type.to_s.downcase
|
40
|
+
key = strip_parameters(key)
|
41
|
+
key << "_#{profile.to_s.downcase}" if profile
|
42
|
+
key
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :map
|
48
|
+
|
49
|
+
def strip_parameters(content_type)
|
50
|
+
content_type&.sub(/;.*/, '')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/shaf_client/hal_form.rb
CHANGED
data/lib/shaf_client/link.rb
CHANGED
@@ -27,8 +27,8 @@ class ShafClient
|
|
27
27
|
return href unless templated?
|
28
28
|
|
29
29
|
href
|
30
|
-
.
|
31
|
-
.
|
30
|
+
.yield_self { |href| resolve_required(href, **args) }
|
31
|
+
.yield_self { |href| resolve_optional(href, **args) }
|
32
32
|
end
|
33
33
|
|
34
34
|
def to_h
|
data/lib/shaf_client/resource.rb
CHANGED
@@ -10,17 +10,28 @@ class ShafClient
|
|
10
10
|
|
11
11
|
ResourceMapper.register(MIME_TYPE_HAL, self)
|
12
12
|
|
13
|
-
def self.content_type(type)
|
14
|
-
ResourceMapper.register(type, self)
|
13
|
+
def self.content_type(type, profile: nil)
|
14
|
+
ResourceMapper.register(type, profile, self)
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.profile(name)
|
18
|
-
content_type
|
18
|
+
content_type MIME_TYPE_HAL, profile: name
|
19
19
|
end
|
20
20
|
|
21
21
|
def self.build(client, payload, content_type = MIME_TYPE_HAL, status = nil, headers = {})
|
22
|
-
ResourceMapper.for(
|
23
|
-
|
22
|
+
resource_class, extensions = ResourceMapper.for(
|
23
|
+
content_type: content_type,
|
24
|
+
headers: headers,
|
25
|
+
payload: payload,
|
26
|
+
client: client,
|
27
|
+
)
|
28
|
+
resrc = resource_class.new(client, payload, status, headers)
|
29
|
+
extensions.compact.each { |extension| resrc.extend extension }
|
30
|
+
resrc
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.default_resource_class!
|
34
|
+
ResourceMapper.default = self
|
24
35
|
end
|
25
36
|
|
26
37
|
def initialize(client, payload, status = nil, headers = {})
|
@@ -63,7 +74,7 @@ class ShafClient
|
|
63
74
|
|
64
75
|
curie = curie(curie_name)
|
65
76
|
uri = curie.resolve_templated(rel: rel)
|
66
|
-
client.get(uri, options)
|
77
|
+
client.get(uri, **options)
|
67
78
|
end
|
68
79
|
|
69
80
|
def get_hal_form(rel)
|
@@ -96,6 +107,7 @@ class ShafClient
|
|
96
107
|
protected
|
97
108
|
|
98
109
|
def <<(other)
|
110
|
+
@client = other.client
|
99
111
|
@http_status = other.http_status.dup
|
100
112
|
@headers = other.headers.dup
|
101
113
|
super
|
@@ -105,6 +117,16 @@ class ShafClient
|
|
105
117
|
|
106
118
|
attr_reader :client
|
107
119
|
|
120
|
+
def build_embedded_resource(payload)
|
121
|
+
self.class.build(
|
122
|
+
client,
|
123
|
+
payload,
|
124
|
+
headers['content-type'],
|
125
|
+
http_status,
|
126
|
+
headers
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
108
130
|
def hypertext_cache_strategy(options)
|
109
131
|
options.fetch(:hypertext_cache_strategy) do
|
110
132
|
ShafClient.default_hypertext_cache_strategy
|
@@ -122,13 +144,13 @@ class ShafClient
|
|
122
144
|
return unless HypertextCacheStrategy.cacheable? cache_strategy
|
123
145
|
|
124
146
|
if HypertextCacheStrategy.fetch_headers? cache_strategy
|
125
|
-
resource = client.head(href, options)
|
147
|
+
resource = client.head(href, **options)
|
126
148
|
status = resource.http_status
|
127
149
|
headers = resource.headers
|
128
150
|
embedded_resource = embedded_resource.payload
|
129
151
|
else
|
130
152
|
status = HypertextCacheStrategy.default_http_status
|
131
|
-
headers =
|
153
|
+
headers = embedded_resource.headers
|
132
154
|
end
|
133
155
|
|
134
156
|
self.class.build(client, embedded_resource, headers['content-type'], status, headers)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
class ShafClient
|
4
|
+
module ResourceExtension
|
5
|
+
class << self
|
6
|
+
def register(extender)
|
7
|
+
extenders << extender
|
8
|
+
end
|
9
|
+
|
10
|
+
def unregister(extender)
|
11
|
+
extenders.delete(extender)
|
12
|
+
end
|
13
|
+
|
14
|
+
def for(profile, base, link_relations, client)
|
15
|
+
link_relations = remove_curies(link_relations)
|
16
|
+
extenders.map { |extender| extender.call(profile, base, link_relations, client) }
|
17
|
+
.compact
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def extenders
|
23
|
+
@extenders ||= Set.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_curies(link_relations)
|
27
|
+
Array(link_relations).map do |rel|
|
28
|
+
rel.to_s.sub(/[^:]*:/, '').to_sym
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'shaf_client/resource_extension/base'
|
36
|
+
require 'shaf_client/resource_extension/alps_http_method'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class ShafClient
|
2
|
+
module ResourceExtension
|
3
|
+
class AlpsHttpMethod < Base
|
4
|
+
class << self
|
5
|
+
def call(profile, base, link_relations, _client)
|
6
|
+
return unless profile.is_a? AlpsJson
|
7
|
+
return unless base <= Resource
|
8
|
+
|
9
|
+
link_relations = Array(link_relations).compact
|
10
|
+
descriptors = descriptors_with_http_method(profile)
|
11
|
+
descriptors.keep_if do |descriptor|
|
12
|
+
link_relations.include? identifier_for(descriptor)&.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
extension_for(descriptors)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def descriptors_with_http_method(profile)
|
21
|
+
profile.each_descriptor.each_with_object([]) do |descriptor, descriptors|
|
22
|
+
next unless descriptor.extension(:http_method)
|
23
|
+
descriptors << descriptor
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def extension_for(descriptors)
|
28
|
+
return if descriptors.empty?
|
29
|
+
|
30
|
+
Module.new.tap do |mod|
|
31
|
+
descriptors.each do |descriptor|
|
32
|
+
add_method(mod, descriptor, methods.first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_method(mod, descriptor, method)
|
38
|
+
rel = identifier_for(descriptor)
|
39
|
+
return unless rel
|
40
|
+
|
41
|
+
ext = descriptor.extension(:http_method)
|
42
|
+
methods = Array(ext&.value)
|
43
|
+
|
44
|
+
# We only know what method to use when size is 1
|
45
|
+
return unless methods.size == 1
|
46
|
+
|
47
|
+
http_method = methods.first.downcase.to_sym
|
48
|
+
name = method_name_from(rel)
|
49
|
+
|
50
|
+
mod.define_method(name) do |payload: nil, **options|
|
51
|
+
href = link(rel).href
|
52
|
+
client.send(http_method, href, payload: payload, **options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_name_from(rel)
|
57
|
+
"#{rel.to_s.downcase.tr('-', '_')}!"
|
58
|
+
end
|
59
|
+
|
60
|
+
def identifier_for(descriptor)
|
61
|
+
# Currently we only support `id` (i.e no support for descriptors with `href`)
|
62
|
+
descriptor.id
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,22 +1,26 @@
|
|
1
1
|
require 'shaf_client/error'
|
2
|
+
require 'shaf_client/content_type_map'
|
3
|
+
require 'shaf_client/resource_extension'
|
2
4
|
|
3
5
|
class ShafClient
|
4
|
-
|
6
|
+
module ResourceMapper
|
5
7
|
class << self
|
6
|
-
def for(content_type)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def for(content_type:, headers: {}, payload: nil, client: nil)
|
9
|
+
content_type = content_type&.to_sym
|
10
|
+
profile = profile_from(content_type, headers, payload)
|
11
|
+
clazz, extensions = result_for(content_type, payload, profile, client)
|
12
|
+
|
13
|
+
raise_unsupported_error(content_type) unless clazz
|
14
|
+
|
15
|
+
[clazz, extensions]
|
12
16
|
end
|
13
17
|
|
14
|
-
def register(content_type, clazz)
|
15
|
-
mapping[content_type&.to_sym] = clazz
|
18
|
+
def register(content_type, profile = nil, clazz)
|
19
|
+
mapping[content_type&.to_sym, profile] = clazz
|
16
20
|
end
|
17
21
|
|
18
|
-
def unregister(content_type)
|
19
|
-
mapping.delete(content_type.to_sym)
|
22
|
+
def unregister(content_type, profile = nil)
|
23
|
+
mapping.delete(content_type.to_sym, profile)
|
20
24
|
end
|
21
25
|
|
22
26
|
def default=(clazz)
|
@@ -26,7 +30,75 @@ class ShafClient
|
|
26
30
|
private
|
27
31
|
|
28
32
|
def mapping
|
29
|
-
@mapping ||=
|
33
|
+
@mapping ||= ContentTypeMap.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def result_for(content_type, payload, profile, client)
|
37
|
+
clazz = nil
|
38
|
+
extensions = []
|
39
|
+
|
40
|
+
# Note: mapping typically has a default value, so we need to check that the key really exist
|
41
|
+
if mapping.key? content_type, profile
|
42
|
+
# Registered classes with profile takes precedence over linked profiles
|
43
|
+
clazz = mapping[content_type, profile]
|
44
|
+
else
|
45
|
+
clazz = mapping[content_type]
|
46
|
+
extensions = extensions_for(clazz, profile, payload, client) if profile
|
47
|
+
end
|
48
|
+
|
49
|
+
[clazz, extensions]
|
50
|
+
end
|
51
|
+
|
52
|
+
def profile_from(content_type, headers, payload)
|
53
|
+
profile_from_content_type(content_type) ||
|
54
|
+
profile_from_link_header(headers) ||
|
55
|
+
profile_from_payload_link(content_type, payload)
|
56
|
+
rescue StandardError => err
|
57
|
+
warn "Exception while looking up profile link relation: #{err}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def profile_from_content_type(content_type)
|
61
|
+
return unless content_type
|
62
|
+
|
63
|
+
content_type[/profile="?([^"\s;]*)/, 1]
|
64
|
+
end
|
65
|
+
|
66
|
+
def profile_from_link_header(headers)
|
67
|
+
links = String(headers["link"] || headers["Link"]).split(',')
|
68
|
+
profile_link = links.find { |link| link.match?(/rel="?profile"?/) }
|
69
|
+
profile_link[/<(.*)>/, 1] if profile_link
|
70
|
+
end
|
71
|
+
|
72
|
+
def profile_from_payload_link(content_type, payload)
|
73
|
+
clazz = mapping[content_type]
|
74
|
+
resource = clazz&.new(nil, payload)
|
75
|
+
return unless resource.respond_to? :link
|
76
|
+
|
77
|
+
link = resource.link(:profile) { nil }
|
78
|
+
link&.href
|
79
|
+
end
|
80
|
+
|
81
|
+
def extensions_for(clazz, profile, payload, client)
|
82
|
+
return [] unless clazz && profile && client
|
83
|
+
|
84
|
+
profile_resource = fetch_profile(profile, client)
|
85
|
+
link_relations = clazz.new(nil, payload).actions if payload
|
86
|
+
|
87
|
+
ResourceExtension.for(profile_resource, clazz, link_relations, client)
|
88
|
+
rescue StandardError => err
|
89
|
+
warn "Exception while resolving extensions for profile " \
|
90
|
+
"#{profile_resource&.name || profile}: #{err}"
|
91
|
+
[]
|
92
|
+
end
|
93
|
+
|
94
|
+
def fetch_profile(profile, client)
|
95
|
+
return unless profile&.start_with? %r{https?://}
|
96
|
+
|
97
|
+
client.get(profile)
|
98
|
+
end
|
99
|
+
|
100
|
+
def raise_unsupported_error(content_type)
|
101
|
+
raise UnSupportedContentType, "Can't handle Content-Type: #{content_type}"
|
30
102
|
end
|
31
103
|
end
|
32
104
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class ShafClient
|
2
|
+
module Test
|
3
|
+
module Stubbing
|
4
|
+
def stubs
|
5
|
+
return unless @adapter == :test
|
6
|
+
@stubs ||= Faraday::Adapter::Test::Stubs.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def adapter_args
|
10
|
+
args = super
|
11
|
+
args << stubs if @adapter == :test
|
12
|
+
args
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -2,9 +2,9 @@ require 'shaf_client/resource'
|
|
2
2
|
|
3
3
|
class ShafClient
|
4
4
|
class UnknownResource < Resource
|
5
|
-
|
5
|
+
default_resource_class!
|
6
6
|
|
7
|
-
|
7
|
+
attr_reader :http_status, :headers, :body
|
8
8
|
|
9
9
|
def initialize(_client, payload, status = nil, headers = {})
|
10
10
|
@body = payload.freeze
|
data/lib/shaf_client/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shaf_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sammy Henningsson
|
@@ -10,27 +10,26 @@ bindir: bin
|
|
10
10
|
cert_chain:
|
11
11
|
- |
|
12
12
|
-----BEGIN CERTIFICATE-----
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
M40=
|
13
|
+
MIIDVjCCAj6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAqMSgwJgYDVQQDDB9zYW1t
|
14
|
+
eS5oZW5uaW5nc3Nvbi9EQz1oZXkvREM9Y29tMB4XDTIxMDIwMjEzNTY1OFoXDTIy
|
15
|
+
MDIwMjEzNTY1OFowKjEoMCYGA1UEAwwfc2FtbXkuaGVubmluZ3Nzb24vREM9aGV5
|
16
|
+
L0RDPWNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+SDC1mfyhu
|
17
|
+
cJ6Va21rIHUGscEtQrdvyBqxFG1s2TgPMAv4RbqwdJVPa7kjtbCzslADlUE1oru2
|
18
|
+
C+rcJsMtVGX02ukMIPHT1OjTyy0/EMqLqSy3WeRI8APyDSxCVbe+h5BMf3zZnYfd
|
19
|
+
dR6AeG7ln09T1P/tX+9lTMc+I+DW1fUlQgY48CNUayvtJR61svXvXMrhLhi29SQi
|
20
|
+
g1qmH6Zoe22/JgH+m2JksPndY5Ep3gqfDc6Imwu2vGvmGErJD63FB0XQ/wb4WVH4
|
21
|
+
l7sHQSTfKDp8SImCt1xqNgIyjw578ZG2geGLoncuxgDrbQ/UFIJ11lDZd4vLevMh
|
22
|
+
nIxTSJpPr2cCAwEAAaOBhjCBgzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNV
|
23
|
+
HQ4EFgQUukjj1Cd2ea6IOHDLZe0ymzs2jWkwJAYDVR0RBB0wG4EZc2FtbXkuaGVu
|
24
|
+
bmluZ3Nzb25AaGV5LmNvbTAkBgNVHRIEHTAbgRlzYW1teS5oZW5uaW5nc3NvbkBo
|
25
|
+
ZXkuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBt310FZ56E/fp/y5Wym1xb4C0owfnI
|
26
|
+
LzUqOjXWZTO7RezlBJV/qIwqt6bEjYDQR56zULJFSp4YdoDsratmQ+/kwtbHF7xf
|
27
|
+
WYkSE36mhLP2ggQFH9fXtX6XdyIlwFqwEfNW73ZrkBXGjMxLVKIS9uHdN7PoNhbS
|
28
|
+
0YjOZZD/rq1Jf+klwl/G7bBDPjn58DWlUmwzoj49goGS/UBG37RssQxRwPelKHZh
|
29
|
+
5ZlcHq9h5CxVt380OKaU6wMg95RJBd/kUJqmPxxlxMH8QDQinTwZmmFA9wW7PJdy
|
30
|
+
wAx8px9LkSjTs0GVLH7VtVRWAELllsswCJktz63Adelx9fmIMgrTYgZM
|
32
31
|
-----END CERTIFICATE-----
|
33
|
-
date:
|
32
|
+
date: 2021-03-12 00:00:00.000000000 Z
|
34
33
|
dependencies:
|
35
34
|
- !ruby/object:Gem::Dependency
|
36
35
|
name: faraday
|
@@ -83,7 +82,7 @@ dependencies:
|
|
83
82
|
version: '5'
|
84
83
|
- - ">="
|
85
84
|
- !ruby/object:Gem::Version
|
86
|
-
version:
|
85
|
+
version: 5.14.3
|
87
86
|
type: :development
|
88
87
|
prerelease: false
|
89
88
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -93,17 +92,37 @@ dependencies:
|
|
93
92
|
version: '5'
|
94
93
|
- - ">="
|
95
94
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
95
|
+
version: 5.14.3
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: minitest-hooks
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1'
|
97
110
|
description: A HAL client customized for Shaf APIs
|
98
111
|
email: sammy.henningsson@gmail.com
|
99
|
-
executables:
|
112
|
+
executables:
|
113
|
+
- shaf_client
|
100
114
|
extensions: []
|
101
115
|
extra_rdoc_files: []
|
102
116
|
files:
|
117
|
+
- bin/shaf_client
|
103
118
|
- lib/faraday_http_cache_patch.rb
|
104
119
|
- lib/shaf_client.rb
|
120
|
+
- lib/shaf_client/alps/descriptor.rb
|
121
|
+
- lib/shaf_client/alps/extension.rb
|
122
|
+
- lib/shaf_client/alps_json.rb
|
105
123
|
- lib/shaf_client/api_error.rb
|
106
124
|
- lib/shaf_client/base_resource.rb
|
125
|
+
- lib/shaf_client/content_type_map.rb
|
107
126
|
- lib/shaf_client/curie.rb
|
108
127
|
- lib/shaf_client/empty_resource.rb
|
109
128
|
- lib/shaf_client/error.rb
|
@@ -116,9 +135,13 @@ files:
|
|
116
135
|
- lib/shaf_client/mime_types.rb
|
117
136
|
- lib/shaf_client/problem_json.rb
|
118
137
|
- lib/shaf_client/resource.rb
|
138
|
+
- lib/shaf_client/resource_extension.rb
|
139
|
+
- lib/shaf_client/resource_extension/alps_http_method.rb
|
140
|
+
- lib/shaf_client/resource_extension/base.rb
|
119
141
|
- lib/shaf_client/resource_mapper.rb
|
120
142
|
- lib/shaf_client/shaf_form.rb
|
121
143
|
- lib/shaf_client/status_codes.rb
|
144
|
+
- lib/shaf_client/test/stubbing.rb
|
122
145
|
- lib/shaf_client/unknown_resource.rb
|
123
146
|
- lib/shaf_client/version.rb
|
124
147
|
homepage: https://github.com/sammyhenningsson/shaf_client
|
metadata.gz.sig
CHANGED
Binary file
|