smart_params 5.1.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,42 +3,95 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe SmartParams do
6
- let(:schema) { CreateAccountSchema.new(params) }
7
- let(:nullable_schema) { NullableSchema.new(params) }
8
- let(:nullable_required_subfield_schema) { NullableRequiredSubfieldSchema.new(params) }
6
+ let(:account_payload) { described_class.from(AccountSchema, params, :create) }
7
+ let(:nullable_payload) { described_class.from(NullableSchema, params) }
8
+ let(:nullable_required_subfield_payload) { described_class.from(NullableRequiredSubfieldSchema, params) }
9
+
10
+ describe ".validate!(account_payload)" do
11
+ subject { account_payload }
12
+
13
+ let(:account_payload) { described_class.validate!(AccountSchema, params, :create) }
9
14
 
10
- describe ".new" do
11
15
  context "with an empty params" do
12
16
  let(:params) { {} }
13
17
 
14
18
  it "throws an error with a message detailing the invalid property type and given properties" do
15
- expect { schema }.to raise_exception(SmartParams::Error::InvalidPropertyType, "expected [:data] to be Hash, but is nil")
19
+ expect { subject }.to raise_exception(SmartParams::InvalidPayloadException, "structure failed to validate: \n\t/data is missing from the structure, last node was {}\n\t/data/type is missing from the structure, last node was {}\n\t/data/attributes is missing from the structure, last node was {}\n\t/data/attributes/email is missing from the structure, last node was {}")
16
20
  end
17
21
 
18
22
  it "throws an error with the missing property and given properties" do
19
- expect { schema }.to raise_exception do |exception|
20
- expect(exception).to have_attributes(keychain: [:data], wanted: a_kind_of(Dry::Types::Constrained), raw: nil)
23
+ expect { subject }.to raise_exception(SmartParams::InvalidPayloadException) do |exception|
24
+ expect(exception).to(have_attributes(failures: an_array_matching([
25
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data], last: {}),
26
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :type], last: {}),
27
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :attributes], last: {}),
28
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :attributes, :email], last: {})
29
+ ])))
30
+ end
31
+ end
32
+ end
33
+
34
+ context "with a good root key, intermediary key, and bad type" do
35
+ let(:params) do
36
+ {
37
+ data: {
38
+ type: "accounts",
39
+ attributes: {
40
+ email: "kurtis@example.com",
41
+ password: 1
42
+ }
43
+ },
44
+ meta: {
45
+ jsonapi_version: "1.0"
46
+ },
47
+ included: [
48
+ {
49
+ data: {
50
+ id: "a",
51
+ type: "widget",
52
+ attributes: {
53
+ title: "Widget A"
54
+ }
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ end
60
+
61
+ it "throws an error with a message detailing the invalid property, expected type, given type, and given value" do
62
+ expect { subject }.to raise_exception(SmartParams::InvalidPayloadException, "structure failed to validate: \n\texpected /data/attributes/password to be NilClass | String, but is 1 and 1 violates constraints (type?(String, 1) failed)")
63
+ end
64
+
65
+ it "throws an error with the invalid property, expected type, given type, and given value" do
66
+ expect { subject }.to raise_exception(SmartParams::InvalidPayloadException) do |exception|
67
+ expect(exception).to(have_attributes(failures: an_array_matching([
68
+ have_attributes(class: SmartParams::InvalidPropertyTypeException, path: [:data, :attributes, :password])
69
+ ])))
21
70
  end
22
71
  end
23
72
  end
24
73
 
25
- context "with a good key but bad type" do
74
+ context "with a good root key but missing intermediary key" do
26
75
  let(:params) { { data: "" } }
27
76
 
28
77
  it "throws an error with a message detailing the invalid property, expected type, given type, and given value" do
29
- expect { schema }.to raise_exception(SmartParams::Error::InvalidPropertyType, "expected [:data] to be Hash, but is \"\"")
78
+ expect { subject }.to raise_exception(SmartParams::InvalidPayloadException, "structure failed to validate: \n\t/data/type is missing from the structure, last node was \"\"\n\t/data/attributes is missing from the structure, last node was \"\"\n\t/data/attributes/email is missing from the structure, last node was \"\"")
30
79
  end
31
80
 
32
81
  it "throws an error with the invalid property, expected type, given type, and given value" do
33
- expect { schema }.to raise_exception do |exception|
34
- expect(exception).to have_attributes(keychain: [:data], wanted: a_kind_of(Dry::Types::Constrained), raw: "")
82
+ expect { subject }.to raise_exception(SmartParams::InvalidPayloadException) do |exception|
83
+ expect(exception).to(have_attributes(failures: an_array_matching([
84
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :type], last: ""),
85
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :attributes], last: ""),
86
+ have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :attributes, :email], last: "")
87
+ ])))
35
88
  end
