haveapi 0.29.1 → 0.29.3
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 +21 -0
- data/haveapi.gemspec +1 -1
- data/lib/haveapi/authentication/basic/provider.rb +1 -2
- data/lib/haveapi/authentication/chain.rb +1 -1
- data/lib/haveapi/authentication/oauth2/provider.rb +1 -17
- data/lib/haveapi/authentication/token/provider.rb +9 -1
- data/lib/haveapi/locales/cs.yml +100 -63
- data/lib/haveapi/locales/en.yml +103 -67
- data/lib/haveapi/parameters/metadata_i18n.rb +80 -3
- data/lib/haveapi/parameters/typed.rb +5 -1
- data/lib/haveapi/resources/action_state.rb +6 -4
- data/lib/haveapi/version.rb +1 -1
- data/spec/action/runtime_spec.rb +1 -1
- data/spec/action/validation_http_status_spec.rb +2 -2
- data/spec/action_state_spec.rb +4 -4
- data/spec/authentication/basic_spec.rb +3 -3
- data/spec/authentication/oauth2_spec.rb +3 -1
- data/spec/authentication/token_spec.rb +8 -0
- data/spec/extensions/exception_mailer_spec.rb +2 -2
- data/spec/i18n_spec.rb +88 -9
- data/spec/model_adapters/active_record_spec.rb +8 -8
- data/spec/params_spec.rb +18 -0
- data/spec/server/integration_spec.rb +5 -5
- metadata +3 -3
data/spec/i18n_spec.rb
CHANGED
|
@@ -10,6 +10,16 @@ module I18nSpec
|
|
|
10
10
|
User.new(:cs) if username == 'user' && password == 'pass'
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
module ActionStateBackend
|
|
15
|
+
def self.list_pending(*)
|
|
16
|
+
[]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.new(*)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
13
23
|
end
|
|
14
24
|
|
|
15
25
|
describe HaveAPI::I18n do
|
|
@@ -48,6 +58,14 @@ describe HaveAPI::I18n do
|
|
|
48
58
|
string :global_attr_label,
|
|
49
59
|
label: 'Global attribute fallback',
|
|
50
60
|
desc: 'Global attribute description fallback'
|
|
61
|
+
string :status, choices: %i[enabled disabled]
|
|
62
|
+
string :priority,
|
|
63
|
+
choices: {
|
|
64
|
+
values: {
|
|
65
|
+
low: 'Low priority',
|
|
66
|
+
high: 'High priority'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
51
69
|
string :code, length: {
|
|
52
70
|
min: 3,
|
|
53
71
|
message: HaveAPI.message('haveapi.validators.length.min', min: 3)
|
|
@@ -105,8 +123,8 @@ describe HaveAPI::I18n do
|
|
|
105
123
|
header 'Accept', 'application/json'
|
|
106
124
|
call_api(:post, '/v1/things', thing: {})
|
|
107
125
|
|
|
108
|
-
expect(api_response.message).to eq('input parameters not valid')
|
|
109
|
-
expect(api_response.errors[:name]).to include('required parameter missing')
|
|
126
|
+
expect(api_response.message).to eq('input parameters are not valid')
|
|
127
|
+
expect(api_response.errors[:name]).to include('required parameter is missing')
|
|
110
128
|
expect(last_response.headers['Vary']).to include('Accept-Language')
|
|
111
129
|
end
|
|
112
130
|
|
|
@@ -115,9 +133,9 @@ describe HaveAPI::I18n do
|
|
|
115
133
|
header 'Accept-Language', 'cs'
|
|
116
134
|
call_api(:post, '/v1/things', thing: { count: 'nope' })
|
|
117
135
|
|
|
118
|
-
expect(api_response.message).to eq('vstupní parametry
|
|
119
|
-
expect(api_response.errors[:name]).to include('povinný parametr
|
|
120
|
-
expect(api_response.errors[:count].first).to include('
|
|
136
|
+
expect(api_response.message).to eq('vstupní parametry jsou neplatné')
|
|
137
|
+
expect(api_response.errors[:name]).to include('chybí povinný parametr')
|
|
138
|
+
expect(api_response.errors[:count].first).to include('není platné celé číslo')
|
|
121
139
|
expect(last_response.headers['Vary']).to include('Accept-Language')
|
|
122
140
|
end
|
|
123
141
|
|
|
@@ -143,7 +161,15 @@ describe HaveAPI::I18n do
|
|
|
143
161
|
options '/v1/things', method: 'POST'
|
|
144
162
|
|
|
145
163
|
length = api_response[:input][:parameters][:name][:validators][:length]
|
|
146
|
-
expect(length[:message]).to eq('délka musí být v rozsahu
|
|
164
|
+
expect(length[:message]).to eq('délka musí být v rozsahu 3–5')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'keeps untranslated array choices unchanged in OPTIONS responses' do
|
|
168
|
+
header 'Accept', 'application/json'
|
|
169
|
+
options '/v1/things', method: 'POST'
|
|
170
|
+
|
|
171
|
+
choices = api_response[:input][:parameters][:status][:validators][:include]
|
|
172
|
+
expect(choices[:values]).to eq(%w[enabled disabled])
|
|
147
173
|
end
|
|
148
174
|
|
|
149
175
|
it 'localizes API metadata in OPTIONS responses' do
|
|
@@ -218,6 +244,18 @@ describe HaveAPI::I18n do
|
|
|
218
244
|
global_attr_label: {
|
|
219
245
|
label: 'Přesný globální popisek',
|
|
220
246
|
description: 'Přesný globální popis'
|
|
247
|
+
},
|
|
248
|
+
status: {
|
|
249
|
+
choices: {
|
|
250
|
+
enabled: { label: 'Zapnuto' },
|
|
251
|
+
disabled: { label: 'Vypnuto' }
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
priority: {
|
|
255
|
+
choices: {
|
|
256
|
+
low: { label: 'Nízká priorita' },
|
|
257
|
+
high: { label: 'Vysoká priorita' }
|
|
258
|
+
}
|
|
221
259
|
}
|
|
222
260
|
},
|
|
223
261
|
output: {
|
|
@@ -325,6 +363,14 @@ describe HaveAPI::I18n do
|
|
|
325
363
|
label: 'Přesný globální popisek',
|
|
326
364
|
description: 'Přesný globální popis'
|
|
327
365
|
)
|
|
366
|
+
expect(input_params[:status][:validators][:include][:values]).to eq(
|
|
367
|
+
enabled: 'Zapnuto',
|
|
368
|
+
disabled: 'Vypnuto'
|
|
369
|
+
)
|
|
370
|
+
expect(input_params[:priority][:validators][:include][:values]).to eq(
|
|
371
|
+
low: 'Nízká priorita',
|
|
372
|
+
high: 'Vysoká priorita'
|
|
373
|
+
)
|
|
328
374
|
expect(input_params[:count]).to include(
|
|
329
375
|
label: 'Count',
|
|
330
376
|
description: nil
|
|
@@ -357,7 +403,7 @@ describe HaveAPI::I18n do
|
|
|
357
403
|
create = api_response[:resources][:thing][:actions][:create]
|
|
358
404
|
no_meta = create[:meta][:global][:input][:parameters][:no]
|
|
359
405
|
|
|
360
|
-
expect(no_meta[:label]).to eq('
|
|
406
|
+
expect(no_meta[:label]).to eq('Vypnout metadata')
|
|
361
407
|
end
|
|
362
408
|
|
|
363
409
|
it 'localizes application-supplied lazy validator messages' do
|
|
@@ -423,7 +469,7 @@ describe HaveAPI::I18n do
|
|
|
423
469
|
expect(HaveAPI.localize(data)).to eq({
|
|
424
470
|
message: 'Akce nebyla nalezena',
|
|
425
471
|
errors: {
|
|
426
|
-
name: ['povinný parametr
|
|
472
|
+
name: ['chybí povinný parametr']
|
|
427
473
|
}
|
|
428
474
|
})
|
|
429
475
|
ensure
|
|
@@ -456,6 +502,39 @@ describe HaveAPI::I18n do
|
|
|
456
502
|
end
|
|
457
503
|
end
|
|
458
504
|
|
|
505
|
+
context 'with localized framework self-description' do
|
|
506
|
+
empty_api
|
|
507
|
+
use_version 1
|
|
508
|
+
default_version 1
|
|
509
|
+
auth_chain I18nSpec::Provider
|
|
510
|
+
action_state I18nSpec::ActionStateBackend
|
|
511
|
+
|
|
512
|
+
before do
|
|
513
|
+
header 'Accept', 'application/json'
|
|
514
|
+
header 'Accept-Language', 'cs'
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
it 'localizes authentication provider descriptions' do
|
|
518
|
+
call_api(:options, '/?describe=default')
|
|
519
|
+
|
|
520
|
+
description = api_response[:authentication][:basic][:description]
|
|
521
|
+
expect(description).to start_with('Autentizace pomocí HTTP Basic')
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
it 'localizes ActionState resource and action descriptions' do
|
|
525
|
+
call_api(:options, '/?describe=default')
|
|
526
|
+
|
|
527
|
+
action_state = api_response[:resources][:action_state]
|
|
528
|
+
expect(action_state[:description]).to eq('Procházet stavy blokujících akcí')
|
|
529
|
+
expect(action_state[:actions][:index][:description]).to eq('Vypsat stavy čekajících akcí')
|
|
530
|
+
expect(action_state[:actions][:poll][:description]).to eq(
|
|
531
|
+
'Vrátit stav po dokončení akce, změně průběhu nebo vypršení časového limitu'
|
|
532
|
+
)
|
|
533
|
+
expect(action_state[:actions][:show][:description]).to eq('Zobrazit stav čekající akce')
|
|
534
|
+
expect(action_state[:actions][:cancel][:description]).to eq('Zrušit čekající akci')
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
459
538
|
context 'with an authenticated locale resolver' do
|
|
460
539
|
api do
|
|
461
540
|
define_resource(:Thing) do
|
|
@@ -489,7 +568,7 @@ describe HaveAPI::I18n do
|
|
|
489
568
|
|
|
490
569
|
action = api_response[:resources][:thing][:actions][:create]
|
|
491
570
|
length = action.dig(:input, :parameters, :name, :validators, :length)
|
|
492
|
-
expect(length[:message]).to eq('délka musí být v rozsahu
|
|
571
|
+
expect(length[:message]).to eq('délka musí být v rozsahu 3–5')
|
|
493
572
|
end
|
|
494
573
|
end
|
|
495
574
|
|
|
@@ -869,23 +869,23 @@ describe HaveAPI::ModelAdapters::ActiveRecord do
|
|
|
869
869
|
|
|
870
870
|
expect do
|
|
871
871
|
described_class::Input.clean(ARAdapterSpec::Environment, 'abc', {})
|
|
872
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
872
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
873
873
|
|
|
874
874
|
expect do
|
|
875
875
|
described_class::Input.clean(ARAdapterSpec::Environment, '', {})
|
|
876
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
876
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
877
877
|
|
|
878
878
|
expect do
|
|
879
879
|
described_class::Input.clean(ARAdapterSpec::Environment, 1.2, {})
|
|
880
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
880
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
881
881
|
|
|
882
882
|
expect do
|
|
883
883
|
described_class::Input.clean(ARAdapterSpec::Environment, false, {})
|
|
884
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
884
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
885
885
|
|
|
886
886
|
expect do
|
|
887
887
|
described_class::Input.clean(ARAdapterSpec::Environment, true, {})
|
|
888
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
888
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
889
889
|
|
|
890
890
|
expect do
|
|
891
891
|
described_class::Input.clean(ARAdapterSpec::Environment, 9999, {})
|
|
@@ -913,11 +913,11 @@ describe HaveAPI::ModelAdapters::ActiveRecord do
|
|
|
913
913
|
|
|
914
914
|
expect do
|
|
915
915
|
described_class::Input.clean(ARAdapterSpec::StringAccount, %w[acct-alpha acct-beta], {})
|
|
916
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
916
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
917
917
|
|
|
918
918
|
expect do
|
|
919
919
|
described_class::Input.clean(ARAdapterSpec::StringAccount, { id: 'acct-alpha' }, {})
|
|
920
|
-
end.to raise_error(HaveAPI::ValidationError, /not a valid
|
|
920
|
+
end.to raise_error(HaveAPI::ValidationError, /not a valid ID/)
|
|
921
921
|
end
|
|
922
922
|
|
|
923
923
|
it 'rejects non-string includes metadata entries' do
|
|
@@ -957,7 +957,7 @@ describe HaveAPI::ModelAdapters::ActiveRecord do
|
|
|
957
957
|
expect(last_response.status).to eq(200)
|
|
958
958
|
expect(api_response).not_to be_ok
|
|
959
959
|
expect(api_response.errors[:limit].first).to include(
|
|
960
|
-
"
|
|
960
|
+
"between 0 and #{HaveAPI::Actions::Paginable::MAX_LIMIT}"
|
|
961
961
|
)
|
|
962
962
|
end
|
|
963
963
|
|
data/spec/params_spec.rb
CHANGED
|
@@ -258,6 +258,8 @@ describe HaveAPI::Params do
|
|
|
258
258
|
p = described_class.new(:input, ParamsSpec::MyResource::Index)
|
|
259
259
|
p.add_block(proc do
|
|
260
260
|
string :hostname, label: 'Hostname', desc: 'VPS hostname'
|
|
261
|
+
string :state, choices: %i[queued running]
|
|
262
|
+
string :priority, choices: { values: { low: 'Low priority' } }
|
|
261
263
|
string :framework,
|
|
262
264
|
label: HaveAPI.message('haveapi.parameters.metadata.no.label')
|
|
263
265
|
end)
|
|
@@ -274,6 +276,22 @@ describe HaveAPI::Params do
|
|
|
274
276
|
params_spec.attributes.hostname.label
|
|
275
277
|
])
|
|
276
278
|
expect(items.none? { |item| item[:param] == 'framework' }).to be true
|
|
279
|
+
|
|
280
|
+
choice = items.detect do |item|
|
|
281
|
+
item[:param] == 'state' && item[:kind] == 'choices.queued.label'
|
|
282
|
+
end
|
|
283
|
+
expect(choice[:value]).to eq('queued')
|
|
284
|
+
expect(choice[:keys]).to eq(%w[
|
|
285
|
+
params_spec.resources.my_resource.actions.index.input.state.choices.queued.label
|
|
286
|
+
params_spec.resources.my_resource.input.state.choices.queued.label
|
|
287
|
+
params_spec.resources.my_resource.attributes.state.choices.queued.label
|
|
288
|
+
params_spec.attributes.state.choices.queued.label
|
|
289
|
+
])
|
|
290
|
+
|
|
291
|
+
choice = items.detect do |item|
|
|
292
|
+
item[:param] == 'priority' && item[:kind] == 'choices.low.label'
|
|
293
|
+
end
|
|
294
|
+
expect(choice[:value]).to eq('Low priority')
|
|
277
295
|
end
|
|
278
296
|
|
|
279
297
|
it 'does not parse meta resource path segments as metadata paths' do
|
|
@@ -97,7 +97,7 @@ describe HaveAPI::Server do
|
|
|
97
97
|
|
|
98
98
|
expect(last_response.status).to eq(400)
|
|
99
99
|
expect(api_response).not_to be_ok
|
|
100
|
-
expect(api_response.message).to match(/
|
|
100
|
+
expect(api_response.message).to match(/Invalid JSON syntax/)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
it 'returns 400 for non-object JSON bodies' do
|
|
@@ -109,7 +109,7 @@ describe HaveAPI::Server do
|
|
|
109
109
|
|
|
110
110
|
expect(last_response.status).to eq(400)
|
|
111
111
|
expect(api_response).not_to be_ok
|
|
112
|
-
expect(api_response.message).to eq('JSON body must be an object')
|
|
112
|
+
expect(api_response.message).to eq('The JSON request body must be an object')
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
@@ -132,7 +132,7 @@ describe HaveAPI::Server do
|
|
|
132
132
|
|
|
133
133
|
expect(last_response.status).to eq(415)
|
|
134
134
|
expect(api_response).not_to be_ok
|
|
135
|
-
expect(api_response.message).to eq('Unsupported Content-Type')
|
|
135
|
+
expect(api_response.message).to eq('Unsupported Content-Type header')
|
|
136
136
|
expect(ServerIntegrationSpec::State.writes).to be_empty
|
|
137
137
|
end
|
|
138
138
|
|
|
@@ -145,7 +145,7 @@ describe HaveAPI::Server do
|
|
|
145
145
|
|
|
146
146
|
expect(last_response.status).to eq(400)
|
|
147
147
|
expect(api_response).not_to be_ok
|
|
148
|
-
expect(api_response.message).to eq('
|
|
148
|
+
expect(api_response.message).to eq('Invalid Accept header')
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
it 'returns JSON envelope for unknown route' do
|
|
@@ -169,7 +169,7 @@ describe HaveAPI::Server do
|
|
|
169
169
|
|
|
170
170
|
expect(last_response.status).to eq(500)
|
|
171
171
|
expect(api_response).not_to be_ok
|
|
172
|
-
expect(api_response.message).to eq('
|
|
172
|
+
expect(api_response.message).to eq('A server error occurred')
|
|
173
173
|
|
|
174
174
|
expect(calls.size).to eq(1)
|
|
175
175
|
context, exception = calls.first
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haveapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.29.
|
|
4
|
+
version: 0.29.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakub Skokan
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.29.
|
|
32
|
+
version: 0.29.3
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.29.
|
|
39
|
+
version: 0.29.3
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: i18n
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|