shaf_client 0.6.2 → 1.1.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/faraday_http_cache_patch.rb +17 -0
- data/lib/shaf_client.rb +27 -19
- 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/api_error.rb +2 -1
- data/lib/shaf_client/base_resource.rb +63 -23
- data/lib/shaf_client/content_type_map.rb +53 -0
- data/lib/shaf_client/curie.rb +4 -0
- data/lib/shaf_client/empty_resource.rb +1 -1
- data/lib/shaf_client/hal_form.rb +1 -1
- data/lib/shaf_client/hypertext_cache_strategy.rb +43 -0
- data/lib/shaf_client/link.rb +2 -2
- data/lib/shaf_client/mime_types.rb +8 -0
- data/lib/shaf_client/problem_json.rb +38 -0
- data/lib/shaf_client/resource.rb +74 -10
- 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 +86 -10
- data/lib/shaf_client/shaf_form.rb +3 -2
- data/lib/shaf_client/status_codes.rb +70 -0
- data/lib/shaf_client/test/stubbing.rb +16 -0
- data/lib/shaf_client/unknown_resource.rb +3 -3
- data/lib/shaf_client/version.rb +3 -0
- metadata +58 -29
- 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: 688d7345f3b3c1962b973d323845060cf57b92aeb7b60de4b6f680e566414edf
|
4
|
+
data.tar.gz: 4ccaecadff24f4a78a805f9998af50f93857ac461505c4fc48832e34884123d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8904fef8dc91ed8baa3fa92d434569b4f83a443e1274df372334a27a1a4fb84d3b5a8bfd5956ca85abf08957ec0c28520c424216ec7eaf9edf5719d72d336cde
|
7
|
+
data.tar.gz: 5328f8b5053b81c590b755c25bf8423b9902f7ea0cfe2a03aacf2317f7eccbe49032276e1a6d2abb304823283436eb5e3f6445b2ceb4785d79f8fdc99d51289e
|
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
|
@@ -0,0 +1,17 @@
|
|
1
|
+
faraday_version = Gem.loaded_specs['faraday']&.version.to_s
|
2
|
+
faraday_http_cache_version = Gem.loaded_specs['faraday-http-cache']&.version.to_s
|
3
|
+
|
4
|
+
if faraday_version.start_with?("0.16") && faraday_http_cache_version <= '2.0.0'
|
5
|
+
module Faraday
|
6
|
+
class HttpCache < Faraday::Middleware
|
7
|
+
def create_response(env)
|
8
|
+
hash = env.to_hash
|
9
|
+
{
|
10
|
+
status: hash[:status],
|
11
|
+
body: hash[:response_body] || hash[:body],
|
12
|
+
response_headers: hash[:response_headers]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/shaf_client.rb
CHANGED
@@ -2,20 +2,36 @@
|
|
2
2
|
|
3
3
|
require 'faraday'
|
4
4
|
require 'faraday-http-cache'
|
5
|
+
|
6
|
+
# FIXME remove this when faraday-http-cache has released this fix
|
7
|
+
# https://github.com/plataformatec/faraday-http-cache/pull/116
|
8
|
+
require 'faraday_http_cache_patch'
|
9
|
+
|
5
10
|
require 'json'
|
6
11
|
require 'shaf_client/error'
|
12
|
+
require 'shaf_client/mime_types'
|
7
13
|
require 'shaf_client/middleware/redirect'
|
8
14
|
require 'shaf_client/resource'
|
9
15
|
require 'shaf_client/shaf_form'
|
10
16
|
require 'shaf_client/hal_form'
|
11
17
|
require 'shaf_client/api_error'
|
18
|
+
require 'shaf_client/problem_json'
|
19
|
+
require 'shaf_client/alps_json'
|
12
20
|
require 'shaf_client/empty_resource'
|
13
21
|
require 'shaf_client/unknown_resource'
|
22
|
+
require 'shaf_client/hypertext_cache_strategy'
|
14
23
|
|
15
24
|
class ShafClient
|
16
|
-
|
17
|
-
|
25
|
+
extend HypertextCacheStrategy
|
26
|
+
include MimeTypes
|
27
|
+
|
18
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(', ')
|
19
35
|
|
20
36
|
def initialize(root_uri, **options)
|
21
37
|
@root_uri = root_uri.dup
|
@@ -30,12 +46,7 @@ class ShafClient
|
|
30
46
|
get(@root_uri, **options)
|
31
47
|
end
|
32
48
|
|
33
|
-
|
34
|
-
response = request(method: :get, uri: uri, opts: options)
|
35
|
-
response&.body || ''
|
36
|
-
end
|
37
|
-
|
38
|
-
%i[get put post delete patch].each do |method|
|
49
|
+
%i[head get put post delete patch].each do |method|
|
39
50
|
define_method(method) do |uri, payload: nil, **options|
|
40
51
|
response = request(
|
41
52
|
method: method,
|
@@ -45,17 +56,12 @@ class ShafClient
|
|
45
56
|
)
|
46
57
|
|
47
58
|
body = String(response.body)
|
48
|
-
response.headers['content-type']
|
59
|
+
content_type = response.headers['content-type'] unless body.empty?
|
49
60
|
|
50
|
-
Resource.build(self, body, response.status, response.headers)
|
61
|
+
Resource.build(self, body, content_type, response.status, response.headers)
|
51
62
|
end
|
52
63
|
end
|
53
64
|
|
54
|
-
def stubs
|
55
|
-
return unless @adapter == :test
|
56
|
-
@stubs ||= Faraday::Adapter::Test::Stubs.new
|
57
|
-
end
|
58
|
-
|
59
65
|
private
|
60
66
|
|
61
67
|
attr_reader :options, :auth_header
|
@@ -63,7 +69,7 @@ class ShafClient
|
|
63
69
|
def setup_default_headers
|
64
70
|
@default_headers = {
|
65
71
|
'Content-Type' => options.fetch(:content_type, MIME_TYPE_JSON),
|
66
|
-
'Accept' => options.fetch(:accept,
|
72
|
+
'Accept' => options.fetch(:accept, DEFAULT_ACCEPT_HEADER)
|
67
73
|
}
|
68
74
|
return unless token = options[:auth_token]
|
69
75
|
|
@@ -101,9 +107,11 @@ class ShafClient
|
|
101
107
|
end
|
102
108
|
|
103
109
|
def connect_adapter(connection)
|
104
|
-
|
105
|
-
|
106
|
-
|
110
|
+
connection.adapter(*adapter_args)
|
111
|
+
end
|
112
|
+
|
113
|
+
def adapter_args
|
114
|
+
[@adapter]
|
107
115
|
end
|
108
116
|
|
109
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
|
@@ -10,8 +10,13 @@ class ShafClient
|
|
10
10
|
@payload =
|
11
11
|
if payload&.is_a? String
|
12
12
|
JSON.parse(payload)
|
13
|
+
elsif payload.respond_to? :to_h
|
14
|
+
payload.to_h
|
13
15
|
else
|
14
|
-
|
16
|
+
raise Error, <<~ERR
|
17
|
+
Trying to create an instance of #{self.class} with a payload that
|
18
|
+
cannot be coerced into a Hash
|
19
|
+
ERR
|
15
20
|
end
|
16
21
|
|
17
22
|
parse
|
@@ -19,8 +24,9 @@ class ShafClient
|
|
19
24
|
|
20
25
|
def to_h
|
21
26
|
attributes.dup.tap do |hash|
|
22
|
-
hash[:_links] =
|
23
|
-
|
27
|
+
hash[:_links] = transform_values_to_h(links)
|
28
|
+
hash[:_links].merge!(curies: curies.values.map(&:to_h)) unless curies.empty?
|
29
|
+
embedded = transform_values_to_h(embedded_resources)
|
24
30
|
hash[:_embedded] = embedded unless embedded.empty?
|
25
31
|
end
|
26
32
|
end
|
@@ -33,32 +39,28 @@ class ShafClient
|
|
33
39
|
to_s
|
34
40
|
end
|
35
41
|
|
36
|
-
def attribute(key)
|
37
|
-
raise Error, "No attribute for key: #{key}"
|
38
|
-
|
42
|
+
def attribute(key, &block)
|
43
|
+
block ||= proc { raise Error, "No attribute for key: #{key}" }
|
44
|
+
_attribute(key, &block)
|
39
45
|
end
|
40
46
|
|
41
|
-
def link(rel)
|
42
|
-
|
43
|
-
|
44
|
-
links[rewritten_rel]
|
47
|
+
def link(rel, &block)
|
48
|
+
block ||= proc { raise Error, "No link with rel: #{rel}" }
|
49
|
+
_link(rel, &block)
|
45
50
|
end
|
46
51
|
|
47
|
-
def curie(rel)
|
48
|
-
raise Error, "No curie with rel: #{rel}"
|
49
|
-
|
52
|
+
def curie(rel, &block)
|
53
|
+
block ||= proc { raise Error, "No curie with rel: #{rel}" }
|
54
|
+
_curie(rel, &block)
|
50
55
|
end
|
51
56
|
|
52
|
-
def embedded(rel)
|
53
|
-
|
54
|
-
|
55
|
-
raise Error, "No embedded resources with rel: #{rel}"
|
56
|
-
end
|
57
|
-
embedded_resources[rewritten_rel]
|
57
|
+
def embedded(rel, &block)
|
58
|
+
block ||= proc { raise Error, "No embedded resources with rel: #{rel}" }
|
59
|
+
_embedded(rel, &block)
|
58
60
|
end
|
59
61
|
|
60
62
|
def rel?(rel)
|
61
|
-
!link(rel).nil?
|
63
|
+
!link(rel).nil? || !embedded(rel).nil?
|
62
64
|
rescue StandardError
|
63
65
|
false
|
64
66
|
end
|
@@ -77,6 +79,40 @@ class ShafClient
|
|
77
79
|
@payload ||= {}
|
78
80
|
end
|
79
81
|
|
82
|
+
def _attribute(key, &block)
|
83
|
+
if block
|
84
|
+
attributes.fetch(key.to_sym, &block)
|
85
|
+
else
|
86
|
+
attributes[key.to_sym]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def _link(rel, &block)
|
91
|
+
rewritten_rel = best_match(links.keys, rel)
|
92
|
+
if block
|
93
|
+
links.fetch(rewritten_rel, &block)
|
94
|
+
else
|
95
|
+
links[rewritten_rel]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def _curie(rel, &block)
|
100
|
+
if block
|
101
|
+
curies.fetch(rel.to_sym, &block)
|
102
|
+
else
|
103
|
+
curies[rel.to_sym]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def _embedded(rel, &block)
|
108
|
+
rewritten_rel = best_match(embedded_resources.keys, rel)
|
109
|
+
if block
|
110
|
+
embedded_resources.fetch(rewritten_rel, &block)
|
111
|
+
else
|
112
|
+
embedded_resources[rewritten_rel]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
80
116
|
def <<(other)
|
81
117
|
@payload = other.payload.dup
|
82
118
|
@attributes = other.attributes.dup
|
@@ -118,13 +154,17 @@ class ShafClient
|
|
118
154
|
embedded.each do |key, value|
|
119
155
|
@embedded_resources[key.to_sym] =
|
120
156
|
if value.is_a? Array
|
121
|
-
value.map { |d|
|
157
|
+
value.map { |d| build_embedded_resource(d) }
|
122
158
|
else
|
123
|
-
|
159
|
+
build_embedded_resource(value)
|
124
160
|
end
|
125
161
|
end
|
126
162
|
end
|
127
163
|
|
164
|
+
def build_embedded_resource(payload)
|
165
|
+
BaseResource.new(payload)
|
166
|
+
end
|
167
|
+
|
128
168
|
def method_missing(method_name, *args, &block)
|
129
169
|
return super unless attributes.key?(method_name)
|
130
170
|
attribute(method_name)
|
@@ -148,7 +188,7 @@ class ShafClient
|
|
148
188
|
best_match(rels, rel.to_s.tr('_', '-')) if rel.to_s.include? '_'
|
149
189
|
end
|
150
190
|
|
151
|
-
def
|
191
|
+
def transform_values_to_h(hash)
|
152
192
|
hash.transform_values do |value|
|
153
193
|
if value.is_a? Array
|
154
194
|
value.map(&:to_h)
|