verquest 0.4.0 → 0.6.0
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/CHANGELOG.md +22 -0
- data/README.md +141 -20
- data/lib/verquest/base/private_class_methods.rb +73 -27
- data/lib/verquest/base/public_class_methods.rb +12 -0
- data/lib/verquest/configuration.rb +8 -2
- data/lib/verquest/gem_version.rb +1 -1
- data/lib/verquest/helper_methods/required_properties.rb +48 -0
- data/lib/verquest/properties/array.rb +16 -8
- data/lib/verquest/properties/base.rb +10 -4
- data/lib/verquest/properties/collection.rb +29 -15
- data/lib/verquest/properties/const.rb +53 -0
- data/lib/verquest/properties/enum.rb +65 -0
- data/lib/verquest/properties/field.rb +12 -4
- data/lib/verquest/properties/object.rb +25 -9
- data/lib/verquest/properties/reference.rb +30 -11
- data/lib/verquest/transformer.rb +7 -7
- data/lib/verquest/version.rb +34 -5
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92e6047d9812ae82cf55b5d0512c459a61306c7b2cc78c9cd224e2b52b4b716c
|
4
|
+
data.tar.gz: db8943d346e30972f3b6b516d9365d7ef37dedf89fa7f4a09c5db4cb1adbe545
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0163416a3ea0f4d5cb5619b74e31ca7f2999ddccba55091d8fa8f10244a36eeb59dadd1b6d9eae6bd22d1f49e5ea7213aeb42218731f248be69d54c2127bb216
|
7
|
+
data.tar.gz: ee7adc256954aafa194a3175f50649479a8f45292d8bed51cf076f17c1d0693fbe7099e64675d249448c1e66cb9b1dce4749da71e76f36fc2d976b7387f23382
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.6.0] - 2025-07-18
|
4
|
+
|
5
|
+
### Breaking Changes
|
6
|
+
- **BREAKING:** Switching to slash notation for mapping to improve consistency with how properties are referenced in JSON Schema. Before: `example.nested.property`, now: `example/nested/property`. ([#12](https://github.com/CiTroNaK/verquest/pull/12), [@CiTroNaK](https://github.com/CiTroNaK))
|
7
|
+
|
8
|
+
### New Features
|
9
|
+
- Add support for `enum` properties. ([#13](https://github.com/CiTroNaK/verquest/pull/13), [@CiTroNaK](https://github.com/CiTroNaK))
|
10
|
+
- Add support for inverted mapping, that can be used to map internal structure to external representation (e.g., for errors). ([#10](https://github.com/CiTroNaK/verquest/pull/10), [@CiTroNaK](https://github.com/CiTroNaK))
|
11
|
+
- Add support for constants in the schema, allowing to define fixed values for properties. ([#11](https://github.com/CiTroNaK/verquest/pull/11), [@CiTroNaK](https://github.com/CiTroNaK))
|
12
|
+
|
13
|
+
## [0.5.0] - 2025-07-01
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
- Handling `with_options` defaults like required and nullable.
|
17
|
+
|
18
|
+
### New Features
|
19
|
+
- Add `default_additional_properties` option to configuration.
|
20
|
+
- Add support for nullable properties (`nullable: true`) based on the latest JSON Schema specification, which is also used in OpenAPI 3.1.
|
21
|
+
- Add support for `dependentRequired` (see https://json-schema.org/understanding-json-schema/reference/conditionals#dependentRequired).
|
22
|
+
|
23
|
+
## [0.4.0] - 2025-06-28
|
24
|
+
|
3
25
|
### Breaking Changes
|
4
26
|
- **BREAKING:** Renaming validation method from `validate_schema` to `valid_schema?` to better reflect its purpose.
|
5
27
|
- **BREAKING:** The `validate_schema` now returns an array of errors instead of a boolean value, allowing for more detailed error reporting.
|
data/README.md
CHANGED
@@ -8,18 +8,18 @@ Verquest is a Ruby gem that offers an elegant solution for versioning API reques
|
|
8
8
|
- Defining versioned request structures
|
9
9
|
- Gracefully handling API versioning
|
10
10
|
- Mapping between external and internal parameter structures
|
11
|
-
- Validating parameters against [JSON Schema](https://json-schema.org/
|
11
|
+
- Validating parameters against [JSON Schema](https://json-schema.org/)
|
12
12
|
- Generating components for OpenAPI documentation
|
13
|
-
- Mapping error keys back to the external API structure
|
13
|
+
- Mapping error keys back to the external API structure
|
14
14
|
|
15
|
-
> The gem is still in development. Until version 1.0, the API may change. There are some features like `oneOf`, `anyOf`, `allOf` that are not implemented yet.
|
15
|
+
> The gem is still in development. Until version 1.0, the API may change. There are some features like `oneOf`, `anyOf`, `allOf` that are not implemented yet. See open [issues](https://github.com/CiTroNaK/verquest/issues?q=sort:updated-desc%20is:issue%20is:open%20label:enhancement).
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
|
19
19
|
Add this line to your application's Gemfile:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
gem "verquest", "~> 0.
|
22
|
+
gem "verquest", "~> 0.6"
|
23
23
|
```
|
24
24
|
|
25
25
|
And then execute:
|
@@ -62,7 +62,7 @@ class UserCreateRequest < Verquest::Base
|
|
62
62
|
field :email, format: "email", description: "The email address of the user"
|
63
63
|
end
|
64
64
|
|
65
|
-
field :birth_date, type: :string, format: "date", description: "The birth date of the user"
|
65
|
+
field :birth_date, type: :string, nullable: true, format: "date", description: "The birth date of the user"
|
66
66
|
|
67
67
|
reference :address, from: AddressCreateRequest, required: true
|
68
68
|
|
@@ -75,7 +75,7 @@ class UserCreateRequest < Verquest::Base
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
|
78
|
+
enum :role, values: %w[member manager], default: "member", description: "Role of the user", required: true
|
79
79
|
|
80
80
|
object :profile_details do
|
81
81
|
field :bio, type: :string, description: "Short biography of the user"
|
@@ -89,6 +89,8 @@ class UserCreateRequest < Verquest::Base
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
92
|
+
|
93
|
+
const :company, value: "Awesome Inc."
|
92
94
|
end
|
93
95
|
end
|
94
96
|
```
|
@@ -130,12 +132,12 @@ Output:
|
|
130
132
|
{
|
131
133
|
"type" => "object",
|
132
134
|
"description" => "User Create Request",
|
133
|
-
"required" => ["first_name", "last_name", "email", "address"],
|
135
|
+
"required" => ["first_name", "last_name", "email", "address", "role"],
|
134
136
|
"properties" => {
|
135
137
|
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
136
138
|
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
137
139
|
"email" => {"type" => "string", "format" => "email", "description" => "The email address of the user"},
|
138
|
-
"birth_date" => {"type" => "string", "format" => "date", "description" => "The birth date of the user"},
|
140
|
+
"birth_date" => {"type" => ["string", "null"], "format" => "date", "description" => "The birth date of the user"},
|
139
141
|
"address" => {"$ref" => "#/components/schemas/AddressCreateRequest"},
|
140
142
|
"permissions" => {
|
141
143
|
"type" => "array",
|
@@ -151,10 +153,9 @@ Output:
|
|
151
153
|
"description" => "Permissions associated with the user"
|
152
154
|
},
|
153
155
|
"role" => {
|
154
|
-
"
|
155
|
-
"
|
156
|
-
"
|
157
|
-
"default" => "member"
|
156
|
+
"enum" => ["member", "manager"],
|
157
|
+
"default" => "member",
|
158
|
+
"description" => "Role of the user"
|
158
159
|
},
|
159
160
|
"profile_details" => {
|
160
161
|
"type" => "object",
|
@@ -172,7 +173,8 @@ Output:
|
|
172
173
|
"description" => "Some social networks"
|
173
174
|
}
|
174
175
|
}
|
175
|
-
}
|
176
|
+
},
|
177
|
+
"company" => {"const" => "Awesome Inc."}
|
176
178
|
},
|
177
179
|
"additionalProperties" => false
|
178
180
|
}
|
@@ -191,7 +193,7 @@ Output:
|
|
191
193
|
{
|
192
194
|
"type" => "object",
|
193
195
|
"description" => "User Create Request",
|
194
|
-
"required" => ["first_name", "last_name", "email", "address"],
|
196
|
+
"required" => ["first_name", "last_name", "email", "address", "role"],
|
195
197
|
"properties" => {
|
196
198
|
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
197
199
|
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
@@ -222,10 +224,9 @@ Output:
|
|
222
224
|
"description" => "Permissions associated with the user"
|
223
225
|
},
|
224
226
|
"role" => {
|
225
|
-
"type" => "string",
|
226
|
-
"description" => "Role of the user",
|
227
227
|
"enum" => ["member", "manager"],
|
228
|
-
"default" => "member"
|
228
|
+
"default" => "member",
|
229
|
+
"description" => "Role of the user"
|
229
230
|
},
|
230
231
|
"profile_details" => {"type" => "object",
|
231
232
|
"required" => [],
|
@@ -240,7 +241,8 @@ Output:
|
|
240
241
|
"mastodon" => {"type" => "string", "format" => "uri", "description" => "Mastodon profile URL"}
|
241
242
|
},
|
242
243
|
"description" => "Some social networks"}
|
243
|
-
}
|
244
|
+
},
|
245
|
+
"company" => {"const" => "Awesome Inc."}
|
244
246
|
}
|
245
247
|
},
|
246
248
|
"additionalProperties" => false
|
@@ -265,10 +267,12 @@ The JSON schema can be used for both validation of incoming parameters and for g
|
|
265
267
|
#### Component types
|
266
268
|
|
267
269
|
- `field`: Represents a scalar value (string, integer, boolean, etc.).
|
270
|
+
- `enum`: Represents a property with a limited set of values (enumeration).
|
268
271
|
- `object`: Represents a JSON object with properties.
|
269
272
|
- `array`: Represents a JSON array with scalar items.
|
270
273
|
- `collection`: Represents a array of objects defined manually or by a reference to another request.
|
271
274
|
- `reference`: Represents a reference to another request, allowing you to reuse existing request structures.
|
275
|
+
- `const`: Represents a [constant](https://json-schema.org/understanding-json-schema/reference/const#constant-values) value that is always present in the request.
|
272
276
|
|
273
277
|
#### Helper methods
|
274
278
|
|
@@ -276,6 +280,101 @@ The JSON schema can be used for both validation of incoming parameters and for g
|
|
276
280
|
- `schema_options`: Allows you to set additional options for the JSON Schema, such as `additional_properties` for request or per version. All fields (except `reference`) can be defined with options like `required`, `format`, `min_lenght`, `max_length`, etc. all in snake case.
|
277
281
|
- `with_options`: Allows you to define multiple fields with the same options, reducing repetition.
|
278
282
|
|
283
|
+
#### Required properties
|
284
|
+
|
285
|
+
You can define required properties in your request schema by setting the `required` option to `true`, or provide a list of dependent required properties. This feature is based on the latest [JSON Schema specification](https://json-schema.org/understanding-json-schema/reference/conditionals#dependentRequired), which is also used in OpenAPI 3.1.
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
class DependentRequiredRequest < Verquest::Base
|
289
|
+
description "This is a simple request with nullable properties for testing purposes."
|
290
|
+
|
291
|
+
version "2025-06" do
|
292
|
+
field :name, type: :string, required: true
|
293
|
+
field :credit_card, type: :number, required: %i[billing_address]
|
294
|
+
field :billing_address, type: :string
|
295
|
+
end
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
Will produce this validation schema:
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
{
|
303
|
+
"type" => "object",
|
304
|
+
"description" => "This is a simple request with nullable properties for testing purposes.",
|
305
|
+
"required" => ["name"],
|
306
|
+
"dependentRequired" => {"credit_card" => ["billing_address"]},
|
307
|
+
"properties" => {
|
308
|
+
"name" => {"type" => "string"},
|
309
|
+
"credit_card" => {"type" => "number"},
|
310
|
+
"billing_address" => {"type" => "string"}
|
311
|
+
},
|
312
|
+
"additionalProperties" => false
|
313
|
+
}
|
314
|
+
```
|
315
|
+
|
316
|
+
#### Nullable properties
|
317
|
+
|
318
|
+
You can define nullable properties in your request schema by setting the `nullable` option to `true`. This feature is based on the latest JSON Schema specification, which is also used in OpenAPI 3.1.
|
319
|
+
|
320
|
+
```ruby
|
321
|
+
class NullableRequest < Verquest::Base
|
322
|
+
description "This is a simple request with nullable properties for testing purposes."
|
323
|
+
|
324
|
+
version "2025-06" do
|
325
|
+
with_options nullable: true do
|
326
|
+
array :array, type: :string
|
327
|
+
collection :collection_with_item, item: ReferencedRequest
|
328
|
+
collection :collection_with_object do
|
329
|
+
field :field, type: :string, nullable: false
|
330
|
+
end
|
331
|
+
|
332
|
+
field :field, type: :string
|
333
|
+
|
334
|
+
object :object do
|
335
|
+
field :field, type: :string, nullable: false
|
336
|
+
end
|
337
|
+
|
338
|
+
reference :referenced_object, from: ReferencedRequest
|
339
|
+
reference :referenced_field, from: ReferencedRequest, property: :simple_field
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
```
|
344
|
+
|
345
|
+
Will produce this validation schema:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
{
|
349
|
+
"type" => "object",
|
350
|
+
"description" => "This is a simple request with nullable properties for testing purposes.",
|
351
|
+
"required" => [],
|
352
|
+
"properties" => {
|
353
|
+
"array" => {"type" => %w[array null], "items" => {"type" => "string"}},
|
354
|
+
"collection_with_item" => {"type" => %w[array null], "items" => {"type" => "object", "description" => "This is an another example for testing purposes.", "required" => %w[simple_field nested], "properties" => {"simple_field" => {"type" => "string", "description" => "The simple field"}, "nested" => {"type" => "object", "required" => %w[nested_field_1 nested_field_2], "properties" => {"nested_field_1" => {"type" => "string", "description" => "This is a nested field"}, "nested_field_2" => {"type" => "string", "description" => "This is another nested field"}}, "additionalProperties" => false}}, "additionalProperties" => false}},
|
355
|
+
"collection_with_object" => {"type" => %w[array null], "items" => {"type" => "object", "required" => [], "properties" => {"field" => {"type" => "string"}}, "additionalProperties" => false}},
|
356
|
+
"field" => {"type" => %w[string null]},
|
357
|
+
"object" => {
|
358
|
+
"type" => %w[object null],
|
359
|
+
"required" => [],
|
360
|
+
"properties" => {
|
361
|
+
"field" => {"type" => "string"}
|
362
|
+
},
|
363
|
+
"additionalProperties" => false
|
364
|
+
},
|
365
|
+
"referenced_object" => {
|
366
|
+
"type" => %w[object null],
|
367
|
+
"description" => "This is an another example for testing purposes.",
|
368
|
+
"required" => %w[simple_field nested],
|
369
|
+
"properties" => {"simple_field" => {"type" => "string", "description" => "The simple field"}, "nested" => {"type" => "object", "required" => %w[nested_field_1 nested_field_2], "properties" => {"nested_field_1" => {"type" => "string", "description" => "This is a nested field"}, "nested_field_2" => {"type" => "string", "description" => "This is another nested field"}}, "additionalProperties" => false}},
|
370
|
+
"additionalProperties" => false
|
371
|
+
},
|
372
|
+
"referenced_field" => {"type" => %w[string null], "description" => "The simple field"}
|
373
|
+
},
|
374
|
+
"additionalProperties" => false
|
375
|
+
}
|
376
|
+
```
|
377
|
+
|
279
378
|
#### Custom Field Types
|
280
379
|
|
281
380
|
You can define custom field types that can be used in `field` and `array` in the configuration.
|
@@ -443,9 +542,28 @@ Will be transformed to:
|
|
443
542
|
|
444
543
|
What you can use:
|
445
544
|
- `/` to reference the root of the request structure
|
446
|
-
- `nested
|
545
|
+
- `nested/structure` use slash notation to reference nested structures
|
447
546
|
- if the `map` is not set, the field name will be used as the key in the internal structure
|
448
547
|
|
548
|
+
To get the mapping to map the request structure back to the external API structure, you can use the `external_mapping` method:
|
549
|
+
|
550
|
+
```ruby
|
551
|
+
UserCreateRequest.external_mapping(version: "2025-06")
|
552
|
+
```
|
553
|
+
|
554
|
+
Will produce the following mapping:
|
555
|
+
|
556
|
+
```ruby
|
557
|
+
{
|
558
|
+
"name" => "full_name",
|
559
|
+
"email" => "email",
|
560
|
+
"phone" => "phone",
|
561
|
+
"address_street" => "address/street",
|
562
|
+
"address_city" => "address/city",
|
563
|
+
"address_zip" => "address/postal_code"
|
564
|
+
}
|
565
|
+
```
|
566
|
+
|
449
567
|
There are some limitations and the implementation can be improved, but it should works for most common use cases.
|
450
568
|
|
451
569
|
See the mapping test (in `test/verquest/base_test.rb`) for more examples of mapping.
|
@@ -483,12 +601,15 @@ Verquest.configure do |config|
|
|
483
601
|
|
484
602
|
# Set custom version resolver
|
485
603
|
config.version_resolver = CustomeVersionResolver # default is `Verquest::VersionResolver`
|
604
|
+
|
605
|
+
# Set default value for additional properties
|
606
|
+
config.default_additional_properties = false # default
|
486
607
|
end
|
487
608
|
```
|
488
609
|
|
489
610
|
## Documentation
|
490
611
|
|
491
|
-
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.
|
612
|
+
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.6.0/).
|
492
613
|
|
493
614
|
## Development
|
494
615
|
|
@@ -92,9 +92,9 @@ module Verquest
|
|
92
92
|
camelize(schema_options)
|
93
93
|
|
94
94
|
if current_scope.nil?
|
95
|
-
versions.schema_options
|
95
|
+
versions.schema_options.merge!(schema_options)
|
96
96
|
elsif current_scope.is_a?(Version)
|
97
|
-
current_scope.schema_options
|
97
|
+
current_scope.schema_options.merge!(schema_options)
|
98
98
|
else
|
99
99
|
raise "Additional properties can only be set within a version scope or globally"
|
100
100
|
end
|
@@ -122,38 +122,78 @@ module Verquest
|
|
122
122
|
# @param name [Symbol] The name of the field
|
123
123
|
# @param type [Symbol] The data type of the field
|
124
124
|
# @param map [String, nil] An optional mapping to another field
|
125
|
-
# @param required [Boolean] Whether the field is required
|
125
|
+
# @param required [Boolean, Array<Symbol>] Whether the field is required
|
126
|
+
# @param nullable [Boolean] Whether the field can be null
|
126
127
|
# @param schema_options [Hash] Additional schema options for the field
|
127
128
|
# @return [void]
|
128
|
-
def field(name, type: nil, map: nil, required:
|
129
|
+
def field(name, type: nil, map: nil, required: nil, nullable: nil, **schema_options)
|
129
130
|
camelize(schema_options)
|
130
131
|
|
131
132
|
type = default_options.fetch(:type, type)
|
132
|
-
required = default_options.fetch(:required, required
|
133
|
-
|
133
|
+
required = default_options.fetch(:required, false) if required.nil?
|
134
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
135
|
+
schema_options = default_options.except(:type, :required, :nullable).merge(schema_options)
|
134
136
|
|
135
|
-
field = Properties::Field.new(name:, type:, map:, required:, **schema_options)
|
137
|
+
field = Properties::Field.new(name:, type:, map:, required:, nullable:, **schema_options)
|
136
138
|
current_scope.add(field)
|
137
139
|
end
|
138
140
|
|
141
|
+
# Defines a new enum property for the current version scope
|
142
|
+
#
|
143
|
+
# @param name [Symbol] The name of the enum
|
144
|
+
# @param values [Array] The possible values for the enum
|
145
|
+
# @param map [String, nil] An optional mapping to another property
|
146
|
+
# @param required [Boolean, Array<Symbol>] Whether the enum is required
|
147
|
+
# @param nullable [Boolean] Whether the enum can be null
|
148
|
+
# @param schema_options [Hash] Additional schema options for the enum
|
149
|
+
# @return [void]
|
150
|
+
def enum(name, values:, map: nil, required: nil, nullable: nil, **schema_options)
|
151
|
+
camelize(schema_options)
|
152
|
+
|
153
|
+
required = default_options.fetch(:required, false) if required.nil?
|
154
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
155
|
+
schema_options = default_options.except(:required, :nullable).merge(schema_options)
|
156
|
+
|
157
|
+
enum_property = Properties::Enum.new(name:, values:, map:, required:, nullable:, **schema_options)
|
158
|
+
current_scope.add(enum_property)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Defines a new constant property for the current version scope
|
162
|
+
#
|
163
|
+
# @param name [Symbol] The name of the constant
|
164
|
+
# @param value [Object] The value of the constant
|
165
|
+
# @param map [String, nil] An optional mapping to another constant
|
166
|
+
# @param required [Boolean, Array<Symbol>] Whether the constant is required
|
167
|
+
# @param schema_options [Hash] Additional schema options for the constant
|
168
|
+
# @return [void]
|
169
|
+
def const(name, value:, map: nil, required: nil, **schema_options)
|
170
|
+
camelize(schema_options)
|
171
|
+
required = default_options.fetch(:required, false) if required.nil?
|
172
|
+
|
173
|
+
const = Properties::Const.new(name:, value:, map:, required:, **schema_options)
|
174
|
+
current_scope.add(const)
|
175
|
+
end
|
176
|
+
|
139
177
|
# Defines a new object for the current version scope
|
140
178
|
#
|
141
179
|
# @param name [Symbol] The name of the object
|
142
180
|
# @param map [String, nil] An optional mapping to another object
|
143
|
-
# @param required [Boolean] Whether the object is required
|
181
|
+
# @param required [Boolean, Array<Symbol>] Whether the object is required
|
182
|
+
# @param nullable [Boolean] Whether the object can be null
|
144
183
|
# @param schema_options [Hash] Additional schema options for the object
|
145
184
|
# @yield Block executed in the context of the new object definition
|
146
185
|
# @return [void]
|
147
|
-
def object(name, map: nil, required:
|
186
|
+
def object(name, map: nil, required: nil, nullable: nil, **schema_options, &block)
|
148
187
|
unless block_given?
|
149
188
|
raise ArgumentError, "a block must be given to define the object"
|
150
189
|
end
|
151
190
|
|
152
191
|
camelize(schema_options)
|
153
|
-
required = default_options.fetch(:required, required
|
154
|
-
|
192
|
+
required = default_options.fetch(:required, false) if required.nil?
|
193
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
194
|
+
schema_options = default_options.except(:type, :required, :nullable).merge(schema_options)
|
155
195
|
|
156
|
-
object = Properties::Object.new(name:, map:, required:, **schema_options)
|
196
|
+
object = Properties::Object.new(name:, map:, required:, nullable:, **schema_options)
|
157
197
|
current_scope.add(object)
|
158
198
|
|
159
199
|
if block_given?
|
@@ -170,12 +210,13 @@ module Verquest
|
|
170
210
|
#
|
171
211
|
# @param name [Symbol] The name of the collection
|
172
212
|
# @param item [Class, nil] The item type in the collection
|
173
|
-
# @param required [Boolean] Whether the collection is required
|
213
|
+
# @param required [Boolean, Array<Symbol>] Whether the collection is required
|
214
|
+
# @param nullable [Boolean] Whether the collection can be null
|
174
215
|
# @param map [String, nil] An optional mapping to another collection
|
175
216
|
# @param schema_options [Hash] Additional schema options for the collection
|
176
217
|
# @yield Block executed in the context of the new collection definition
|
177
218
|
# @return [void]
|
178
|
-
def collection(name, item: nil, required:
|
219
|
+
def collection(name, item: nil, required: nil, nullable: nil, map: nil, **schema_options, &block)
|
179
220
|
if item.nil? && !block_given?
|
180
221
|
raise ArgumentError, "item must be provided or a block must be given to define the collection"
|
181
222
|
elsif !item.nil? && !block_given? && !(item <= Verquest::Base)
|
@@ -183,10 +224,11 @@ module Verquest
|
|
183
224
|
end
|
184
225
|
|
185
226
|
camelize(schema_options)
|
186
|
-
required = default_options.fetch(:required, required
|
187
|
-
|
227
|
+
required = default_options.fetch(:required, false) if required.nil?
|
228
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
229
|
+
schema_options = default_options.except(:required, :nullable).merge(schema_options)
|
188
230
|
|
189
|
-
collection = Properties::Collection.new(name:, item:, required:, map:, **schema_options)
|
231
|
+
collection = Properties::Collection.new(name:, item:, required:, nullable:, map:, **schema_options)
|
190
232
|
current_scope.add(collection)
|
191
233
|
|
192
234
|
if block_given?
|
@@ -205,31 +247,35 @@ module Verquest
|
|
205
247
|
# @param from [Verquest::Base] The source of the reference
|
206
248
|
# @param property [Symbol, nil] An optional specific property to reference
|
207
249
|
# @param map [String, nil] An optional mapping to another reference
|
208
|
-
# @param required [Boolean] Whether the reference is required
|
250
|
+
# @param required [Boolean, Array<Symbol>] Whether the reference is required
|
251
|
+
# @param nullable [Boolean] Whether this reference can be null
|
209
252
|
# @return [void]
|
210
|
-
def reference(name, from:, property: nil, map: nil, required:
|
211
|
-
required = default_options.fetch(:required, required
|
253
|
+
def reference(name, from:, property: nil, map: nil, required: nil, nullable: nil)
|
254
|
+
required = default_options.fetch(:required, false) if required.nil?
|
255
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
212
256
|
|
213
|
-
reference = Properties::Reference.new(name:, from:, property:, map:, required:)
|
257
|
+
reference = Properties::Reference.new(name:, from:, property:, map:, required:, nullable:)
|
214
258
|
current_scope.add(reference)
|
215
259
|
end
|
216
260
|
|
217
261
|
# Defines a new array property for the current version scope
|
218
262
|
#
|
219
263
|
# @param name [Symbol] The name of the array property
|
220
|
-
# @param type [
|
221
|
-
# @param required [Boolean] Whether the array property is required
|
264
|
+
# @param type [Symbol] The data type of the array elements
|
265
|
+
# @param required [Boolean, Array<Symbol>] Whether the array property is required
|
266
|
+
# @param nullable [Boolean] Whether this array can be null
|
222
267
|
# @param map [String, nil] An optional mapping to another array property
|
223
268
|
# @param schema_options [Hash] Additional schema options for the array property
|
224
269
|
# @return [void]
|
225
|
-
def array(name, type:, required:
|
270
|
+
def array(name, type:, required: nil, nullable: nil, map: nil, **schema_options)
|
226
271
|
camelize(schema_options)
|
227
272
|
|
228
273
|
type = default_options.fetch(:type, type)
|
229
|
-
required = default_options.fetch(:required, required
|
230
|
-
|
274
|
+
required = default_options.fetch(:required, false) if required.nil?
|
275
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
276
|
+
schema_options = default_options.except(:type, :required, :nullable).merge(schema_options)
|
231
277
|
|
232
|
-
array = Properties::Array.new(name:, type:, required:, map:, **schema_options)
|
278
|
+
array = Properties::Array.new(name:, type:, required:, nullable:, map:, **schema_options)
|
233
279
|
current_scope.add(array)
|
234
280
|
end
|
235
281
|
|
@@ -102,6 +102,18 @@ module Verquest
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
# Returns the external mapping for a specific version
|
106
|
+
#
|
107
|
+
# This method returns a mapping hash that translates from internal attribute names back to external parameter names.
|
108
|
+
#
|
109
|
+
# @param version [String, nil] Specific version to use, defaults to configuration setting
|
110
|
+
# @return [Hash] The inverted mapping configuration where keys are internal names and values are external names
|
111
|
+
# @see #mapping
|
112
|
+
def external_mapping(version: nil)
|
113
|
+
version = resolve(version)
|
114
|
+
version.external_mapping
|
115
|
+
end
|
116
|
+
|
105
117
|
# Returns the JSON reference for the request or a specific property
|
106
118
|
#
|
107
119
|
# @param property [String, Symbol, nil] Specific property to retrieve reference for
|
@@ -52,7 +52,12 @@ module Verquest
|
|
52
52
|
# @!attribute [rw] insert_property_defaults
|
53
53
|
# Controls whether default values defined in property schemas should be inserted when not provided during validation
|
54
54
|
# @return [Boolean] true if default values should be inserted, false otherwise
|
55
|
-
|
55
|
+
#
|
56
|
+
# @!attribute [rw] default_additional_properties
|
57
|
+
# Controls the default behavior for handling properties not defined in the schema
|
58
|
+
# @return [Boolean] false to disallow additional properties (default), true to allow them
|
59
|
+
attr_accessor :validate_params, :json_schema_version, :validation_error_handling,
|
60
|
+
:remove_extra_root_keys, :insert_property_defaults, :default_additional_properties
|
56
61
|
|
57
62
|
# @!attribute [r] current_version
|
58
63
|
# A callable object that returns the current API version to use when not explicitly specified
|
@@ -73,11 +78,12 @@ module Verquest
|
|
73
78
|
def initialize
|
74
79
|
@validate_params = true
|
75
80
|
@json_schema_version = :draft2020_12
|
76
|
-
@validation_error_handling = :raise
|
81
|
+
@validation_error_handling = :raise
|
77
82
|
@remove_extra_root_keys = true
|
78
83
|
@version_resolver = VersionResolver
|
79
84
|
@insert_property_defaults = true
|
80
85
|
@custom_field_types = {}
|
86
|
+
@default_additional_properties = false
|
81
87
|
end
|
82
88
|
|
83
89
|
# Sets the current version strategy using a callable object
|
data/lib/verquest/gem_version.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verquest
|
4
|
+
# HelperMethods module provides utility methods for Verquest
|
5
|
+
module HelperMethods
|
6
|
+
# Module that provides methods for working with required properties in schemas
|
7
|
+
#
|
8
|
+
# This module offers functionality to identify and categorize properties based on
|
9
|
+
# their required status within schema definitions. It distinguishes between
|
10
|
+
# unconditionally required properties (those marked with `required: true`) and
|
11
|
+
# conditionally required properties (those with dependencies expressed as arrays).
|
12
|
+
#
|
13
|
+
# When included in classes that manage properties (like Version or Base property classes),
|
14
|
+
# it provides methods to extract both types of required properties which can be used
|
15
|
+
# for schema validation, documentation generation, or UI rendering.
|
16
|
+
module RequiredProperties
|
17
|
+
# Returns all properties that are unconditionally required
|
18
|
+
#
|
19
|
+
# This method identifies properties that must always be present in valid data,
|
20
|
+
# by selecting those with their required attribute set to true (boolean).
|
21
|
+
# Results are memoized to avoid recalculating on subsequent calls.
|
22
|
+
#
|
23
|
+
# @return [Array<Symbol>] Names of properties marked as unconditionally required (required == true)
|
24
|
+
def required_properties
|
25
|
+
@_required_properties ||= properties.values.select { _1.required == true }.map(&:name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns properties that are conditionally required based on other properties
|
29
|
+
#
|
30
|
+
# This method identifies properties that are required only when certain other
|
31
|
+
# properties are present. These are properties where the required attribute
|
32
|
+
# is an array of dependency names rather than a boolean.
|
33
|
+
# Results are memoized to avoid recalculating on subsequent calls.
|
34
|
+
#
|
35
|
+
# @return [Hash<String, Array<String>>] Hash mapping property names to their dependency arrays
|
36
|
+
# @example Return value format:
|
37
|
+
# {
|
38
|
+
# "property_name1": ["dependency1", "dependency2"],
|
39
|
+
# "property_name2": ["dependency3"]
|
40
|
+
# }
|
41
|
+
def dependent_required_properties
|
42
|
+
@_dependent_required_properties ||= properties.values.select { _1.required.is_a?(Array) }.each_with_object({}) do |property, hash|
|
43
|
+
hash[property.name] = property.required.map(&:to_s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|