haveapi 0.26.5 → 0.27.1

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/Gemfile +7 -3
  4. data/haveapi.gemspec +1 -1
  5. data/lib/haveapi/action.rb +1 -1
  6. data/lib/haveapi/authentication/base.rb +2 -0
  7. data/lib/haveapi/authentication/oauth2/provider.rb +10 -2
  8. data/lib/haveapi/authentication/token/provider.rb +25 -1
  9. data/lib/haveapi/model_adapters/active_record.rb +66 -4
  10. data/lib/haveapi/parameters/resource.rb +8 -1
  11. data/lib/haveapi/parameters/typed.rb +94 -10
  12. data/lib/haveapi/params.rb +6 -1
  13. data/lib/haveapi/resource.rb +1 -1
  14. data/lib/haveapi/server.rb +10 -1
  15. data/lib/haveapi/spec/api_builder.rb +8 -3
  16. data/lib/haveapi/spec/spec_methods.rb +20 -10
  17. data/lib/haveapi/version.rb +1 -1
  18. data/spec/action/authorize_spec.rb +317 -0
  19. data/spec/action/dsl_spec.rb +98 -100
  20. data/spec/action/runtime_spec.rb +207 -0
  21. data/spec/action_state_spec.rb +301 -0
  22. data/spec/authentication/basic_spec.rb +108 -0
  23. data/spec/authentication/oauth2_spec.rb +127 -0
  24. data/spec/authentication/token_spec.rb +233 -0
  25. data/spec/authorization_spec.rb +23 -18
  26. data/spec/common_spec.rb +19 -17
  27. data/spec/documentation/auth_filtering_spec.rb +111 -0
  28. data/spec/documentation_spec.rb +165 -2
  29. data/spec/envelope_spec.rb +5 -9
  30. data/spec/extensions/action_exceptions_spec.rb +163 -0
  31. data/spec/hooks_spec.rb +32 -38
  32. data/spec/model_adapters/active_record_spec.rb +413 -0
  33. data/spec/parameters/typed_spec.rb +54 -1
  34. data/spec/params_spec.rb +27 -25
  35. data/spec/resource_spec.rb +36 -22
  36. data/spec/server/integration_spec.rb +71 -0
  37. data/spec/spec_helper.rb +2 -2
  38. data/spec/validators/acceptance_spec.rb +10 -12
  39. data/spec/validators/confirmation_spec.rb +14 -16
  40. data/spec/validators/custom_spec.rb +1 -1
  41. data/spec/validators/exclusion_spec.rb +13 -15
  42. data/spec/validators/format_spec.rb +20 -22
  43. data/spec/validators/inclusion_spec.rb +13 -15
  44. data/spec/validators/length_spec.rb +6 -6
  45. data/spec/validators/numericality_spec.rb +10 -10
  46. data/spec/validators/presence_spec.rb +16 -22
  47. data/test_support/client_test_api.rb +607 -0
  48. data/test_support/client_test_server.rb +59 -0
  49. metadata +16 -3