36
89
  end
37
90
  end
38
91
  end
39
92
 
40
- describe "#payload" do
41
- subject { schema.payload }
93
+ describe ".from(account_payload)" do
94
+ subject { account_payload }
42
95
 
43
96
  context "with a reasonably good params" do
44
97
  let(:params) do
@@ -67,25 +120,59 @@ RSpec.describe SmartParams do
67
120
  }
68
121
  end
69
122
 
123
+ it "returns as native data types" do
124
+ expect(
125
+ subject
126
+ ).to match(
127
+ hash_including(
128
+ {
129
+ data: hash_including(
130
+ {
131
+ type: "accounts",
132
+ attributes: hash_including(
133
+ {
134
+ email: "kurtis@example.com",
135
+ password: an_instance_of(String)
136
+ }
137
+ )
138
+ }
139
+ ),
140
+ meta: {
141
+ jsonapi_version: "1.0"
142
+ },
143
+ included: [
144
+ {
145
+ data: {
146
+ id: "a",
147
+ type: "widget",
148
+ attributes: {
149
+ title: "Widget A"
150
+ }
151
+ }
152
+ }
153
+ ]
154
+ }
155
+ )
156
+ )
157
+ end
158
+
70
159
  it "has a chain path data.type" do
71
- expect(subject.data.type).to eq("accounts")
160
+ expect(subject[:data][:type]).to eq("accounts")
72
161
  end
73
162
 
74
163
  it "has a chain path data.attributes.email" do
75
- expect(subject.data.attributes.email).to eq("kurtis@example.com")
164
+ expect(subject[:data][:attributes][:email]).to eq("kurtis@example.com")
76
165
  end
77
166
 
78
167
  it "has a chain path data.attributes.password" do
79
- expect(subject.data.attributes.password).to be_a(String)
168
+ expect(subject[:data][:attributes][:password]).to be_a(String)
80
169
  end
81
170
 
82
171
  it "has a chain path meta.jsonapi_version" do
83
- expect(subject.meta.jsonapi_version).to eq("1.0")
172
+ expect(subject[:meta][:jsonapi_version]).to eq("1.0")
84
173
  end
85
174
  end
86
- end
87
175
 
88
- shared_examples "native types" do
89
176
  context "with extra params" do
90
177
  let(:params) do
