verquest 0.4.0 → 0.5.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 +12 -0
- data/README.md +102 -4
- data/lib/verquest/base/private_class_methods.rb +37 -27
- 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 +15 -7
- data/lib/verquest/properties/base.rb +6 -0
- data/lib/verquest/properties/collection.rb +27 -13
- data/lib/verquest/properties/field.rb +11 -3
- data/lib/verquest/properties/object.rb +25 -9
- data/lib/verquest/properties/reference.rb +25 -6
- data/lib/verquest/version.rb +16 -4
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9349b2773c4a03024805d78257b1e763ffb87b87f27726620e343395c683aa0
|
4
|
+
data.tar.gz: ec2b70a4a10c3314ca27283776138f85b53b245d88869de39e450ca2eb2064de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d0042f959285f4253c7802166ab64d53b7013d765bfe954fd68a8ab46bb59f162512a285aca11c77e86a8e6b5ce36445f47e4e9530fcaa0010e8c6cadcefdf2
|
7
|
+
data.tar.gz: 06bbfceb1c989e1e3eabb20e495790ac35934e1cb4687e71960315c3e64d3a30b42b345c03d19d32be921b75550a6fef002d8ba44018ded8f7c9a3af73d920f0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.5.0] - 2025-07-01
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
- Handling `with_options` defaults like required and nullable.
|
7
|
+
|
8
|
+
### New Features
|
9
|
+
- Add `default_additional_properties` option to configuration.
|
10
|
+
- Add support for nullable properties (`nullable: true`) based on the latest JSON Schema specification, which is also used in OpenAPI 3.1.
|
11
|
+
- Add support for `dependentRequired` (see https://json-schema.org/understanding-json-schema/reference/conditionals#dependentRequired).
|
12
|
+
|
13
|
+
## [0.4.0] - 2025-06-28
|
14
|
+
|
3
15
|
### Breaking Changes
|
4
16
|
- **BREAKING:** Renaming validation method from `validate_schema` to `valid_schema?` to better reflect its purpose.
|
5
17
|
- **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
@@ -19,7 +19,7 @@ Verquest is a Ruby gem that offers an elegant solution for versioning API reques
|
|
19
19
|
Add this line to your application's Gemfile:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
gem "verquest", "~> 0.
|
22
|
+
gem "verquest", "~> 0.5"
|
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
|
|
@@ -135,7 +135,7 @@ Output:
|
|
135
135
|
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
136
136
|
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
137
137
|
"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"},
|
138
|
+
"birth_date" => {"type" => ["string", "null"], "format" => "date", "description" => "The birth date of the user"},
|
139
139
|
"address" => {"$ref" => "#/components/schemas/AddressCreateRequest"},
|
140
140
|
"permissions" => {
|
141
141
|
"type" => "array",
|
@@ -276,6 +276,101 @@ The JSON schema can be used for both validation of incoming parameters and for g
|
|
276
276
|
- `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
277
|
- `with_options`: Allows you to define multiple fields with the same options, reducing repetition.
|
278
278
|
|
279
|
+
### Required properties
|
280
|
+
|
281
|
+
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.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
class DependentRequiredRequest < Verquest::Base
|
285
|
+
description "This is a simple request with nullable properties for testing purposes."
|
286
|
+
|
287
|
+
version "2025-06" do
|
288
|
+
field :name, type: :string, required: true
|
289
|
+
field :credit_card, type: :number, required: %i[billing_address]
|
290
|
+
field :billing_address, type: :string
|
291
|
+
end
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
295
|
+
Will produce this validation schema:
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
{
|
299
|
+
"type" => "object",
|
300
|
+
"description" => "This is a simple request with nullable properties for testing purposes.",
|
301
|
+
"required" => ["name"],
|
302
|
+
"dependentRequired" => {"credit_card" => ["billing_address"]},
|
303
|
+
"properties" => {
|
304
|
+
"name" => {"type" => "string"},
|
305
|
+
"credit_card" => {"type" => "number"},
|
306
|
+
"billing_address" => {"type" => "string"}
|
307
|
+
},
|
308
|
+
"additionalProperties" => false
|
309
|
+
}
|
310
|
+
```
|
311
|
+
|
312
|
+
#### Nullable properties
|
313
|
+
|
314
|
+
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.
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
class NullableRequest < Verquest::Base
|
318
|
+
description "This is a simple request with nullable properties for testing purposes."
|
319
|
+
|
320
|
+
version "2025-06" do
|
321
|
+
with_options nullable: true do
|
322
|
+
array :array, type: :string
|
323
|
+
collection :collection_with_item, item: ReferencedRequest
|
324
|
+
collection :collection_with_object do
|
325
|
+
field :field, type: :string, nullable: false
|
326
|
+
end
|
327
|
+
|
328
|
+
field :field, type: :string
|
329
|
+
|
330
|
+
object :object do
|
331
|
+
field :field, type: :string, nullable: false
|
332
|
+
end
|
333
|
+
|
334
|
+
reference :referenced_object, from: ReferencedRequest
|
335
|
+
reference :referenced_field, from: ReferencedRequest, property: :simple_field
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
```
|
340
|
+
|
341
|
+
Will produce this validation schema:
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
{
|
345
|
+
"type" => "object",
|
346
|
+
"description" => "This is a simple request with nullable properties for testing purposes.",
|
347
|
+
"required" => [],
|
348
|
+
"properties" => {
|
349
|
+
"array" => {"type" => %w[array null], "items" => {"type" => "string"}},
|
350
|
+
"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}},
|
351
|
+
"collection_with_object" => {"type" => %w[array null], "items" => {"type" => "object", "required" => [], "properties" => {"field" => {"type" => "string"}}, "additionalProperties" => false}},
|
352
|
+
"field" => {"type" => %w[string null]},
|
353
|
+
"object" => {
|
354
|
+
"type" => %w[object null],
|
355
|
+
"required" => [],
|
356
|
+
"properties" => {
|
357
|
+
"field" => {"type" => "string"}
|
358
|
+
},
|
359
|
+
"additionalProperties" => false
|
360
|
+
},
|
361
|
+
"referenced_object" => {
|
362
|
+
"type" => %w[object null],
|
363
|
+
"description" => "This is an another example for testing purposes.",
|
364
|
+
"required" => %w[simple_field nested],
|
365
|
+
"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}},
|
366
|
+
"additionalProperties" => false
|
367
|
+
},
|
368
|
+
"referenced_field" => {"type" => %w[string null], "description" => "The simple field"}
|
369
|
+
},
|
370
|
+
"additionalProperties" => false
|
371
|
+
}
|
372
|
+
```
|
373
|
+
|
279
374
|
#### Custom Field Types
|
280
375
|
|
281
376
|
You can define custom field types that can be used in `field` and `array` in the configuration.
|
@@ -483,12 +578,15 @@ Verquest.configure do |config|
|
|
483
578
|
|
484
579
|
# Set custom version resolver
|
485
580
|
config.version_resolver = CustomeVersionResolver # default is `Verquest::VersionResolver`
|
581
|
+
|
582
|
+
# Set default value for additional properties
|
583
|
+
config.default_additional_properties = false # default
|
486
584
|
end
|
487
585
|
```
|
488
586
|
|
489
587
|
## Documentation
|
490
588
|
|
491
|
-
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.
|
589
|
+
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.5.0/).
|
492
590
|
|
493
591
|
## Development
|
494
592
|
|
@@ -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,17 +122,19 @@ 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
|
|
@@ -140,20 +142,22 @@ module Verquest
|
|
140
142
|
#
|
141
143
|
# @param name [Symbol] The name of the object
|
142
144
|
# @param map [String, nil] An optional mapping to another object
|
143
|
-
# @param required [Boolean] Whether the object is required
|
145
|
+
# @param required [Boolean, Array<Symbol>] Whether the object is required
|
146
|
+
# @param nullable [Boolean] Whether the object can be null
|
144
147
|
# @param schema_options [Hash] Additional schema options for the object
|
145
148
|
# @yield Block executed in the context of the new object definition
|
146
149
|
# @return [void]
|
147
|
-
def object(name, map: nil, required:
|
150
|
+
def object(name, map: nil, required: nil, nullable: nil, **schema_options, &block)
|
148
151
|
unless block_given?
|
149
152
|
raise ArgumentError, "a block must be given to define the object"
|
150
153
|
end
|
151
154
|
|
152
155
|
camelize(schema_options)
|
153
|
-
required = default_options.fetch(:required, required
|
154
|
-
|
156
|
+
required = default_options.fetch(:required, false) if required.nil?
|
157
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
158
|
+
schema_options = default_options.except(:type, :required, :nullable).merge(schema_options)
|
155
159
|
|
156
|
-
object = Properties::Object.new(name:, map:, required:, **schema_options)
|
160
|
+
object = Properties::Object.new(name:, map:, required:, nullable:, **schema_options)
|
157
161
|
current_scope.add(object)
|
158
162
|
|
159
163
|
if block_given?
|
@@ -170,12 +174,13 @@ module Verquest
|
|
170
174
|
#
|
171
175
|
# @param name [Symbol] The name of the collection
|
172
176
|
# @param item [Class, nil] The item type in the collection
|
173
|
-
# @param required [Boolean] Whether the collection is required
|
177
|
+
# @param required [Boolean, Array<Symbol>] Whether the collection is required
|
178
|
+
# @param nullable [Boolean] Whether the collection can be null
|
174
179
|
# @param map [String, nil] An optional mapping to another collection
|
175
180
|
# @param schema_options [Hash] Additional schema options for the collection
|
176
181
|
# @yield Block executed in the context of the new collection definition
|
177
182
|
# @return [void]
|
178
|
-
def collection(name, item: nil, required:
|
183
|
+
def collection(name, item: nil, required: nil, nullable: nil, map: nil, **schema_options, &block)
|
179
184
|
if item.nil? && !block_given?
|
180
185
|
raise ArgumentError, "item must be provided or a block must be given to define the collection"
|
181
186
|
elsif !item.nil? && !block_given? && !(item <= Verquest::Base)
|
@@ -183,10 +188,11 @@ module Verquest
|
|
183
188
|
end
|
184
189
|
|
185
190
|
camelize(schema_options)
|
186
|
-
required = default_options.fetch(:required, required
|
187
|
-
|
191
|
+
required = default_options.fetch(:required, false) if required.nil?
|
192
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
193
|
+
schema_options = default_options.except(:required, :nullable).merge(schema_options)
|
188
194
|
|
189
|
-
collection = Properties::Collection.new(name:, item:, required:, map:, **schema_options)
|
195
|
+
collection = Properties::Collection.new(name:, item:, required:, nullable:, map:, **schema_options)
|
190
196
|
current_scope.add(collection)
|
191
197
|
|
192
198
|
if block_given?
|
@@ -205,31 +211,35 @@ module Verquest
|
|
205
211
|
# @param from [Verquest::Base] The source of the reference
|
206
212
|
# @param property [Symbol, nil] An optional specific property to reference
|
207
213
|
# @param map [String, nil] An optional mapping to another reference
|
208
|
-
# @param required [Boolean] Whether the reference is required
|
214
|
+
# @param required [Boolean, Array<Symbol>] Whether the reference is required
|
215
|
+
# @param nullable [Boolean] Whether this reference can be null
|
209
216
|
# @return [void]
|
210
|
-
def reference(name, from:, property: nil, map: nil, required:
|
211
|
-
required = default_options.fetch(:required, required
|
217
|
+
def reference(name, from:, property: nil, map: nil, required: nil, nullable: nil)
|
218
|
+
required = default_options.fetch(:required, false) if required.nil?
|
219
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
212
220
|
|
213
|
-
reference = Properties::Reference.new(name:, from:, property:, map:, required:)
|
221
|
+
reference = Properties::Reference.new(name:, from:, property:, map:, required:, nullable:)
|
214
222
|
current_scope.add(reference)
|
215
223
|
end
|
216
224
|
|
217
225
|
# Defines a new array property for the current version scope
|
218
226
|
#
|
219
227
|
# @param name [Symbol] The name of the array property
|
220
|
-
# @param type [
|
221
|
-
# @param required [Boolean] Whether the array property is required
|
228
|
+
# @param type [Symbol] The data type of the array elements
|
229
|
+
# @param required [Boolean, Array<Symbol>] Whether the array property is required
|
230
|
+
# @param nullable [Boolean] Whether this array can be null
|
222
231
|
# @param map [String, nil] An optional mapping to another array property
|
223
232
|
# @param schema_options [Hash] Additional schema options for the array property
|
224
233
|
# @return [void]
|
225
|
-
def array(name, type:, required:
|
234
|
+
def array(name, type:, required: nil, nullable: nil, map: nil, **schema_options)
|
226
235
|
camelize(schema_options)
|
227
236
|
|
228
237
|
type = default_options.fetch(:type, type)
|
229
|
-
required = default_options.fetch(:required, required
|
230
|
-
|
238
|
+
required = default_options.fetch(:required, false) if required.nil?
|
239
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
240
|
+
schema_options = default_options.except(:type, :required, :nullable).merge(schema_options)
|
231
241
|
|
232
|
-
array = Properties::Array.new(name:, type:, required:, map:, **schema_options)
|
242
|
+
array = Properties::Array.new(name:, type:, required:, nullable:, map:, **schema_options)
|
233
243
|
current_scope.add(array)
|
234
244
|
end
|
235
245
|
|
@@ -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
|
@@ -20,31 +20,39 @@ module Verquest
|
|
20
20
|
# @param name [String, Symbol] The name of the property
|
21
21
|
# @param type [String, Symbol] The type of items in the array, can be a default type or a custom field type
|
22
22
|
# @param map [String, nil] The mapping path for this property (nil for no explicit mapping)
|
23
|
-
# @param required [Boolean] Whether this property is required
|
23
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names
|
24
|
+
# @param nullable [Boolean] Whether this property can be null
|
24
25
|
# @param item_schema_options [Hash] Additional JSON schema options for the array items (merged with custom type options)
|
25
26
|
# @param schema_options [Hash] Additional JSON schema options for the array property itself
|
26
27
|
# @raise [ArgumentError] If type is not one of the allowed types (default or custom)
|
27
28
|
# @raise [ArgumentError] If attempting to map an array to the root
|
28
|
-
def initialize(name:, type:, map: nil, required: false, item_schema_options: {}, **schema_options)
|
29
|
+
def initialize(name:, type:, map: nil, required: false, nullable: false, item_schema_options: {}, **schema_options)
|
29
30
|
raise ArgumentError, "Type must be one of #{allowed_types.join(", ")}" unless allowed_types.include?(type.to_s)
|
30
31
|
raise ArgumentError, "You can not map array to the root" if map == "/"
|
31
32
|
|
32
33
|
if (custom_type = Verquest.configuration.custom_field_types[type.to_sym])
|
33
|
-
@
|
34
|
+
@item_type = custom_type[:type].to_s
|
34
35
|
@item_schema_options = if custom_type.key?(:schema_options)
|
35
36
|
custom_type[:schema_options].merge(item_schema_options).transform_keys(&:to_s)
|
36
37
|
else
|
37
38
|
item_schema_options.transform_keys(&:to_s)
|
38
39
|
end
|
39
40
|
else
|
40
|
-
@
|
41
|
+
@item_type = type.to_s
|
41
42
|
@item_schema_options = item_schema_options.transform_keys(&:to_s)
|
42
43
|
end
|
43
44
|
|
44
45
|
@name = name.to_s
|
45
46
|
@map = map
|
46
47
|
@required = required
|
48
|
+
@nullable = nullable
|
47
49
|
@schema_options = schema_options&.transform_keys(&:to_s)
|
50
|
+
|
51
|
+
@type = if nullable
|
52
|
+
%w[array null]
|
53
|
+
else
|
54
|
+
"array"
|
55
|
+
end
|
48
56
|
end
|
49
57
|
|
50
58
|
# Generate JSON schema definition for this array property
|
@@ -53,8 +61,8 @@ module Verquest
|
|
53
61
|
def to_schema
|
54
62
|
{
|
55
63
|
name => {
|
56
|
-
"type" =>
|
57
|
-
"items" => {"type" =>
|
64
|
+
"type" => type,
|
65
|
+
"items" => {"type" => item_type}.merge(item_schema_options)
|
58
66
|
}.merge(schema_options)
|
59
67
|
}
|
60
68
|
end
|
@@ -72,7 +80,7 @@ module Verquest
|
|
72
80
|
|
73
81
|
private
|
74
82
|
|
75
|
-
attr_reader :type, :schema_options, :item_schema_options
|
83
|
+
attr_reader :type, :item_type, :schema_options, :item_schema_options
|
76
84
|
|
77
85
|
# Gets the list of allowed item types, including both default and custom types
|
78
86
|
#
|
@@ -10,6 +10,8 @@ module Verquest
|
|
10
10
|
#
|
11
11
|
# @abstract Subclass and override {#to_schema}, {#mapping} to implement
|
12
12
|
class Base
|
13
|
+
include HelperMethods::RequiredProperties
|
14
|
+
|
13
15
|
# @!attribute [rw] name
|
14
16
|
# @return [String] The name of the property
|
15
17
|
# @!attribute [rw] required
|
@@ -55,6 +57,10 @@ module Verquest
|
|
55
57
|
|
56
58
|
private
|
57
59
|
|
60
|
+
# @!attribute [r] nullable
|
61
|
+
# @return [Boolean] Whether this property can be null
|
62
|
+
attr_reader :nullable
|
63
|
+
|
58
64
|
# Determines the mapping target key based on mapping configuration
|
59
65
|
# @param value_prefix [Array<String>] Prefix for the target value
|
60
66
|
# @param collection [Boolean] Whether this is a collection mapping
|
@@ -22,11 +22,12 @@ module Verquest
|
|
22
22
|
#
|
23
23
|
# @param name [String, Symbol] The name of the property
|
24
24
|
# @param item [Verquest::Base, nil] Optional reference to an external schema class
|
25
|
-
# @param required [Boolean] Whether this property is required
|
25
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names
|
26
|
+
# @param nullable [Boolean] Whether this property can be null
|
26
27
|
# @param map [String, nil] The mapping path for this property
|
27
28
|
# @param schema_options [Hash] Additional JSON schema options for this property
|
28
29
|
# @raise [ArgumentError] If attempting to map a collection to the root
|
29
|
-
def initialize(name:, item: nil, required: false, map: nil, **schema_options)
|
30
|
+
def initialize(name:, item: nil, required: false, nullable: false, map: nil, **schema_options)
|
30
31
|
raise ArgumentError, "You can not map collection to the root" if map == "/"
|
31
32
|
|
32
33
|
@properties = {}
|
@@ -34,8 +35,15 @@ module Verquest
|
|
34
35
|
@name = name.to_s
|
35
36
|
@item = item
|
36
37
|
@required = required
|
38
|
+
@nullable = nullable
|
37
39
|
@map = map
|
38
40
|
@schema_options = schema_options&.transform_keys(&:to_s)
|
41
|
+
|
42
|
+
@type = if nullable
|
43
|
+
%w[array null]
|
44
|
+
else
|
45
|
+
"array"
|
46
|
+
end
|
39
47
|
end
|
40
48
|
|
41
49
|
# Add a child property to this collection's item definition
|
@@ -60,7 +68,7 @@ module Verquest
|
|
60
68
|
if has_item?
|
61
69
|
{
|
62
70
|
name => {
|
63
|
-
"type" =>
|
71
|
+
"type" => type,
|
64
72
|
"items" => {
|
65
73
|
"$ref" => item.to_ref
|
66
74
|
}
|
@@ -69,12 +77,15 @@ module Verquest
|
|
69
77
|
else
|
70
78
|
{
|
71
79
|
name => {
|
72
|
-
"type" =>
|
80
|
+
"type" => type,
|
73
81
|
"items" => {
|
74
82
|
"type" => "object",
|
75
|
-
"required" =>
|
76
|
-
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
77
|
-
|
83
|
+
"required" => required_properties,
|
84
|
+
"properties" => properties.transform_values { |property| property.to_schema[property.name] },
|
85
|
+
"additionalProperties" => Verquest.configuration.default_additional_properties
|
86
|
+
}.tap do |schema|
|
87
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
88
|
+
end
|
78
89
|
}.merge(schema_options)
|
79
90
|
}
|
80
91
|
end
|
@@ -88,19 +99,22 @@ module Verquest
|
|
88
99
|
if has_item?
|
89
100
|
{
|
90
101
|
name => {
|
91
|
-
"type" =>
|
102
|
+
"type" => type,
|
92
103
|
"items" => item.to_validation_schema(version: version)
|
93
104
|
}.merge(schema_options)
|
94
105
|
}
|
95
106
|
else
|
96
107
|
{
|
97
108
|
name => {
|
98
|
-
"type" =>
|
109
|
+
"type" => type,
|
99
110
|
"items" => {
|
100
111
|
"type" => "object",
|
101
|
-
"required" =>
|
102
|
-
"properties" => properties.transform_values { |property| property.to_validation_schema(version: version)[property.name] }
|
103
|
-
|
112
|
+
"required" => required_properties,
|
113
|
+
"properties" => properties.transform_values { |property| property.to_validation_schema(version: version)[property.name] },
|
114
|
+
"additionalProperties" => Verquest.configuration.default_additional_properties
|
115
|
+
}.tap do |schema|
|
116
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
117
|
+
end
|
104
118
|
}.merge(schema_options)
|
105
119
|
}
|
106
120
|
end
|
@@ -141,7 +155,7 @@ module Verquest
|
|
141
155
|
|
142
156
|
private
|
143
157
|
|
144
|
-
attr_reader :item, :schema_options, :properties
|
158
|
+
attr_reader :item, :schema_options, :properties, :type
|
145
159
|
end
|
146
160
|
end
|
147
161
|
end
|
@@ -24,12 +24,13 @@ module Verquest
|
|
24
24
|
#
|
25
25
|
# @param name [String, Symbol] The name of the property
|
26
26
|
# @param type [String, Symbol] The data type for this field, can be a default type or a custom field type
|
27
|
-
# @param required [Boolean] Whether this property is required (overridden by custom type
|
27
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names (can be overridden by custom type)
|
28
|
+
# @param nullable [Boolean] Whether this property can be null
|
28
29
|
# @param map [String, nil] The mapping path for this property
|
29
30
|
# @param schema_options [Hash] Additional JSON schema options for this property (merged with custom type options)
|
30
31
|
# @raise [ArgumentError] If type is not one of the allowed types (default or custom)
|
31
32
|
# @raise [ArgumentError] If attempting to map a field to root without a name
|
32
|
-
def initialize(name:, type:, required: false, map: nil, **schema_options)
|
33
|
+
def initialize(name:, type:, required: false, nullable: false, map: nil, **schema_options)
|
33
34
|
raise ArgumentError, "Type must be one of #{allowed_types.join(", ")}" unless allowed_types.include?(type.to_s)
|
34
35
|
raise ArgumentError, "You can not map fields to the root without a name" if map == "/"
|
35
36
|
|
@@ -48,14 +49,21 @@ module Verquest
|
|
48
49
|
end
|
49
50
|
|
50
51
|
@name = name.to_s
|
52
|
+
@nullable = nullable
|
51
53
|
@map = map
|
54
|
+
|
55
|
+
if nullable
|
56
|
+
@type = [@type, "null"]
|
57
|
+
end
|
52
58
|
end
|
53
59
|
|
54
60
|
# Generate JSON schema definition for this field
|
55
61
|
#
|
56
62
|
# @return [Hash] The schema definition for this field
|
57
63
|
def to_schema
|
58
|
-
{
|
64
|
+
{
|
65
|
+
name => {"type" => type}.merge(schema_options)
|
66
|
+
}
|
59
67
|
end
|
60
68
|
|
61
69
|
# Create mapping for this field property
|
@@ -15,16 +15,28 @@ module Verquest
|
|
15
15
|
# Initialize a new Object property
|
16
16
|
#
|
17
17
|
# @param name [String, Symbol] The name of the property
|
18
|
-
# @param required [Boolean] Whether this property is required
|
18
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names
|
19
|
+
# @param nullable [Boolean] Whether this property can be null
|
19
20
|
# @param map [String, nil] The mapping path for this property
|
20
21
|
# @param schema_options [Hash] Additional JSON schema options for this property
|
21
|
-
def initialize(name:, required: false, map: nil, **schema_options)
|
22
|
+
def initialize(name:, required: false, nullable: false, map: nil, **schema_options)
|
22
23
|
@properties = {}
|
23
24
|
|
24
25
|
@name = name.to_s
|
25
26
|
@required = required
|
27
|
+
@nullable = nullable
|
26
28
|
@map = map
|
27
|
-
@schema_options =
|
29
|
+
@schema_options = {
|
30
|
+
additionalProperties: Verquest.configuration.default_additional_properties
|
31
|
+
}.merge(schema_options)
|
32
|
+
.delete_if { |_, v| v.nil? }
|
33
|
+
.transform_keys(&:to_s)
|
34
|
+
|
35
|
+
@type = if nullable
|
36
|
+
%w[object null]
|
37
|
+
else
|
38
|
+
"object"
|
39
|
+
end
|
28
40
|
end
|
29
41
|
|
30
42
|
# Add a child property to this object
|
@@ -41,10 +53,12 @@ module Verquest
|
|
41
53
|
def to_schema
|
42
54
|
{
|
43
55
|
name => {
|
44
|
-
"type" =>
|
45
|
-
"required" =>
|
56
|
+
"type" => type,
|
57
|
+
"required" => required_properties,
|
46
58
|
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
47
|
-
}.merge(schema_options)
|
59
|
+
}.merge(schema_options).tap do |schema|
|
60
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
61
|
+
end
|
48
62
|
}
|
49
63
|
end
|
50
64
|
|
@@ -55,10 +69,12 @@ module Verquest
|
|
55
69
|
def to_validation_schema(version: nil)
|
56
70
|
{
|
57
71
|
name => {
|
58
|
-
"type" =>
|
59
|
-
"required" =>
|
72
|
+
"type" => type,
|
73
|
+
"required" => required_properties,
|
60
74
|
"properties" => properties.transform_values { |property| property.to_validation_schema(version: version)[property.name] }
|
61
|
-
}.merge(schema_options)
|
75
|
+
}.merge(schema_options).tap do |schema|
|
76
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
77
|
+
end
|
62
78
|
}
|
63
79
|
end
|
64
80
|
|
@@ -26,12 +26,14 @@ module Verquest
|
|
26
26
|
# @param name [String, Symbol] The name of the property
|
27
27
|
# @param from [Class] The schema class to reference
|
28
28
|
# @param property [Symbol, nil] Optional specific property to reference
|
29
|
+
# @param nullable [Boolean] Whether this property can be null
|
29
30
|
# @param map [String, nil] The mapping path for this property
|
30
|
-
# @param required [Boolean] Whether this property is required
|
31
|
-
def initialize(name:, from:, property: nil, map: nil, required: false)
|
31
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names
|
32
|
+
def initialize(name:, from:, property: nil, nullable: false, map: nil, required: false)
|
32
33
|
@name = name.to_s
|
33
34
|
@from = from
|
34
35
|
@property = property
|
36
|
+
@nullable = nullable
|
35
37
|
@map = map
|
36
38
|
@required = required
|
37
39
|
end
|
@@ -40,9 +42,20 @@ module Verquest
|
|
40
42
|
#
|
41
43
|
# @return [Hash] The schema definition with a $ref pointer
|
42
44
|
def to_schema
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
if nullable
|
46
|
+
{
|
47
|
+
name => {
|
48
|
+
"oneOf" => [
|
49
|
+
{"$ref" => from.to_ref(property: property)},
|
50
|
+
{"type" => "null"}
|
51
|
+
]
|
52
|
+
}
|
53
|
+
}
|
54
|
+
else
|
55
|
+
{
|
56
|
+
name => {"$ref" => from.to_ref(property: property)}
|
57
|
+
}
|
58
|
+
end
|
46
59
|
end
|
47
60
|
|
48
61
|
# Generate validation schema for this reference property
|
@@ -50,8 +63,14 @@ module Verquest
|
|
50
63
|
# @param version [String, nil] The version to generate validation schema for
|
51
64
|
# @return [Hash] The validation schema for this reference
|
52
65
|
def to_validation_schema(version: nil)
|
66
|
+
schema = from.to_validation_schema(version:, property: property).dup
|
67
|
+
|
68
|
+
if nullable
|
69
|
+
schema["type"] = [schema["type"], "null"] unless schema["type"].include?("null")
|
70
|
+
end
|
71
|
+
|
53
72
|
{
|
54
|
-
name =>
|
73
|
+
name => schema
|
55
74
|
}
|
56
75
|
end
|
57
76
|
|
data/lib/verquest/version.rb
CHANGED
@@ -19,6 +19,8 @@ module Verquest
|
|
19
19
|
# # Get mapping
|
20
20
|
# mapping = version.mapping
|
21
21
|
class Version
|
22
|
+
include HelperMethods::RequiredProperties
|
23
|
+
|
22
24
|
# @!attribute [r] name
|
23
25
|
# @return [String] The name/identifier of the version (e.g., "2023-01")
|
24
26
|
#
|
@@ -102,6 +104,12 @@ module Verquest
|
|
102
104
|
def prepare
|
103
105
|
return if frozen?
|
104
106
|
|
107
|
+
unless schema_options.key?("additionalProperties")
|
108
|
+
schema_options["additionalProperties"] = Verquest.configuration.default_additional_properties
|
109
|
+
end
|
110
|
+
|
111
|
+
schema_options.delete_if { |_, v| v.nil? }
|
112
|
+
|
105
113
|
prepare_schema
|
106
114
|
prepare_validation_schema
|
107
115
|
prepare_mapping
|
@@ -197,9 +205,11 @@ module Verquest
|
|
197
205
|
@schema = {
|
198
206
|
"type" => "object",
|
199
207
|
"description" => description,
|
200
|
-
"required" =>
|
208
|
+
"required" => required_properties,
|
201
209
|
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
202
|
-
}.merge(schema_options).
|
210
|
+
}.merge(schema_options).tap do |schema|
|
211
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
212
|
+
end.freeze
|
203
213
|
end
|
204
214
|
|
205
215
|
# Generates the validation schema for this version
|
@@ -212,9 +222,11 @@ module Verquest
|
|
212
222
|
@validation_schema = {
|
213
223
|
"type" => "object",
|
214
224
|
"description" => description,
|
215
|
-
"required" =>
|
225
|
+
"required" => required_properties,
|
216
226
|
"properties" => properties.transform_values { |property| property.to_validation_schema(version: name)[property.name] }
|
217
|
-
}.merge(schema_options).
|
227
|
+
}.merge(schema_options).tap do |schema|
|
228
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
229
|
+
end.freeze
|
218
230
|
end
|
219
231
|
|
220
232
|
# Prepares the parameter mapping for this version
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verquest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Hlavicka
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: zeitwerk
|
@@ -62,6 +61,7 @@ files:
|
|
62
61
|
- lib/verquest/base/public_class_methods.rb
|
63
62
|
- lib/verquest/configuration.rb
|
64
63
|
- lib/verquest/gem_version.rb
|
64
|
+
- lib/verquest/helper_methods/required_properties.rb
|
65
65
|
- lib/verquest/properties.rb
|
66
66
|
- lib/verquest/properties/array.rb
|
67
67
|
- lib/verquest/properties/base.rb
|
@@ -81,7 +81,6 @@ metadata:
|
|
81
81
|
homepage_uri: https://github.com/CiTroNaK/verquest
|
82
82
|
source_code_uri: https://github.com/CiTroNaK/verquest
|
83
83
|
changelog_uri: https://github.com/CiTroNaK/verquest/blob/main/CHANGELOG.md
|
84
|
-
post_install_message:
|
85
84
|
rdoc_options: []
|
86
85
|
require_paths:
|
87
86
|
- lib
|
@@ -96,8 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
95
|
- !ruby/object:Gem::Version
|
97
96
|
version: '0'
|
98
97
|
requirements: []
|
99
|
-
rubygems_version: 3.
|
100
|
-
signing_key:
|
98
|
+
rubygems_version: 3.6.9
|
101
99
|
specification_version: 4
|
102
100
|
summary: Verquest is a Ruby gem that offers an elegant solution for versioning API
|
103
101
|
requests
|