@@ -0,0 +1,413 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'active_record'
5
+ require 'sqlite3'
6
+ require_relative '../../lib/haveapi/model_adapters/active_record'
7
+
8
+ module ARAdapterSpec
9
+ class Environment < ActiveRecord::Base
10
+ has_many :groups, class_name: 'ARAdapterSpec::Group'
11
+ end
12
+
13
+ class Group < ActiveRecord::Base
14
+ belongs_to :environment, class_name: 'ARAdapterSpec::Environment', optional: true
15
+ has_many :users, class_name: 'ARAdapterSpec::User'
16
+ end
17
+
18
+ class User < ActiveRecord::Base
19
+ belongs_to :group, class_name: 'ARAdapterSpec::Group', optional: true
20
+
21
+ validates :name, length: { minimum: 3, maximum: 20 }
22
+ validates :email, format: { with: /\A.+@.+\z/ }
23
+ validates :role, inclusion: { in: %w[user admin] }
24
+ validates :state, exclusion: { in: %w[banned] }
25
+ validates :age, numericality: {
26
+ greater_than_or_equal_to: 18,
27
+ less_than_or_equal_to: 100
28
+ }
29
+ validates :score, numericality: { equal_to: 7 }
30
+ validates :name, presence: true
31
+ end
32
+ end
33
+
34
+ describe HaveAPI::ModelAdapters::ActiveRecord do
35
+ api do
36
+ env_resource = define_resource(:Environment) do
37
+ version 1
38
+ auth false
39
+ model ARAdapterSpec::Environment
40
+
41
+ define_action(:Index, superclass: HaveAPI::Actions::Default::Index) do
42
+ authorize { allow }
43
+
44
+ output(:object_list) do
45
+ integer :id
46
+ string :label
47
+ end
48
+
49
+ def exec
50
+ self.class.model.order(id: :asc).to_a
51
+ end
52
+ end
53
+
54
+ define_action(:Show, superclass: HaveAPI::Actions::Default::Show) do
55
+ authorize { allow }
56
+
57
+ output(:object) do
58
+ integer :id
59
+ string :label
60
+ string :note
61
+ end
62
+
63
+ def exec
64
+ self.class.model.find(params['environment_id'])
65
+ end
66
+ end
67
+ end
68
+
69
+ group_resource = define_resource(:Group) do
70
+ version 1
71
+ auth false
72
+ model ARAdapterSpec::Group
73
+
74
+ define_action(:Index, superclass: HaveAPI::Actions::Default::Index) do
75
+ authorize { allow }
76
+
77
+ output(:object_list) do
78
+ integer :id
79
+ string :label
80
+ end
81
+
82
+ def exec
83
+ self.class.model.order(id: :asc).to_a
84
+ end
85
+ end
86
+
87
+ define_action(:Show, superclass: HaveAPI::Actions::Default::Show) do
88
+ authorize { allow }
89
+
90
+ output(:object) do
91
+ integer :id
92
+ string :label
93
+ string :note
94
+ resource env_resource
95
+ end
96
+
97
+ def exec
98
+ self.class.model.find(params['group_id'])
99
+ end
100
+ end
101
+ end
102
+
103
+ define_resource(:User) do
104
+ version 1
105
+ auth false
106
+ model ARAdapterSpec::User
107
+
108
+ define_action(:Index, superclass: HaveAPI::Actions::Default::Index) do
109
+ authorize { allow }
110
+
111
+ output(:object_list) do
112
+ integer :id
113
+ string :name
114
+ end
115
+
116
+ def exec
117
+ with_pagination(self.class.model.order(id: :asc)).to_a
118
+ end
119
+ end
120
+
121
+ define_action(:IndexDesc, superclass: HaveAPI::Actions::Default::Index) do
122
+ route 'desc'
123
+ authorize { allow }
124
+
125
+ output(:object_list) do
126
+ integer :id
127
+ string :name
128
+ end
129
+
130
+ def exec
131
+ with_desc_pagination(self.class.model.order(id: :desc)).to_a
132
+ end
133
+ end
134
+
135
+ define_action(:Show, superclass: HaveAPI::Actions::Default::Show) do
136
+ authorize { allow }
137
+
138
+ output(:object) do
139
+ integer :id
140
+ string :name
141
+ resource group_resource
142
+ end
143
+
144
+ def exec
145
+ id = params['user_id'].to_i
146
+ with_includes(self.class.model.where(id: id)).take!
147
+ end
148
+ end
149
+
150
+ define_action(:Create, superclass: HaveAPI::Actions::Default::Create) do
151
+ authorize { allow }
152
+
153
+ input do
154
+ string :name
155
+ string :email
156
+ string :role
157
+ string :state
158
+ integer :age
159
+ integer :score
160
+ end
161
+
162
+ def exec
163
+ {}
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ default_version 1
170
+
171
+ before(:all) do
172
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
173
+ ActiveRecord::Schema.verbose = false
174
+
175
+ ActiveRecord::Schema.define do
176
+ create_table :environments do |t|
177
+ t.string :label, null: false
178
+ t.string :note
179
+ end
180
+
181
+ create_table :groups do |t|
182
+ t.string :label, null: false
183
+ t.string :note
184
+ t.integer :environment_id
185
+ end
186
+
187
+ create_table :users do |t|
188
+ t.string :name
189
+ t.string :email
190
+ t.integer :age
191
+ t.integer :score
192
+ t.string :role
193
+ t.string :state
194
+ t.integer :group_id
195
+ end
196
+ end
197
+ end
198
+
199
+ before do
200
+ ARAdapterSpec::User.delete_all
201
+ ARAdapterSpec::Group.delete_all
202
+ ARAdapterSpec::Environment.delete_all
203
+ end
204
+
205
+ let(:dummy_action) do
206
+ Class.new do
207
+ include HaveAPI::ModelAdapters::ActiveRecord::Action::InstanceMethods
208
+
209
+ def self.model
210
+ ARAdapterSpec::User
211
+ end
212
+
213
+ def input
214
+ {}
215
+ end
216
+ end.new
217
+ end
218
+
219
+ def create_user(attrs = {})
220
+ defaults = {
221
+ name: 'user',
222
+ email: 'user@example.com',
223
+ role: 'user',
224
+ state: 'active',
225
+ age: 20,
226
+ score: 7
227
+ }
228
+
229
+ ARAdapterSpec::User.create!(defaults.merge(attrs))
230
+ end
231
+
232
+ def action_class(resource, action)
233
+ klass, = find_action(1, resource, action)
234
+ klass
235
+ end
236
+
237
+ def fetch_validator(validators, klass, short_name)
238
+ validators[klass] || validators[short_name.to_sym] || validators[short_name.to_s]
239
+ end
240
+
241
+ it 'translates ActiveModel validators to HaveAPI validators' do
242
+ app
243
+ create_action = action_class(:User, :create)
244
+
245
+ name_validators = create_action.input[:name].describe(nil)[:validators]
246
+ name_length = fetch_validator(name_validators, 'HaveAPI::Validators::Length', :length)
247
+ expect(name_length).to be_a(Hash)
248
+ expect(name_length[:min]).to eq(3)
249
+ expect(name_length[:max]).to eq(20)
250
+ expect(fetch_validator(name_validators, 'HaveAPI::Validators::Presence', :present)).to be_nil
251
+
252
+ email_validators = create_action.input[:email].describe(nil)[:validators]
253
+ expect(fetch_validator(email_validators, 'HaveAPI::Validators::Format', :format)).to be_a(Hash)
254
+
255
+ role_validators = create_action.input[:role].describe(nil)[:validators]
256
+ role_inclusion = fetch_validator(role_validators, 'HaveAPI::Validators::Inclusion', :include)
257
+ expect(role_inclusion).to be_a(Hash)
258
+ expect(role_inclusion[:values]).to include('user', 'admin')
259
+
260
+ state_validators = create_action.input[:state].describe(nil)[:validators]
261
+ state_exclusion = fetch_validator(state_validators, 'HaveAPI::Validators::Exclusion', :exclude)
262
+ expect(state_exclusion).to be_a(Hash)
263
+ expect(state_exclusion[:values]).to include('banned')
264
+
265
+ age_validators = create_action.input[:age].describe(nil)[:validators]
266
+ age_number = fetch_validator(age_validators, 'HaveAPI::Validators::Numericality', :number)
267
+ expect(age_number).to be_a(Hash)
268
+ expect(age_number[:min]).to eq(18)
269
+ expect(age_number[:max]).to eq(100)
270
+
271
+ score_validators = create_action.input[:score].describe(nil)[:validators]
272
+ score_acceptance = fetch_validator(score_validators, 'HaveAPI::Validators::Acceptance', :accept)
273
+ expect(score_acceptance).to be_a(Hash)
274
+ expect(score_acceptance[:value]).to eq(7)
275
+ end
276
+
277
+ it 'parses nested includes and drops unknown associations' do
278
+ parsed = dummy_action.ar_parse_includes(%w[group group__environment foo bar__baz])
279
+
280
+ expect(parsed).to include(:group)
281
+ expect(parsed.any? { |v| v.is_a?(Hash) && v.has_key?(:group) }).to be(true)
282
+
283
+ nested = parsed.detect { |v| v.is_a?(Hash) && v.has_key?(:group) }
284
+ expect(nested[:group].flatten).to include(:environment)
285
+
286
+ expect(parsed).not_to include(:foo)
287
+ expect(parsed).not_to include(:bar)
288
+ end
289
+
290
+ it 'returns unresolved associations without includes' do
291
+ environment = ARAdapterSpec::Environment.create!(label: 'env', note: 'ENV_NOTE')
292
+ group = ARAdapterSpec::Group.create!(label: 'grp', note: 'GRP_NOTE', environment: environment)
293
+ user = create_user(name: 'user', group: group)
294
+
295
+ get "/v1/users/#{user.id}", {}, input: ''
296
+
297
+ expect(api_response).to be_ok
298
+ ret = api_response[:user]
299
+ group_data = ret[:group]
300
+
301
+ expect(group_data[:_meta][:resolved]).to be(false)
302
+ expect(group_data).to include(id: group.id, label: group.label)
303
+ expect(group_data).not_to have_key(:note)
304
+ end
305
+
306
+ it 'resolves direct associations when included' do
307
+ environment = ARAdapterSpec::Environment.create!(label: 'env', note: 'ENV_NOTE')
308
+ group = ARAdapterSpec::Group.create!(label: 'grp', note: 'GRP_NOTE', environment: environment)
309
+ user = create_user(name: 'user', group: group)
310
+
311
+ get "/v1/users/#{user.id}", { _meta: { includes: 'group' } }, input: ''
312
+
313
+ expect(api_response).to be_ok
314
+ ret = api_response[:user]
315
+ group_data = ret[:group]
316
+
317
+ expect(group_data[:_meta][:resolved]).to be(true)
318
+ expect(group_data[:note]).to eq('GRP_NOTE')
319
+ expect(group_data[:environment][:_meta][:resolved]).to be(false)
320
+ expect(group_data[:environment]).not_to have_key(:note)
321
+ end
322
+
323
+ it 'resolves nested associations when included' do
324
+ environment = ARAdapterSpec::Environment.create!(label: 'env', note: 'ENV_NOTE')
325
+ group = ARAdapterSpec::Group.create!(label: 'grp', note: 'GRP_NOTE', environment: environment)
326
+ user = create_user(name: 'user', group: group)
327
+
328
+ get "/v1/users/#{user.id}", { _meta: { includes: 'group__environment' } }, input: ''
329
+
330
+ expect(api_response).to be_ok
331
+ ret = api_response[:user]
332
+ group_data = ret[:group]
333
+ env_data = group_data[:environment]
334
+
335
+ expect(group_data[:_meta][:resolved]).to be(true)
336
+ expect(env_data[:_meta][:resolved]).to be(true)
337
+ expect(env_data[:note]).to eq('ENV_NOTE')
338
+ end
339
+
340
+ it 'cleans resource input ids and maps invalid values to validation errors' do
341
+ environment = ARAdapterSpec::Environment.create!(id: 1, label: 'env')
342
+
343
+ expect(described_class::Input.clean(ARAdapterSpec::Environment, 1, {})).to eq(environment)
344
+ expect(described_class::Input.clean(ARAdapterSpec::Environment, '1', {})).to eq(environment)
345
+ expect(described_class::Input.clean(ARAdapterSpec::Environment, 1.0, {})).to eq(environment)
346
+ expect(described_class::Input.clean(ARAdapterSpec::Environment, '', { optional: true })).to be_nil
347
+ expect(described_class::Input.clean(ARAdapterSpec::Environment, ' ', { optional: true })).to be_nil
348
+
349
+ expect do
350
+ described_class::Input.clean(ARAdapterSpec::Environment, 'abc', {})
351
+ end.to raise_error(HaveAPI::ValidationError, /not a valid id/)
352
+
353
+ expect do
354
+ described_class::Input.clean(ARAdapterSpec::Environment, '', {})
355
+ end.to raise_error(HaveAPI::ValidationError, /not a valid id/)
356
+
357
+ expect do
358
+ described_class::Input.clean(ARAdapterSpec::Environment, 1.2, {})
359
+ end.to raise_error(HaveAPI::ValidationError, /not a valid id/)
360
+
361
+ expect do
362
+ described_class::Input.clean(ARAdapterSpec::Environment, false, {})
363
+ end.to raise_error(HaveAPI::ValidationError, /not a valid id/)
364
+
365
+ expect do
366
+ described_class::Input.clean(ARAdapterSpec::Environment, true, {})
367
+ end.to raise_error(HaveAPI::ValidationError, /not a valid id/)
368
+
369
+ expect do
370
+ described_class::Input.clean(ARAdapterSpec::Environment, 9999, {})
371
+ end.to raise_error(HaveAPI::ValidationError, /resource not found/)
372
+ end
373
+
374
+ it 'applies ascending pagination with with_pagination' do
375
+ 5.times do |i|
376
+ create_user(
377
+ id: i + 1,
378
+ name: "user#{i + 1}",
379
+ email: "user#{i + 1}@example.com",
380
+ age: 20 + i
381
+ )
382
+ end
383
+
384
+ get '/v1/users', { user: { from_id: 2, limit: 2 } }, input: ''
385
+
386
+ expect(api_response).to be_ok
387
+ ids = api_response[:users].map { |u| u[:id] }
388
+ expect(ids).to eq([3, 4])
389
+ end
390
+
391
+ it 'applies descending pagination with with_desc_pagination' do
392
+ 5.times do |i|
393
+ create_user(
394
+ id: i + 1,
395
+ name: "user#{i + 1}",
396
+ email: "user#{i + 1}@example.com",
397
+ age: 20 + i
398
+ )
399
+ end
400
+
401
+ get '/v1/users/desc', { user: { from_id: 4, limit: 2 } }, input: ''
402
+
403
+ expect(api_response).to be_ok
404
+ ids = api_response[:users].map { |u| u[:id] }
405
+ expect(ids).to eq([3, 2])
406
+ end
407
+
408
+ it 'raises when paginating a composite primary key model' do
409
+ allow(ARAdapterSpec::User).to receive(:primary_key).and_return(%w[a b])
410
+
411
+ expect { dummy_action.with_asc_pagination }.to raise_error(RuntimeError, /composite primary key/)
412
+ end
413
+ end
@@ -16,7 +16,7 @@ describe 'Parameters::Typed' do
16
16
  required: true
