haveapi-client 0.29.5 → 0.29.6
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/README.md +8 -0
- data/lib/haveapi/client/client.rb +2 -18
- data/lib/haveapi/client/resource_instance.rb +12 -5
- data/lib/haveapi/client/version.rb +1 -1
- data/spec/description_method_security_spec.rb +6 -1
- data/spec/i18n_spec.rb +12 -0
- data/spec/resource_instance_association_spec.rb +166 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2a413b63db6d6f00c9664d93b6a2bb7d6958f7cbe5375206173ad1449b35123
|
|
4
|
+
data.tar.gz: ea5a9393d531ee3503b6ce0274926aae67d9419c8c98d3c85ed2b1fe83d23499
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 947f1336247d07ad353055d7fc5cc68708b4a698927a789b80e91878526dc04b33a9ab2a1ab509e4c8ba2846bcde5cb9710a18b05e61f243c2c82d311b93cc68
|
|
7
|
+
data.tar.gz: 8bf5a0cae801ca319dcb18fd441a3b03d3fad11de8eda64b483a078886f72d9fbbe81187aada825b6abcf4e81e2d788b9777ed96faab76439eaaa28310df18ae
|
data/README.md
CHANGED
|
@@ -120,6 +120,14 @@ client-side validation errors:
|
|
|
120
120
|
api = HaveAPI::Client::Client.new('https://your.api.tld', language: 'cs')
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
Change the request language on an existing client with `set_opts`. Read the
|
|
124
|
+
configured value through `opts`:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
api.set_opts(language: 'en')
|
|
128
|
+
p api.opts[:language]
|
|
129
|
+
```
|
|
130
|
+
|
|
123
131
|
The value is sent in `Accept-Language` by default. Use `language_header` when
|
|
124
132
|
the API is configured with a custom language header. Set the language before
|
|
125
133
|
the client fetches the API description so translated validator descriptions
|
|
@@ -90,24 +90,8 @@ class HaveAPI::Client::Client
|
|
|
90
90
|
# @param opts [Hash] options
|
|
91
91
|
def set_opts(opts)
|
|
92
92
|
@opts.update(opts)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def language
|
|
98
|
-
@api.language
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def language=(value)
|
|
102
|
-
@api.language = value
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def language_header
|
|
106
|
-
@api.language_header
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def language_header=(value)
|
|
110
|
-
@api.language_header = value
|
|
93
|
+
@api.language = opts[:language] if opts.has_key?(:language)
|
|
94
|
+
@api.language_header = opts[:language_header] if opts.has_key?(:language_header)
|
|
111
95
|
end
|
|
112
96
|
|
|
113
97
|
def client_message(key, **values)
|
|
@@ -205,10 +205,17 @@ module HaveAPI::Client
|
|
|
205
205
|
def find_association(res_desc, res_val)
|
|
206
206
|
return nil unless res_val
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
resource = @client
|
|
209
|
+
path = []
|
|
209
210
|
|
|
210
|
-
res_desc[:resource].each do |
|
|
211
|
-
|
|
211
|
+
res_desc[:resource].each do |name|
|
|
212
|
+
path << name
|
|
213
|
+
|
|
214
|
+
begin
|
|
215
|
+
resource = resource.resources.fetch(name.to_sym)
|
|
216
|
+
rescue KeyError
|
|
217
|
+
raise ProtocolError, "associated resource '#{path.join('.')}' not found"
|
|
218
|
+
end
|
|
212
219
|
end
|
|
213
220
|
|
|
214
221
|
# FIXME: read _meta namespace from description
|
|
@@ -217,8 +224,8 @@ module HaveAPI::Client
|
|
|
217
224
|
ResourceInstance.new(
|
|
218
225
|
@client,
|
|
219
226
|
@api,
|
|
220
|
-
|
|
221
|
-
action:
|
|
227
|
+
resource,
|
|
228
|
+
action: resource.actions[:show],
|
|
222
229
|
resolved: meta[:resolved],
|
|
223
230
|
response: meta[:resolved] ? res_val : nil,
|
|
224
231
|
meta: meta
|
|
@@ -80,6 +80,10 @@ RSpec.describe HaveAPI::Client::Client do
|
|
|
80
80
|
actions: {},
|
|
81
81
|
resources: {}
|
|
82
82
|
},
|
|
83
|
+
language: {
|
|
84
|
+
actions: {},
|
|
85
|
+
resources: {}
|
|
86
|
+
},
|
|
83
87
|
users: {
|
|
84
88
|
actions: {},
|
|
85
89
|
resources: {}
|
|
@@ -94,8 +98,9 @@ RSpec.describe HaveAPI::Client::Client do
|
|
|
94
98
|
expect(client.method(:setup).owner).to eq(described_class)
|
|
95
99
|
expect(client.authenticate(:token, token: 'secret-token')).to eq(:authenticated)
|
|
96
100
|
expect(api.authenticate_calls).to eq(2)
|
|
101
|
+
expect(client.language).to be_a(HaveAPI::Client::Resource)
|
|
97
102
|
expect(client.users).to be_a(HaveAPI::Client::Resource)
|
|
98
|
-
expect(client.resources.keys).to contain_exactly(:authenticate, :setup, :users)
|
|
103
|
+
expect(client.resources.keys).to contain_exactly(:authenticate, :setup, :language, :users)
|
|
99
104
|
end
|
|
100
105
|
|
|
101
106
|
it 'does not let actions or nested resources replace resource methods' do
|
data/spec/i18n_spec.rb
CHANGED
|
@@ -24,6 +24,18 @@ RSpec.describe HaveAPI::Client::Client do
|
|
|
24
24
|
expect(client.communicator.language_headers).to eq('X-Language' => 'cs-CZ')
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
it 'updates and reports language options through the option interface' do
|
|
28
|
+
client = described_class.new(TEST_SERVER.base_url)
|
|
29
|
+
|
|
30
|
+
client.set_opts(language: 'cs-CZ', language_header: 'X-Language')
|
|
31
|
+
|
|
32
|
+
expect(client.opts(:language, :language_header)).to eq(
|
|
33
|
+
language: 'cs-CZ',
|
|
34
|
+
language_header: 'X-Language'
|
|
35
|
+
)
|
|
36
|
+
expect(client.communicator.language_headers).to eq('X-Language' => 'cs-CZ')
|
|
37
|
+
end
|
|
38
|
+
|
|
27
39
|
it 'localizes local validation errors' do
|
|
28
40
|
client = described_class.new(TEST_SERVER.base_url, language: 'cs')
|
|
29
41
|
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
class ResourceAssociationTestCommunicator
|
|
6
|
+
attr_reader :calls
|
|
7
|
+
|
|
8
|
+
def initialize(resolved:, association_path: ['language'])
|
|
9
|
+
@resolved = resolved
|
|
10
|
+
@association_path = association_path
|
|
11
|
+
@calls = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def describe_api(_version)
|
|
15
|
+
{
|
|
16
|
+
resources: {
|
|
17
|
+
language: resource_description(
|
|
18
|
+
show: action_description(
|
|
19
|
+
namespace: 'language',
|
|
20
|
+
path: '/v1/languages/{language_id}',
|
|
21
|
+
parameters: {
|
|
22
|
+
id: { type: 'Integer' },
|
|
23
|
+
code: { type: 'String' }
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
),
|
|
27
|
+
user: resource_description(
|
|
28
|
+
current: action_description(
|
|
29
|
+
namespace: 'user',
|
|
30
|
+
path: '/v1/users/current',
|
|
31
|
+
parameters: {
|
|
32
|
+
id: { type: 'Integer' },
|
|
33
|
+
language: {
|
|
34
|
+
type: 'Resource',
|
|
35
|
+
resource: @association_path,
|
|
36
|
+
value_id: 'id',
|
|
37
|
+
value_label: 'code'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def call(action, _params)
|
|
47
|
+
@calls << action.name
|
|
48
|
+
|
|
49
|
+
case action.name
|
|
50
|
+
when :current
|
|
51
|
+
response(
|
|
52
|
+
user: {
|
|
53
|
+
id: 1,
|
|
54
|
+
language: {
|
|
55
|
+
id: 1,
|
|
56
|
+
code: 'cs',
|
|
57
|
+
_meta: {
|
|
58
|
+
resolved: @resolved,
|
|
59
|
+
path_params: [1]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
when :show
|
|
65
|
+
response(
|
|
66
|
+
language: {
|
|
67
|
+
id: 1,
|
|
68
|
+
code: 'cs'
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
else
|
|
72
|
+
raise "unexpected action #{action.name}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def url
|
|
77
|
+
'https://api.example'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
protected
|
|
81
|
+
|
|
82
|
+
def resource_description(actions)
|
|
83
|
+
{
|
|
84
|
+
actions: actions.transform_values { |v| v },
|
|
85
|
+
resources: {}
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def action_description(namespace:, path:, parameters:)
|
|
90
|
+
{
|
|
91
|
+
auth: false,
|
|
92
|
+
description: 'test action',
|
|
93
|
+
aliases: [],
|
|
94
|
+
blocking: false,
|
|
95
|
+
input: {
|
|
96
|
+
parameters: {},
|
|
97
|
+
layout: 'object',
|
|
98
|
+
namespace: namespace
|
|
99
|
+
},
|
|
100
|
+
output: {
|
|
101
|
+
parameters: parameters,
|
|
102
|
+
layout: 'object',
|
|
103
|
+
namespace: namespace
|
|
104
|
+
},
|
|
105
|
+
meta: {
|
|
106
|
+
object: nil,
|
|
107
|
+
global: nil
|
|
108
|
+
},
|
|
109
|
+
examples: [],
|
|
110
|
+
scope: "#{namespace}#show",
|
|
111
|
+
path: path,
|
|
112
|
+
method: 'GET',
|
|
113
|
+
help: "#{path}?method=GET"
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def response(values)
|
|
118
|
+
{
|
|
119
|
+
status: true,
|
|
120
|
+
response: values.merge(_meta: {}),
|
|
121
|
+
message: nil,
|
|
122
|
+
errors: nil
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
RSpec.describe HaveAPI::Client::ResourceInstance do
|
|
128
|
+
def client_for(...)
|
|
129
|
+
communicator = ResourceAssociationTestCommunicator.new(...)
|
|
130
|
+
client = HaveAPI::Client::Client.new(
|
|
131
|
+
'https://api.example',
|
|
132
|
+
communicator: communicator
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
[client, communicator]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'materializes a resolved association through the resource registry' do
|
|
139
|
+
client, communicator = client_for(resolved: true)
|
|
140
|
+
|
|
141
|
+
user = client.user.current
|
|
142
|
+
|
|
143
|
+
expect(user.language_id).to eq(1)
|
|
144
|
+
expect(user.language.code).to eq('cs')
|
|
145
|
+
expect(communicator.calls).to eq([:current])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'resolves a lazy association through the resource registry' do
|
|
149
|
+
client, communicator = client_for(resolved: false)
|
|
150
|
+
|
|
151
|
+
user = client.user.current
|
|
152
|
+
|
|
153
|
+
expect(user.language.code).to eq('cs')
|
|
154
|
+
expect(communicator.calls).to eq(%i[current show])
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'reports an invalid associated resource path as a protocol error' do
|
|
158
|
+
client, = client_for(resolved: true, association_path: ['missing'])
|
|
159
|
+
|
|
160
|
+
expect { client.user.current }
|
|
161
|
+
.to raise_error(
|
|
162
|
+
HaveAPI::Client::ProtocolError,
|
|
163
|
+
"associated resource 'missing' not found"
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haveapi-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.29.
|
|
4
|
+
version: 0.29.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakub Skokan
|
|
@@ -162,6 +162,7 @@ files:
|
|
|
162
162
|
- spec/integration/client_spec.rb
|
|
163
163
|
- spec/integration/typed_input_spec.rb
|
|
164
164
|
- spec/load_spec.rb
|
|
165
|
+
- spec/resource_instance_association_spec.rb
|
|
165
166
|
- spec/spec_helper.rb
|
|
166
167
|
- spec/support/test_server.rb
|
|
167
168
|
- spec/validators/inclusion_spec.rb
|