shaf_client 0.7.1 → 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
- 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
|