17
17
  }
18
18
  p_arg(kwargs)
19
- expect(kwargs.keys).to contain_exactly(*%i[label desc required])
19
+ expect(kwargs.keys).to match_array(%i[label desc required])
20
20
  end
21
21
 
22
22
  it 'automatically sets label' do
@@ -38,16 +38,34 @@ describe 'Parameters::Typed' do
38
38
  it 'can be required' do
39
39
  p = p_arg(required: true)
40
40
  expect(p.required?).to be true
41
+ expect(p.optional?).to be false
41
42
  end
42
43
 
43
44
  it 'can be optional' do
44
45
  p = p_arg
46
+ expect(p.required?).to be false
45
47
  expect(p.optional?).to be true
46
48
 
47
49
  p = p_arg(required: false)
50
+ expect(p.required?).to be false
48
51
  expect(p.optional?).to be true
49
52
 
50
53
  p = p_arg(required: nil)
54
+ expect(p.required?).to be false
55
+ expect(p.optional?).to be true
56
+ end
57
+
58
+ it 'updates optional? when required is patched' do
59
+ p = p_arg(required: false)
60
+ expect(p.required?).to be false
61
+ expect(p.optional?).to be true
62
+
63
+ p.patch(required: true)
64
+ expect(p.required?).to be true
65
+ expect(p.optional?).to be false
66
+
67
+ p.patch(required: nil)
68
+ expect(p.required?).to be false
51
69
  expect(p.optional?).to be true