91
178
  {
@@ -105,12 +192,12 @@ RSpec.describe SmartParams do
105
192
  subject
106
193
  ).to match(
107
194
  {
108
- "data" => hash_including(
195
+ data: hash_including(
109
196
  {
110
- "type" => "accounts",
111
- "attributes" => hash_including(
197
+ type: "accounts",
198
+ attributes: hash_including(
112
199
  {
113
- "email" => "kurtis@example.com"
200
+ email: "kurtis@example.com"
114
201
  }
115
202
  )
116
203
  }
@@ -138,12 +225,12 @@ RSpec.describe SmartParams do
138
225
  subject
139
226
  ).to match(
140
227
  {
141
- "data" => hash_including(
228
+ data: hash_including(
142
229
  {
143
- "type" => "accounts",
144
- "attributes" => hash_including(
230
+ type: "accounts",
231
+ attributes: hash_including(
145
232
  {
146
- "email" => "kurtis@example.com",
233
+ email: "kurtis@example.com",
147
234
  "full-name" => "Kurtis Rainbolt-Greene"
148
235
  }
149
236
  )
@@ -153,234 +240,63 @@ RSpec.describe SmartParams do
153
240
  )
154
241
  end
155
242
  end
156
-
157
- context "with a reasonably good params" do
158
- let(:params) do
159
- {
160
- data: {
161
- type: "accounts",
162
- attributes: {
163
- email: "kurtis@example.com",
164
- password: "secret"
165
- }
166
- },
167
- meta: {
168
- jsonapi_version: "1.0"
169
- },
170
- included: [
171
- {
172
- data: {
173
- id: "a",
174
- type: "widget",
175
- attributes: {
176
- title: "Widget A"
177
- }
178
- }
179
- }
180
- ]
181
- }
182
- end
183
-
184
- it "returns as native data types" do
185
- expect(
186
- subject
187
- ).to match(
188
- hash_including(
189
- {
190
- "data" => hash_including(
191
- {
192
- "type" => "accounts",
193
- "attributes" => hash_including(
194
- {
195
- "email" => "kurtis@example.com",
196
- "password" => an_instance_of(String)
197
- }
198
- )
199
- }
200
- ),
201
- "meta" => {
202
- "jsonapi_version" => "1.0"
203
- },
204
- "included" => [
205
- {
206
- "data" => {
207
- "id" => "a",
208
- "type" => "widget",
209
- "attributes" => {
210
- "title" => "Widget A"
211
- }
212
- }
213
- }
214
- ]
215
- }
216
- )
217
- )
218
- end
219
- end
220
- end
221
-
222
- describe "#to_hash" do
223
- subject { schema.to_hash }
224
-
225
- include_examples "native types"
226
- end
227
-
228
- describe "#as_json" do
229
- subject { schema.as_json }
230
-
231
- include_examples "native types"
232
243
  end
233
244
 
234
- describe "#fetch" do
235
- subject { schema.fetch("data") }
245
+ describe ".from(nullable_required_subfield_payload)" do
246
+ subject { nullable_required_subfield_payload }
236
247
 
237
- context "with a reasonably good params" do
248
+ context "when specified with unclean data" do
238
249
  let(:params) do
239
250
  {
251
+ # This will raise an exception becase the data hash is specified
252
+ # but its required subfields are not.
240
253
  data: {
241
- type: "accounts",
242
- attributes: {
243
- email: "kurtis@example.com",
244
- password: "secret"
245
- }
246
- },
247
- meta: {
248
- jsonapi_version: "1.0"
249
- },
250
- included: [
251
- {
252
- data: {
253
- id: "a",
254
- type: "widget",
255
- attributes: {
256
- title: "Widget A"
257
- }
258
- }
259
- }
260
- ]
254
+ is: "garbage"
255
+ }
261
256
  }
262
257
  end
263
258
 
264
- it "returns the native type" do
265
- expect(
266
- subject
267
- ).to match(
268
- hash_including(
269
- {
270
- "type" => "accounts",
271
- "attributes" => hash_including(
272
- {
273
- "email" => "kurtis@example.com",
274
- "password" => an_instance_of(String)
275
- }
276
- )
277
- }
278
- )
279
- )
259
+ it "checks subfields" do
260
+ expect(subject).to(an_array_matching([have_attributes(class: SmartParams::MissingPropertyException, path: [:data, :id], last: { is: "garbage" })]))
280
261
  end
281
262
  end
282
- end
283
-
284
- describe "#dig" do
285
- subject { schema.dig("data", "attributes", "email") }
286
263
 
287
- context "with a reasonably good params" do
264
+ context "when specified as null" do
288
265
  let(:params) do
289
266
  {
290
- data: {
291
- type: "accounts",
292
- attributes: {
293
- email: "kurtis@example.com"
294
- }
295
- },
296
- meta: {
297
- jsonapi_version: "1.0"
298
- },
299
- included: [
300
- {
301
- data: {
302
- id: "a",
303
- type: "widget",
304
- attributes: {
305
- title: "Widget A"
306
- }
307
- }
308
- }
309
- ]
267
+ # This will not raise an error, since data is allowed to be null.
268
+ # Subfields will not be checked.
269
+ data: nil
310
270
  }
311
271
  end
312
272
 
313
- it "returns the native type" do
314
- expect(
273
+ it "checks subfields" do
274
+ expect do
315
275
  subject
316
- ).to eq(
317
- "kurtis@example.com"
318
- )
276
+ end.not_to raise_exception
319
277
  end
320
278
  end
321
- end
322
-
323
- describe "#fetch_values" do
324
- subject { schema.fetch_values("data", "meta") }
325
279
 
326
- context "with a reasonably good params" do
280
+ context "when unspecified with required subfield" do
327
281
  let(:params) do
328
282
  {
329
- data: {
330
- type: "accounts",
331
- attributes: {
332
- email: "kurtis@example.com",
333
- password: "secret"
334
- }
335
- },
336
- meta: {
337
- jsonapi_version: "1.0"
338
- },
339
- included: [
340
- {
341
- data: {
342
- id: "a",
343
- type: "widget",
344
- attributes: {
345
- title: "Widget A"
346
- }
347
- }
348
- }
349
- ]
283
+ # In this case, the nullable data hash is not specified so we
284
+ # don't need to enforce constraints on subfields.
350
285
  }
351
286
  end
352
287
 
353
- it "returns the native type" do
354
- expect(
288
+ it "allows null value" do
289
+ expect do
355
290
  subject
356
- ).to match(
357
- [
358
- hash_including(
359
- {
360
- "type" => "accounts",
361
- "attributes" => hash_including(
362
- {
363
- "email" => "kurtis@example.com",
364
- "password" => an_instance_of(String)
365
- }
366
- )
367
- }
368
- ),
369
- hash_including(
370
- {
371
- "jsonapi_version" => "1.0"
372
- }
373
- )
374
- ]
375
- )
291
+ end.not_to raise_exception
376
292
  end
377
293
  end
378
294
  end
379
295
 
380
- describe "nullable values" do
381
- context "when set to nil" do
382
- subject { nullable_schema.to_hash }
296
+ describe ".from(nullable_payload)" do
297
+ subject { nullable_payload }
383
298
 
299
+ context "when set to nil" do
384
300
  let(:params) do
385
301
  {
386
302
  data: nil
@@ -393,7 +309,7 @@ RSpec.describe SmartParams do
393
309
  ).to match(
394
310
  hash_including(
395
311
  {
396
- "data" => nil
312
+ data: nil
397
313
  }
398
314
  )
399
315
  )
@@ -401,8 +317,6 @@ RSpec.describe SmartParams do
401
317
  end
402
318
 
403
319
  context "when provided matching data" do
404
- subject { nullable_schema.to_hash }
405
-
406
320
  let(:params) do
407
321
  {
408
322
  data: {
@@ -418,10 +332,10 @@ RSpec.describe SmartParams do
418
332
  ).to match(
419
333
  hash_including(
420
334
  {
421
- "data" => hash_including(
335
+ data: hash_including(
422
336
  {
423
- "id" => "1",
424
- "type" => "people"
337
+ id: "1",
338
+ type: "people"
425
339
  }
426
340
  )
427
341
  }
@@ -431,8 +345,6 @@ RSpec.describe SmartParams do
431
345
  end
432
346
 
433
347
  context "when not provided" do
434
- subject { nullable_schema.to_hash }
435
-
436
348
  let(:params) do
437
349
  {}
438
350
  end
@@ -443,7 +355,7 @@ RSpec.describe SmartParams do
443
355
  ).to match(
444
356
  hash_excluding(
445
357
  {
446
- "data" => nil
358
+ data: nil
447
359
  }
448
360
  )
449
361
  )
@@ -451,8 +363,6 @@ RSpec.describe SmartParams do
451
363
  end
452
364
 
453
365
  context "with non matching subfield data" do
454
- subject { nullable_schema.to_hash }
455
-
456
366
  let(:params) do
457
367
  {
458
368
  data: {
@@ -469,66 +379,11 @@ RSpec.describe SmartParams do
469
379
  ).to match(
470
380
  hash_excluding(
471
381
  {
472
- "is" => "garbage"
382
+ is: "garbage"
473
383
  }
474
384
  )
475
385
  )
476
386
  end
477
387
  end
478
-
479
- context "when specified with unclean data" do
480
- subject { nullable_required_subfield_schema.to_hash }
481
-
482
- let(:params) do
483
- {
484
- # This will raise an exception becase the data hash is specified
485
- # but its required subfields are not.
486
- data: {
487
- is: "garbage"
488
- }
489
- }
490
- end
491
-
492
- it "checks subfields" do
493
- expect do
494
- subject
495
- end.to raise_exception(SmartParams::Error::InvalidPropertyType)
496
- end
497
- end
498
-
499
- context "when specified as null" do
500
- subject { nullable_required_subfield_schema.to_hash }
501
-
502
- let(:params) do
503
- {
504
- # This will not raise an error, since data is allowed to be null.
505
- # Subfields will not be checked.
506
- data: nil
507
- }
508
- end
509
-
510
- it "checks subfields" do
511
- expect do
512
- subject
513
- end.not_to raise_exception
514
- end
515
- end
516
-
517
- context "when unspecified with required subfield" do
518
- subject { nullable_required_subfield_schema.to_hash }
519
-
520
- let(:params) do
521
- {
522
- # In this case, the nullable data hash is not specified so we
523
- # don't need to enforce constraints on subfields.
524
- }
525
- end
526
-
527
- it "allows null value" do
528
- expect do
529
- subject
530
- end.not_to raise_exception
531
- end
532
- end
533
388
  end
534
389
  end
metadata CHANGED
@@ -1,57 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_params
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kurtis Rainbolt-Greene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-10 00:00:00.000000000 Z
11
+ date: 2024-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '7.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '7.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: dry-types
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '1.7'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: recursive-open-struct
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
40
+ version: '1.7'
55
41
  description: Apply an organized and easy to maintain schema to request params
56
42
  email:
57
43
  - kurtis@rainbolt-greene.online
@@ -63,10 +49,15 @@ files:
63
49
  - README.md
64
50
  - Rakefile
65
51
  - lib/smart_params.rb
66
- - lib/smart_params/error.rb
67
- - lib/smart_params/error/invalid_property_type.rb
68
- - lib/smart_params/error/invalid_property_type_spec.rb
69
52
  - lib/smart_params/field.rb
53
+ - lib/smart_params/fluent_language.rb
54
+ - lib/smart_params/invalid_payload_exception.rb
55
+ - lib/smart_params/invalid_property_type_exception.rb
56
+ - lib/smart_params/missing_property_exception.rb
57
+ - lib/smart_params/missing_type_annotation_exception.rb
58
+ - lib/smart_params/namespace_already_defined_exception.rb
59
+ - lib/smart_params/no_matching_namespace_exception.rb
60
+ - lib/smart_params/path_already_defined_exception.rb
70
61
  - lib/smart_params/version.rb
71
62
  - lib/smart_params_spec.rb
72
63
  homepage: https://github.com/krainboltgreene/smart_params.rb