shaf_client 0.6.2 → 1.1.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/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)
|