52
70
  end
53
71
 
@@ -55,10 +73,26 @@ describe 'Parameters::Typed' do
55
73
  # Integer
56
74
  p = p_type(Integer)
57
75
  expect(p.clean('42')).to eq(42)
76
+ expect(p.clean(' -7 ')).to eq(-7)
77
+ expect(p.clean(12)).to eq(12)
78
+ expect(p.clean(12.0)).to eq(12)
79
+ expect { p.clean('abc') }.to raise_error(HaveAPI::ValidationError)
80
+ expect { p.clean('12abc') }.to raise_error(HaveAPI::ValidationError)
81
+ expect { p.clean('') }.to raise_error(HaveAPI::ValidationError)
82
+ expect { p.clean('12.0') }.to raise_error(HaveAPI::ValidationError)
83
+ expect { p.clean(12.3) }.to raise_error(HaveAPI::ValidationError)
84
+ expect { p.clean(true) }.to raise_error(HaveAPI::ValidationError)
58
85
 
59
86
  # Float
60
87
  p = p_type(Float)
61
88
  expect(p.clean('3.1456')).to eq(3.1456)
89
+ expect(p.clean('1e3')).to eq(1000.0)
90
+ expect(p.clean(3)).to eq(3.0)
91
+ expect { p.clean('abc') }.to raise_error(HaveAPI::ValidationError)
92
+ expect { p.clean('') }.to raise_error(HaveAPI::ValidationError)
93
+ expect { p.clean('NaN') }.to raise_error(HaveAPI::ValidationError)
94
+ expect { p.clean(Float::NAN) }.to raise_error(HaveAPI::ValidationError)
95
+ expect { p.clean(Float::INFINITY) }.to raise_error(HaveAPI::ValidationError)
62
96
 
63
97
  # Boolean
64
98
  p = p_type(Boolean)
@@ -71,6 +105,13 @@ describe 'Parameters::Typed' do
71
105
  expect(p.clean(v)).to be false
72
106
  end
73
107
 
108
+ expect(p.clean(0)).to be false
109
+ expect(p.clean(1)).to be true
110
+ expect(p.clean(' YES ')).to be true
111
+ expect { p.clean('maybe') }.to raise_error(HaveAPI::ValidationError)
112
+ expect { p.clean('') }.to raise_error(HaveAPI::ValidationError)
113
+ expect { p.clean(2) }.to raise_error(HaveAPI::ValidationError)
114
+
74
115
  # Datetime
75
116
  p = p_type(Datetime)
76
117
  t = Time.now
@@ -78,10 +119,22 @@ describe 'Parameters::Typed' do
78
119
 
79
120
  expect(p.clean(t.iso8601)).to eq(t2)
80
121
  expect { p.clean('bzz') }.to raise_error(HaveAPI::ValidationError)
122
+ expect { p.clean('') }.to raise_error(HaveAPI::ValidationError)
81
123
 
82
124
  # String, Text
83
125
  p = p_type(String)
84
126
  expect(p.clean('bzz')).to eq('bzz')
127
+ expect(p.clean(123)).to eq('123')
128
+ expect(p.clean(true)).to eq('true')
129
+ expect { p.clean([]) }.to raise_error(HaveAPI::ValidationError)
130
+ expect { p.clean({}) }.to raise_error(HaveAPI::ValidationError)
131
+
132
+ p = p_type(Text)
133
+ expect(p.clean('bzz')).to eq('bzz')
134
+ expect(p.clean(123)).to eq('123')
135
+ expect(p.clean(true)).to eq('true')
136
+ expect { p.clean([]) }.to raise_error(HaveAPI::ValidationError)
137
+ expect { p.clean({}) }.to raise_error(HaveAPI::ValidationError)
85
138
 
86
139
  # Defaults
87
140
  p = p_type(String)
data/spec/params_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- describe HaveAPI::Params do
1
+ module ParamsSpec
2
2
  class MyResource < HaveAPI::Resource
3
3
  params(:all) do
4
4
  string :res_param1
@@ -17,63 +17,65 @@ describe HaveAPI::Params do
17
17
  class Create < HaveAPI::Actions::Default::Create
18
18
  end
19
19
  end
20
+ end
20
21
 
22
+ describe HaveAPI::Params do
21
23
  it 'executes all blocks' do
22
- p = HaveAPI::Params.new(:input, MyResource::Index)
24
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
23
25
  p.add_block proc { string :param1 }
24
26
  p.add_block proc { string :param2 }
25
27
  p.add_block proc { string :param3 }
26
28
  p.exec
27
- expect(p.params.map(&:name)).to contain_exactly(*%i[param1 param2 param3])
29
+ expect(p.params.map(&:name)).to match_array(%i[param1 param2 param3])
28
30
  end
29
31
 
30
32
  it 'returns deduced singular namespace' do
31
- p = HaveAPI::Params.new(:input, MyResource::Index)
33
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
32
34
  expect(p.namespace).to eq(:my_resource)
33
35
  end
34
36
 
35
37
  it 'returns deduced plural namespace' do
36
- p = HaveAPI::Params.new(:input, MyResource::Index)
38
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
37
39
  p.layout = :object_list
38
40
  expect(p.namespace).to eq(:my_resources)
39
41
  end
40
42
 
41
43
  it 'returns set namespace' do
42
- p = HaveAPI::Params.new(:input, MyResource::Index)
44
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
43
45
  p.namespace = :custom_ns
44
46
  expect(p.namespace).to eq(:custom_ns)
45
47
  end
46
48
 
47
49
  it 'uses params from parent resource' do
48
- p = HaveAPI::Params.new(:input, MyResource::Index)
50
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
49
51
  p.add_block proc { use :all }
50
52
  p.exec
51
- expect(p.params.map(&:name)).to contain_exactly(*%i[res_param1 res_param2])
53
+ expect(p.params.map(&:name)).to match_array(%i[res_param1 res_param2])
52
54
  end
53
55
 
54
56
  it 'has param requires' do
55
- p = HaveAPI::Params.new(:input, MyResource::Index)
57
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
56
58
  p.add_block proc { requires :p_required }
57
59
  p.exec
58
60
  expect(p.params.first.required?).to be true
59
61
  end
60
62
 
61
63
  it 'has param optional' do
62
- p = HaveAPI::Params.new(:input, MyResource::Index)
64
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
63
65
  p.add_block proc { optional :p_optional }
64
66
  p.exec
65
67
  expect(p.params.first.required?).to be false
66
68
  end
67
69
 
68
70
  it 'has param string' do
69
- p = HaveAPI::Params.new(:input, MyResource::Index)
71
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
70
72
  p.add_block proc { string :p_string }
71
73
  p.exec
72
74
  expect(p.params.first.type).to eq(String)
73
75
  end
74
76
 
75
77
  it 'has param text' do
76
- p = HaveAPI::Params.new(:input, MyResource::Index)
78
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
77
79
  p.add_block proc { text :p_text }
78
80
  p.exec
79
81
  expect(p.params.first.type).to eq(Text)
@@ -81,7 +83,7 @@ describe HaveAPI::Params do
81
83
 
82
84
  %i[id integer foreign_key].each do |type|
83
85
  it "has param #{type}" do
84
- p = HaveAPI::Params.new(:input, MyResource::Index)
86
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
85
87
  p.add_block proc { send(type, :"p_#{type}") }
86
88
  p.exec
87
89
  expect(p.params.first.type).to eq(Integer)
@@ -89,42 +91,42 @@ describe HaveAPI::Params do
89
91
  end
90
92
 
91
93
  it 'has param float' do
92
- p = HaveAPI::Params.new(:input, MyResource::Index)
94
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
93
95
  p.add_block proc { float :p_float }
94
96
  p.exec
95
97
  expect(p.params.first.type).to eq(Float)
96
98
  end
97
99
 
98
100
  it 'has param bool' do
99
- p = HaveAPI::Params.new(:input, MyResource::Index)
101
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
100
102
  p.add_block proc { bool :p_bool }
101
103
  p.exec
102
104
  expect(p.params.first.type).to eq(Boolean)
103
105
  end
104
106
 
105
107
  it 'has param datetime' do
106
- p = HaveAPI::Params.new(:input, MyResource::Index)
108
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
107
109
  p.add_block proc { datetime :p_datetime }
108
110
  p.exec
109
111
  expect(p.params.first.type).to eq(Datetime)
110
112
  end
111
113
 
112
114
  it 'has param param' do
113
- p = HaveAPI::Params.new(:input, MyResource::Index)
115
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
114
116
  p.add_block proc { param :p_param, type: Integer }
115
117
  p.exec
116
118
  expect(p.params.first.type).to eq(Integer)
117
119
  end
118
120
 
119
121
  it 'has param resource' do
120
- p = HaveAPI::Params.new(:input, MyResource::Index)
121
- p.add_block proc { resource MyResource }
122
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
123
+ p.add_block proc { resource ParamsSpec::MyResource }
122
124
  p.exec
123
125
  expect(p.params.first).to be_an_instance_of(HaveAPI::Parameters::Resource)
124
126
  end
125
127
 
126
128
  it 'patches params' do
127
- p = HaveAPI::Params.new(:input, MyResource::Index)
129
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
128
130
  p.add_block(proc do
129
131
  string :param1, label: 'Param 1'
130
132
  string :param2, label: 'Param 2', desc: 'Implicit description'
@@ -137,15 +139,15 @@ describe HaveAPI::Params do
137
139
  end
138
140
 
139
141
  it 'validates upon build' do
140
- p = HaveAPI::Params.new(:output, MyResource::Index)
141
- p.add_block proc { resource MyResource }
142
+ p = described_class.new(:output, ParamsSpec::MyResource::Index)
143
+ p.add_block proc { resource ParamsSpec::MyResource }
142
144
  p.exec
143
145
  expect(p.params.first).to be_an_instance_of(HaveAPI::Parameters::Resource)
144
146
  expect { p.validate_build }.to raise_error(RuntimeError)
145
147
  end
146
148
 
147
149
  it 'accepts valid input layout' do
148
- p = HaveAPI::Params.new(:input, MyResource::Index)
150
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
149
151
  p.add_block(proc do
150
152
  string :param1, required: true
151
153
  string :param2
@@ -160,7 +162,7 @@ describe HaveAPI::Params do
160
162
  end
161
163
 
162
164
  it 'rejects invalid input layout' do
163
- p = HaveAPI::Params.new(:input, MyResource::Index)
165
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
164
166
  p.add_block(proc do
165
167
  string :param1, required: true
166
168
  string :param2
@@ -175,7 +177,7 @@ describe HaveAPI::Params do
175
177
  end
176
178
 
177
179
  it 'indexes parameters by name' do
178
- p = HaveAPI::Params.new(:input, MyResource::Index)
180
+ p = described_class.new(:input, ParamsSpec::MyResource::Index)
179
181
  p.add_block(proc do
180
182
  string :param1
181
183
  string :param2