verquest 0.2.1 → 0.4.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 +19 -0
- data/README.md +148 -85
- data/lib/verquest/base/private_class_methods.rb +1 -1
- data/lib/verquest/base/public_class_methods.rb +19 -5
- data/lib/verquest/configuration.rb +85 -5
- data/lib/verquest/gem_version.rb +1 -1
- data/lib/verquest/properties/array.rb +34 -12
- data/lib/verquest/properties/base.rb +2 -2
- data/lib/verquest/properties/collection.rb +19 -19
- data/lib/verquest/properties/field.rb +32 -13
- data/lib/verquest/properties/object.rb +11 -11
- data/lib/verquest/properties/reference.rb +4 -4
- data/lib/verquest/transformer.rb +5 -6
- data/lib/verquest/version.rb +53 -28
- data/lib/verquest.rb +4 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 725838444df6f3861ab842c88b430aa7bbc81023637595b4a0a8a909df5a2fbe
|
4
|
+
data.tar.gz: 88041a5f8bc888f90894975b5fb371ac8184877bb75b74e2e099ce57997b331a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3991e8ff96b436e82a6edce43d591f35f669cbab9ef99b01bc26192bef94e3f48afe59595d8ae5d3d605bd96c0150bfdb557e555f1d20e79d74cbe4343d99c49
|
7
|
+
data.tar.gz: dcc10eeadec0f32073569db402893bb3f1ab5629da79eee32d605c476bbcf05f637f5b348f83478863bf7d28fc1d0b3d2cd9b54c38f356f3bfa53d51d3ac50bb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
### Breaking Changes
|
4
|
+
- **BREAKING:** Renaming validation method from `validate_schema` to `valid_schema?` to better reflect its purpose.
|
5
|
+
- **BREAKING:** The `validate_schema` now returns an array of errors instead of a boolean value, allowing for more detailed error reporting.
|
6
|
+
|
7
|
+
### New Features
|
8
|
+
- Add support for custom field types.
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
- Loading the gem in another project with `zeitwerk` now works correctly.
|
12
|
+
- Fix schema validation after `json_schemer` refactoring.
|
13
|
+
|
14
|
+
## [0.3.0] - 2025-06-25
|
15
|
+
|
16
|
+
### Breaking Changes
|
17
|
+
- **BREAKING:** Replace `json-schema` gem with `json_schemer` for support of newer JSON Schema specifications (set the lasest by default).
|
18
|
+
- **BREAKING:** Schema and validation schema now uses string keys instead of symbols.
|
19
|
+
|
20
|
+
### Added
|
21
|
+
- Allow insert default values for properties when validation is used.
|
3
22
|
|
4
23
|
## [0.2.1] - 2025-06-22
|
5
24
|
|
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.3"
|
23
23
|
```
|
24
24
|
|
25
25
|
And then execute:
|
@@ -128,48 +128,53 @@ UserCreateRequest.to_schema(version: "2025-06")
|
|
128
128
|
Output:
|
129
129
|
```ruby
|
130
130
|
{
|
131
|
-
type
|
132
|
-
description
|
133
|
-
required
|
134
|
-
properties
|
135
|
-
first_name
|
136
|
-
last_name
|
137
|
-
email
|
138
|
-
birth_date
|
139
|
-
address
|
140
|
-
permissions
|
141
|
-
type
|
142
|
-
items
|
143
|
-
type
|
144
|
-
required
|
145
|
-
properties
|
146
|
-
name
|
147
|
-
read
|
148
|
-
write
|
131
|
+
"type" => "object",
|
132
|
+
"description" => "User Create Request",
|
133
|
+
"required" => ["first_name", "last_name", "email", "address"],
|
134
|
+
"properties" => {
|
135
|
+
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
136
|
+
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
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"},
|
139
|
+
"address" => {"$ref" => "#/components/schemas/AddressCreateRequest"},
|
140
|
+
"permissions" => {
|
141
|
+
"type" => "array",
|
142
|
+
"items" => {
|
143
|
+
"type" => "object",
|
144
|
+
"required" => ["name"],
|
145
|
+
"properties" => {
|
146
|
+
"name" => {"type" => "string", "description" => "Name of the permission"},
|
147
|
+
"read" => {"type" => "boolean", "description" => "Permission to read"},
|
148
|
+
"write" => {"type" => "boolean", "description" => "Permission to write"}
|
149
149
|
}
|
150
|
-
},
|
151
|
-
description
|
150
|
+
},
|
151
|
+
"description" => "Permissions associated with the user"
|
152
152
|
},
|
153
|
-
role
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
153
|
+
"role" => {
|
154
|
+
"type" => "string",
|
155
|
+
"description" => "Role of the user",
|
156
|
+
"enum" => ["member", "manager"],
|
157
|
+
"default" => "member"
|
158
|
+
},
|
159
|
+
"profile_details" => {
|
160
|
+
"type" => "object",
|
161
|
+
"required" => [],
|
162
|
+
"properties" => {
|
163
|
+
"bio" => {"type" => "string", "description" => "Short biography of the user"},
|
164
|
+
"hobbies" => {"type" => "array", "items" => {"type" => "string"}, "description" => "Tags associated with the user"},
|
165
|
+
"social_links" => {
|
166
|
+
"type" => "object",
|
167
|
+
"required" => [],
|
168
|
+
"properties" => {
|
169
|
+
"github" => {"type" => "string", "format" => "uri", "description" => "GitHub profile URL"},
|
170
|
+
"mastodon" => {"type" => "string", "format" => "uri", "description" => "Mastodon profile URL"}
|
171
|
+
},
|
172
|
+
"description" => "Some social networks"
|
168
173
|
}
|
169
174
|
}
|
170
175
|
}
|
171
176
|
},
|
172
|
-
additionalProperties
|
177
|
+
"additionalProperties" => false
|
173
178
|
}
|
174
179
|
```
|
175
180
|
|
@@ -184,66 +189,69 @@ UserCreateRequest.to_validation_schema(version: "2025-06")
|
|
184
189
|
Output:
|
185
190
|
```ruby
|
186
191
|
{
|
187
|
-
type
|
188
|
-
description
|
189
|
-
required
|
190
|
-
properties
|
191
|
-
first_name
|
192
|
-
last_name
|
193
|
-
email
|
194
|
-
birth_date
|
195
|
-
address
|
196
|
-
type
|
197
|
-
description
|
198
|
-
required
|
199
|
-
properties
|
200
|
-
street
|
201
|
-
city
|
202
|
-
postal_code
|
203
|
-
country
|
192
|
+
"type" => "object",
|
193
|
+
"description" => "User Create Request",
|
194
|
+
"required" => ["first_name", "last_name", "email", "address"],
|
195
|
+
"properties" => {
|
196
|
+
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
197
|
+
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
198
|
+
"email" => {"type" => "string", "format" => "email", "description" => "The email address of the user"},
|
199
|
+
"birth_date" => {"type" => "string", "format" => "date", "description" => "The birth date of the user"},
|
200
|
+
"address" => { # from the AddressCreateRequest
|
201
|
+
"type" => "object",
|
202
|
+
"description" => "Address Create Request",
|
203
|
+
"required" => ["street", "city", "postal_code", "country"],
|
204
|
+
"properties" => {
|
205
|
+
"street" => {"type" => "string", "description" => "Street address"},
|
206
|
+
"city" => {"type" => "string", "description" => "City of residence"},
|
207
|
+
"postal_code" => {"type" => "string", "description" => "Postal code"},
|
208
|
+
"country" => {"type" => "string", "description" => "Country of residence"}
|
204
209
|
},
|
205
|
-
additionalProperties
|
210
|
+
"additionalProperties" => false
|
206
211
|
},
|
207
|
-
permissions
|
208
|
-
type
|
209
|
-
items
|
210
|
-
type
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
write: {type: :boolean, description: "Permission to write"}
|
212
|
+
"permissions" => {
|
213
|
+
"type" => "array",
|
214
|
+
"items" => {
|
215
|
+
"type" => "object", "required" => ["name"],
|
216
|
+
"properties" => {
|
217
|
+
"name" => {"type" => "string", "description" => "Name of the permission"},
|
218
|
+
"read" => {"type" => "boolean", "description" => "Permission to read"},
|
219
|
+
"write" => {"type" => "boolean", "description" => "Permission to write"}
|
216
220
|
}
|
217
221
|
},
|
218
|
-
description
|
222
|
+
"description" => "Permissions associated with the user"
|
219
223
|
},
|
220
|
-
role
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
224
|
+
"role" => {
|
225
|
+
"type" => "string",
|
226
|
+
"description" => "Role of the user",
|
227
|
+
"enum" => ["member", "manager"],
|
228
|
+
"default" => "member"
|
229
|
+
},
|
230
|
+
"profile_details" => {"type" => "object",
|
231
|
+
"required" => [],
|
232
|
+
"properties" => {
|
233
|
+
"bio" => {"type" => "string", "description" => "Short biography of the user"},
|
234
|
+
"hobbies" => {"type" => "array", "items" => {"type" => "string"}, "description" => "Tags associated with the user"},
|
235
|
+
"social_links" => {
|
236
|
+
"type" => "object",
|
237
|
+
"required" => [],
|
238
|
+
"properties" => {
|
239
|
+
"github" => {"type" => "string", "format" => "uri", "description" => "GitHub profile URL"},
|
240
|
+
"mastodon" => {"type" => "string", "format" => "uri", "description" => "Mastodon profile URL"}
|
241
|
+
},
|
242
|
+
"description" => "Some social networks"}
|
236
243
|
}
|
237
244
|
}
|
238
245
|
},
|
239
|
-
additionalProperties
|
246
|
+
"additionalProperties" => false
|
240
247
|
}
|
241
248
|
```
|
242
249
|
|
243
250
|
You can also validate it to ensure it meets the JSON Schema standards:
|
244
251
|
|
245
252
|
```ruby
|
246
|
-
UserCreateRequest.
|
253
|
+
UserCreateRequest.valid_schema?(version: "2025-06") # => true/false
|
254
|
+
UserCreateRequest.validate_schema(version: "2025-06") # => Array of errors or empty array if valid
|
247
255
|
```
|
248
256
|
|
249
257
|
## Core Features
|
@@ -268,6 +276,61 @@ The JSON schema can be used for both validation of incoming parameters and for g
|
|
268
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.
|
269
277
|
- `with_options`: Allows you to define multiple fields with the same options, reducing repetition.
|
270
278
|
|
279
|
+
#### Custom Field Types
|
280
|
+
|
281
|
+
You can define custom field types that can be used in `field` and `array` in the configuration.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
Verquest.configure do |config|
|
285
|
+
config.custom_field_types = {
|
286
|
+
email: {
|
287
|
+
type: "string",
|
288
|
+
schema_options: {format: "email"}
|
289
|
+
},
|
290
|
+
uuid: {
|
291
|
+
type: "string",
|
292
|
+
schema_options: {format: "uuid"}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
Then you can use it in your request:
|
299
|
+
```ruby
|
300
|
+
class EmailRequest < Verquest::Base
|
301
|
+
description "User Create Request"
|
302
|
+
schema_options additional_properties: false
|
303
|
+
|
304
|
+
version "2025-06" do
|
305
|
+
field :email, type: :email
|
306
|
+
array :uuids, type: :uuid
|
307
|
+
end
|
308
|
+
end
|
309
|
+
```
|
310
|
+
|
311
|
+
`EmailRequest.to_schema(version: "2025-06")` will then generate the following JSON Schema:
|
312
|
+
```ruby
|
313
|
+
{
|
314
|
+
"type" => "object",
|
315
|
+
"description" => "User Create Request",
|
316
|
+
"required" => ["email"],
|
317
|
+
"properties" => {
|
318
|
+
"email" => {
|
319
|
+
"type" => "string",
|
320
|
+
"format" => "email"
|
321
|
+
},
|
322
|
+
"uuids" => {
|
323
|
+
"type" => "array",
|
324
|
+
"items" => {
|
325
|
+
"type" => "string",
|
326
|
+
"format" => "uuid"
|
327
|
+
}
|
328
|
+
}
|
329
|
+
},
|
330
|
+
"additionalProperties" => false
|
331
|
+
}
|
332
|
+
```
|
333
|
+
|
271
334
|
### Versioning
|
272
335
|
|
273
336
|
Verquest allows you to define multiple versions of your API requests, making it easy to evolve your API over time:
|
@@ -410,7 +473,7 @@ Verquest.configure do |config|
|
|
410
473
|
config.current_version = -> { Current.api_version }
|
411
474
|
|
412
475
|
# Set the JSON Schema version
|
413
|
-
config.json_schema_version = :
|
476
|
+
config.json_schema_version = :draft2020_12 # default
|
414
477
|
|
415
478
|
# Set the error handling strategy for processing params
|
416
479
|
config.validation_error_handling = :raise # default, can be set also to :result
|
@@ -425,7 +488,7 @@ end
|
|
425
488
|
|
426
489
|
## Documentation
|
427
490
|
|
428
|
-
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest).
|
491
|
+
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.4.0/).
|
429
492
|
|
430
493
|
## Development
|
431
494
|
|
@@ -64,7 +64,7 @@ module Verquest
|
|
64
64
|
instance_exec(&block)
|
65
65
|
ensure
|
66
66
|
version.description ||= versions.description
|
67
|
-
version.schema_options = versions.schema_options.merge(version.schema_options)
|
67
|
+
version.schema_options = versions.schema_options.merge(version.schema_options).transform_keys(&:to_s)
|
68
68
|
version.prepare
|
69
69
|
end
|
70
70
|
|
@@ -12,7 +12,8 @@ module Verquest
|
|
12
12
|
# @param version [String, nil] Specific version to use, defaults to configuration setting
|
13
13
|
# @param validate [Boolean, nil] Whether to validate the params, defaults to configuration setting
|
14
14
|
# @param remove_extra_root_keys [Boolean, nil] Whether to remove extra keys at the root level, defaults to configuration setting
|
15
|
-
# @return [Verquest::Result] Success result with mapped params or
|
15
|
+
# @return [Verquest::Result, Hash, Exception] When validation_error_handling is :result, returns a Success result with mapped params or Failure result with validation errors.
|
16
|
+
# When validation_error_handling is :raise, returns mapped params directly or raises InvalidParamsError with validation errors.
|
16
17
|
def process(params, version: nil, validate: nil, remove_extra_root_keys: nil)
|
17
18
|
validate = Verquest.configuration.validate_params if validate.nil?
|
18
19
|
remove_extra_root_keys = Verquest.configuration.remove_extra_root_keys if remove_extra_root_keys.nil?
|
@@ -23,7 +24,7 @@ module Verquest
|
|
23
24
|
params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
|
24
25
|
params = params.slice(*version_class.properties.keys) if remove_extra_root_keys
|
25
26
|
|
26
|
-
if validate && (validation_result = version_class.validate_params(params: params
|
27
|
+
if validate && (validation_result = version_class.validate_params(params: params)) && validation_result.any?
|
27
28
|
case Verquest.configuration.validation_error_handling
|
28
29
|
when :raise
|
29
30
|
raise InvalidParamsError.new("Validation failed", errors: validation_result)
|
@@ -59,7 +60,7 @@ module Verquest
|
|
59
60
|
version = resolve(version)
|
60
61
|
|
61
62
|
if property
|
62
|
-
version.validation_schema[
|
63
|
+
version.validation_schema["properties"][property.to_s]
|
63
64
|
else
|
64
65
|
version.validation_schema
|
65
66
|
end
|
@@ -69,6 +70,19 @@ module Verquest
|
|
69
70
|
#
|
70
71
|
# @param version [String, nil] Specific version to use, defaults to configuration setting
|
71
72
|
# @return [Boolean] True if schema is valid
|
73
|
+
def valid_schema?(version: nil)
|
74
|
+
resolve(version).valid_schema?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Validates the schema against the metaschema and returns detailed validation errors
|
78
|
+
#
|
79
|
+
# This method validates the schema against the configured JSON Schema metaschema
|
80
|
+
# and returns detailed validation errors if any are found. It's useful for debugging
|
81
|
+
# schema issues during development and testing.
|
82
|
+
#
|
83
|
+
# @param version [String, nil] Specific version to use, defaults to configuration setting
|
84
|
+
# @return [Array<Hash>] An array of validation error details, empty if schema is valid
|
85
|
+
# @see #valid_schema?
|
72
86
|
def validate_schema(version: nil)
|
73
87
|
resolve(version).validate_schema
|
74
88
|
end
|
@@ -76,7 +90,7 @@ module Verquest
|
|
76
90
|
# Returns the mapping for a specific version or property
|
77
91
|
#
|
78
92
|
# @param version [String, nil] Specific version to use, defaults to configuration setting
|
79
|
-
# @param property [Symbol, nil] Specific property to retrieve mapping for
|
93
|
+
# @param property [String, Symbol, nil] Specific property to retrieve mapping for
|
80
94
|
# @return [Hash] The mapping configuration
|
81
95
|
def mapping(version: nil, property: nil)
|
82
96
|
version = resolve(version)
|
@@ -90,7 +104,7 @@ module Verquest
|
|
90
104
|
|
91
105
|
# Returns the JSON reference for the request or a specific property
|
92
106
|
#
|
93
|
-
# @param property [Symbol, nil] Specific property to retrieve reference for
|
107
|
+
# @param property [String, Symbol, nil] Specific property to retrieve reference for
|
94
108
|
# @return [String] The JSON reference for the request or property
|
95
109
|
def to_ref(property: nil)
|
96
110
|
base = "#/components/schemas/#{component_name}"
|
@@ -11,13 +11,35 @@ module Verquest
|
|
11
11
|
# config.current_version = -> { Current.api_version }
|
12
12
|
# end
|
13
13
|
class Configuration
|
14
|
+
include Base::HelperClassMethods
|
15
|
+
|
16
|
+
# Mapping of supported JSON Schema versions to their implementation classes
|
17
|
+
#
|
18
|
+
# This constant maps the symbolic names of JSON Schema versions to their
|
19
|
+
# corresponding JSONSchemer implementation classes. These are used for schema
|
20
|
+
# validation and generation based on the configured schema version.
|
21
|
+
#
|
22
|
+
# @example Accessing a schema implementation
|
23
|
+
# schema_class = Verquest::Configuration::SCHEMAS[:draft2020_12]
|
24
|
+
#
|
25
|
+
# @return [Hash<Symbol, Class>] A frozen hash mapping schema version names to implementation classes
|
26
|
+
SCHEMAS = {
|
27
|
+
draft4: JSONSchemer::Draft4,
|
28
|
+
draft6: JSONSchemer::Draft6,
|
29
|
+
draft7: JSONSchemer::Draft7,
|
30
|
+
draft2019_09: JSONSchemer::Draft201909,
|
31
|
+
draft2020_12: JSONSchemer::Draft202012,
|
32
|
+
openapi30: JSONSchemer::OpenAPI30,
|
33
|
+
openapi31: JSONSchemer::OpenAPI31
|
34
|
+
}.freeze
|
35
|
+
|
14
36
|
# @!attribute [rw] validate_params
|
15
37
|
# Controls whether parameters are automatically validated against the schema
|
16
38
|
# @return [Boolean] true if validation is enabled, false otherwise
|
17
39
|
#
|
18
40
|
# @!attribute [rw] json_schema_version
|
19
|
-
# The JSON Schema draft version to use for validation and schema generation (see
|
20
|
-
# @return [Symbol] The JSON Schema version (e.g., :
|
41
|
+
# The JSON Schema draft version to use for validation and schema generation (see Configuration::SCHEMAS)
|
42
|
+
# @return [Symbol] The JSON Schema version (e.g., :draft2020_12, :draft2019_09, :draft7)
|
21
43
|
#
|
22
44
|
# @!attribute [rw] validation_error_handling
|
23
45
|
# Controls how errors during parameter processing are handled
|
@@ -26,7 +48,11 @@ module Verquest
|
|
26
48
|
# @!attribute [rw] remove_extra_root_keys
|
27
49
|
# Controls if extra root keys not defined in the schema should be removed from the parameters
|
28
50
|
# @return [Boolean] true if extra keys should be removed, false otherwise
|
29
|
-
|
51
|
+
#
|
52
|
+
# @!attribute [rw] insert_property_defaults
|
53
|
+
# Controls whether default values defined in property schemas should be inserted when not provided during validation
|
54
|
+
# @return [Boolean] true if default values should be inserted, false otherwise
|
55
|
+
attr_accessor :validate_params, :json_schema_version, :validation_error_handling, :remove_extra_root_keys, :insert_property_defaults
|
30
56
|
|
31
57
|
# @!attribute [r] current_version
|
32
58
|
# A callable object that returns the current API version to use when not explicitly specified
|
@@ -35,17 +61,23 @@ module Verquest
|
|
35
61
|
# @!attribute [r] version_resolver
|
36
62
|
# The resolver used to map version strings/identifiers to version objects
|
37
63
|
# @return [#call] An object that responds to `call` for resolving versions
|
38
|
-
|
64
|
+
#
|
65
|
+
# @!attribute [r] custom_field_types
|
66
|
+
# Custom field types to extend the standard set of field types
|
67
|
+
# @return [Hash<Symbol, Hash>] Hash mapping field type names to their configuration
|
68
|
+
attr_reader :current_version, :version_resolver, :custom_field_types
|
39
69
|
|
40
70
|
# Initialize a new Configuration with default values
|
41
71
|
#
|
42
72
|
# @return [Configuration] A new configuration instance with default settings
|
43
73
|
def initialize
|
44
74
|
@validate_params = true
|
45
|
-
@json_schema_version = :
|
75
|
+
@json_schema_version = :draft2020_12
|
46
76
|
@validation_error_handling = :raise # or :result
|
47
77
|
@remove_extra_root_keys = true
|
48
78
|
@version_resolver = VersionResolver
|
79
|
+
@insert_property_defaults = true
|
80
|
+
@custom_field_types = {}
|
49
81
|
end
|
50
82
|
|
51
83
|
# Sets the current version strategy using a callable object
|
@@ -69,5 +101,53 @@ module Verquest
|
|
69
101
|
|
70
102
|
@version_resolver = version_resolver
|
71
103
|
end
|
104
|
+
|
105
|
+
# Sets the custom field types
|
106
|
+
#
|
107
|
+
# This method allows defining custom field types beyond the default ones.
|
108
|
+
# Custom field types can be used to extend validation with specific formats
|
109
|
+
# or patterns. Each custom field type should include a base type and optional
|
110
|
+
# schema validation options.
|
111
|
+
#
|
112
|
+
# @example Adding a phone number field type
|
113
|
+
# config.custom_field_types = {
|
114
|
+
# email: {
|
115
|
+
# type: "string",
|
116
|
+
# schema_options: {format: "email", pattern: /\A[^@\s]+@[^@.\s]+(\.[^@.\s]+)+\z/}
|
117
|
+
# },
|
118
|
+
# uuid: {
|
119
|
+
# type: "string",
|
120
|
+
# schema_options: {format: "uuid", pattern: /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/}
|
121
|
+
# }
|
122
|
+
# }
|
123
|
+
#
|
124
|
+
# @param custom_field_types [Hash] A hash mapping field type names to their configuration
|
125
|
+
# @raise [ArgumentError] If the provided value isn't a Hash
|
126
|
+
# @return [Hash<Symbol, Hash>] The processed custom field types hash with symbolized keys
|
127
|
+
def custom_field_types=(custom_field_types)
|
128
|
+
raise ArgumentError, "Custom field types must be a Hash" unless custom_field_types.is_a?(Hash)
|
129
|
+
|
130
|
+
custom_field_types.delete_if { |k, _| Properties::Field::DEFAULT_TYPES.include?(k.to_s) }
|
131
|
+
custom_field_types.each do |_, value|
|
132
|
+
value[:schema_options] = camelize(value[:schema_options]) if value[:schema_options]
|
133
|
+
end
|
134
|
+
|
135
|
+
@custom_field_types = custom_field_types.transform_keys(&:to_sym)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Gets the JSON Schema class based on the configured version
|
139
|
+
#
|
140
|
+
# @return [Class] The JSON Schema class matching the configured version
|
141
|
+
# @raise [ArgumentError] If the configured json_schema_version is not supported
|
142
|
+
def json_schema
|
143
|
+
SCHEMAS[json_schema_version] || raise(ArgumentError, "Unsupported JSON Schema version: #{json_schema_version}")
|
144
|
+
end
|
145
|
+
|
146
|
+
# Gets the JSON Schema URI for the configured schema version
|
147
|
+
#
|
148
|
+
# @return [String] The base URI for the configured JSON Schema version
|
149
|
+
def json_schema_uri
|
150
|
+
json_schema::BASE_URI.to_s
|
151
|
+
end
|
72
152
|
end
|
73
153
|
end
|
data/lib/verquest/gem_version.rb
CHANGED
@@ -6,6 +6,7 @@ module Verquest
|
|
6
6
|
#
|
7
7
|
# Represents an array data structure in the schema with specified item type.
|
8
8
|
# Used to define arrays of scalar types (string, number, integer, boolean).
|
9
|
+
# Supports both default item types and custom field types defined in the configuration.
|
9
10
|
#
|
10
11
|
# @example Define an array of strings
|
11
12
|
# array = Verquest::Properties::Array.new(
|
@@ -16,20 +17,34 @@ module Verquest
|
|
16
17
|
class Array < Base
|
17
18
|
# Initialize a new Array property
|
18
19
|
#
|
19
|
-
# @param name [Symbol] The name of the property
|
20
|
-
# @param type [Symbol] The type of items in the array
|
20
|
+
# @param name [String, Symbol] The name of the property
|
21
|
+
# @param type [String, Symbol] The type of items in the array, can be a default type or a custom field type
|
21
22
|
# @param map [String, nil] The mapping path for this property (nil for no explicit mapping)
|
22
23
|
# @param required [Boolean] Whether this property is required
|
23
|
-
# @param
|
24
|
+
# @param item_schema_options [Hash] Additional JSON schema options for the array items (merged with custom type options)
|
25
|
+
# @param schema_options [Hash] Additional JSON schema options for the array property itself
|
26
|
+
# @raise [ArgumentError] If type is not one of the allowed types (default or custom)
|
24
27
|
# @raise [ArgumentError] If attempting to map an array to the root
|
25
|
-
def initialize(name:, type:, map: nil, required: false, **schema_options)
|
28
|
+
def initialize(name:, type:, map: nil, required: false, item_schema_options: {}, **schema_options)
|
29
|
+
raise ArgumentError, "Type must be one of #{allowed_types.join(", ")}" unless allowed_types.include?(type.to_s)
|
26
30
|
raise ArgumentError, "You can not map array to the root" if map == "/"
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
if (custom_type = Verquest.configuration.custom_field_types[type.to_sym])
|
33
|
+
@type = custom_type[:type].to_s
|
34
|
+
@item_schema_options = if custom_type.key?(:schema_options)
|
35
|
+
custom_type[:schema_options].merge(item_schema_options).transform_keys(&:to_s)
|
36
|
+
else
|
37
|
+
item_schema_options.transform_keys(&:to_s)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@type = type.to_s
|
41
|
+
@item_schema_options = item_schema_options.transform_keys(&:to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
@name = name.to_s
|
30
45
|
@map = map
|
31
46
|
@required = required
|
32
|
-
@schema_options = schema_options
|
47
|
+
@schema_options = schema_options&.transform_keys(&:to_s)
|
33
48
|
end
|
34
49
|
|
35
50
|
# Generate JSON schema definition for this array property
|
@@ -38,16 +53,16 @@ module Verquest
|
|
38
53
|
def to_schema
|
39
54
|
{
|
40
55
|
name => {
|
41
|
-
type
|
42
|
-
items
|
56
|
+
"type" => "array",
|
57
|
+
"items" => {"type" => type}.merge(item_schema_options)
|
43
58
|
}.merge(schema_options)
|
44
59
|
}
|
45
60
|
end
|
46
61
|
|
47
62
|
# Create mapping for this array property
|
48
63
|
#
|
49
|
-
# @param key_prefix [Array<
|
50
|
-
# @param value_prefix [Array<
|
64
|
+
# @param key_prefix [Array<String>] Prefix for the source key
|
65
|
+
# @param value_prefix [Array<String>] Prefix for the target value
|
51
66
|
# @param mapping [Hash] The mapping hash to be updated
|
52
67
|
# @param version [String, nil] The version to create mapping for, defaults to configuration setting
|
53
68
|
# @return [Hash] The updated mapping hash
|
@@ -57,7 +72,14 @@ module Verquest
|
|
57
72
|
|
58
73
|
private
|
59
74
|
|
60
|
-
attr_reader :type, :schema_options
|
75
|
+
attr_reader :type, :schema_options, :item_schema_options
|
76
|
+
|
77
|
+
# Gets the list of allowed item types, including both default and custom types
|
78
|
+
#
|
79
|
+
# @return [Array<String>] Array of allowed item type names
|
80
|
+
def allowed_types
|
81
|
+
Verquest::Properties::Field::DEFAULT_TYPES + Verquest.configuration.custom_field_types.keys.map(&:to_s)
|
82
|
+
end
|
61
83
|
end
|
62
84
|
end
|
63
85
|
end
|
@@ -11,7 +11,7 @@ module Verquest
|
|
11
11
|
# @abstract Subclass and override {#to_schema}, {#mapping} to implement
|
12
12
|
class Base
|
13
13
|
# @!attribute [rw] name
|
14
|
-
# @return [
|
14
|
+
# @return [String] The name of the property
|
15
15
|
# @!attribute [rw] required
|
16
16
|
# @return [Boolean] Whether this property is required
|
17
17
|
# @!attribute [rw] map
|
@@ -43,7 +43,7 @@ module Verquest
|
|
43
43
|
|
44
44
|
# Creates mapping for this property
|
45
45
|
# @abstract
|
46
|
-
# @param key_prefix [Array<
|
46
|
+
# @param key_prefix [Array<String>] Prefix for the source key
|
47
47
|
# @param value_prefix [Array<String>] Prefix for the target value
|
48
48
|
# @param mapping [Hash] The mapping hash to be updated
|
49
49
|
# @param version [String, nil] The version to create mapping for
|
@@ -20,7 +20,7 @@ module Verquest
|
|
20
20
|
class Collection < Base
|
21
21
|
# Initialize a new Collection property
|
22
22
|
#
|
23
|
-
# @param name [Symbol] The name of the property
|
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
25
|
# @param required [Boolean] Whether this property is required
|
26
26
|
# @param map [String, nil] The mapping path for this property
|
@@ -31,11 +31,11 @@ module Verquest
|
|
31
31
|
|
32
32
|
@properties = {}
|
33
33
|
|
34
|
-
@name = name
|
34
|
+
@name = name.to_s
|
35
35
|
@item = item
|
36
36
|
@required = required
|
37
37
|
@map = map
|
38
|
-
@schema_options = schema_options
|
38
|
+
@schema_options = schema_options&.transform_keys(&:to_s)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Add a child property to this collection's item definition
|
@@ -60,20 +60,20 @@ module Verquest
|
|
60
60
|
if has_item?
|
61
61
|
{
|
62
62
|
name => {
|
63
|
-
type
|
64
|
-
items
|
65
|
-
"$ref"
|
63
|
+
"type" => "array",
|
64
|
+
"items" => {
|
65
|
+
"$ref" => item.to_ref
|
66
66
|
}
|
67
67
|
}.merge(schema_options)
|
68
68
|
}
|
69
69
|
else
|
70
70
|
{
|
71
71
|
name => {
|
72
|
-
type
|
73
|
-
items
|
74
|
-
type
|
75
|
-
required
|
76
|
-
properties
|
72
|
+
"type" => "array",
|
73
|
+
"items" => {
|
74
|
+
"type" => "object",
|
75
|
+
"required" => properties.values.select(&:required).map(&:name),
|
76
|
+
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
77
77
|
}
|
78
78
|
}.merge(schema_options)
|
79
79
|
}
|
@@ -88,18 +88,18 @@ module Verquest
|
|
88
88
|
if has_item?
|
89
89
|
{
|
90
90
|
name => {
|
91
|
-
type
|
92
|
-
items
|
91
|
+
"type" => "array",
|
92
|
+
"items" => item.to_validation_schema(version: version)
|
93
93
|
}.merge(schema_options)
|
94
94
|
}
|
95
95
|
else
|
96
96
|
{
|
97
97
|
name => {
|
98
|
-
type
|
99
|
-
items
|
100
|
-
type
|
101
|
-
required
|
102
|
-
properties
|
98
|
+
"type" => "array",
|
99
|
+
"items" => {
|
100
|
+
"type" => "object",
|
101
|
+
"required" => properties.values.select(&:required).map(&:name),
|
102
|
+
"properties" => properties.transform_values { |property| property.to_validation_schema(version: version)[property.name] }
|
103
103
|
}
|
104
104
|
}.merge(schema_options)
|
105
105
|
}
|
@@ -118,7 +118,7 @@ module Verquest
|
|
118
118
|
# - Creates mappings for each property in the collection items
|
119
119
|
# - Each property gets mapped with array notation and appropriate prefixes
|
120
120
|
#
|
121
|
-
# @param key_prefix [Array<
|
121
|
+
# @param key_prefix [Array<String>] Prefix for the source key
|
122
122
|
# @param value_prefix [Array<String>] Prefix for the target value
|
123
123
|
# @param mapping [Hash] The mapping hash to be updated
|
124
124
|
# @param version [String, nil] The version to create mapping for
|
@@ -6,6 +6,7 @@ module Verquest
|
|
6
6
|
#
|
7
7
|
# Represents simple scalar types (string, number, integer, boolean) in the schema.
|
8
8
|
# Used for defining basic data fields without nesting.
|
9
|
+
# Supports both default types and custom field types defined in the configuration.
|
9
10
|
#
|
10
11
|
# @example Define a required string field
|
11
12
|
# field = Verquest::Properties::Field.new(
|
@@ -15,35 +16,46 @@ module Verquest
|
|
15
16
|
# format: "email"
|
16
17
|
# )
|
17
18
|
class Field < Base
|
18
|
-
# List of
|
19
|
+
# List of default field types
|
19
20
|
# @return [Array<Symbol>]
|
20
|
-
|
21
|
+
DEFAULT_TYPES = %w[string number integer boolean].freeze
|
21
22
|
|
22
23
|
# Initialize a new Field property
|
23
24
|
#
|
24
|
-
# @param name [Symbol] The name of the property
|
25
|
-
# @param type [Symbol] The data type for this field,
|
26
|
-
# @param required [Boolean] Whether this property is required
|
25
|
+
# @param name [String, Symbol] The name of the property
|
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 if it defines required)
|
27
28
|
# @param map [String, nil] The mapping path for this property
|
28
|
-
# @param schema_options [Hash] Additional JSON schema options for this property
|
29
|
-
# @raise [ArgumentError] If type is not one of the allowed types
|
29
|
+
# @param schema_options [Hash] Additional JSON schema options for this property (merged with custom type options)
|
30
|
+
# @raise [ArgumentError] If type is not one of the allowed types (default or custom)
|
30
31
|
# @raise [ArgumentError] If attempting to map a field to root without a name
|
31
32
|
def initialize(name:, type:, required: false, map: nil, **schema_options)
|
32
|
-
raise ArgumentError, "Type must be one of #{
|
33
|
+
raise ArgumentError, "Type must be one of #{allowed_types.join(", ")}" unless allowed_types.include?(type.to_s)
|
33
34
|
raise ArgumentError, "You can not map fields to the root without a name" if map == "/"
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
if (custom_type = Verquest.configuration.custom_field_types[type.to_sym])
|
37
|
+
@type = custom_type[:type].to_s
|
38
|
+
@required = custom_type.key?(:required) ? custom_type[:required] : required
|
39
|
+
@schema_options = if custom_type.key?(:schema_options)
|
40
|
+
custom_type[:schema_options].merge(schema_options).transform_keys(&:to_s)
|
41
|
+
else
|
42
|
+
schema_options.transform_keys(&:to_s)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@type = type.to_s
|
46
|
+
@required = required
|
47
|
+
@schema_options = schema_options&.transform_keys(&:to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
@name = name.to_s
|
38
51
|
@map = map
|
39
|
-
@schema_options = schema_options
|
40
52
|
end
|
41
53
|
|
42
54
|
# Generate JSON schema definition for this field
|
43
55
|
#
|
44
56
|
# @return [Hash] The schema definition for this field
|
45
57
|
def to_schema
|
46
|
-
{name => {type
|
58
|
+
{name => {"type" => type}.merge(schema_options)}
|
47
59
|
end
|
48
60
|
|
49
61
|
# Create mapping for this field property
|
@@ -60,6 +72,13 @@ module Verquest
|
|
60
72
|
private
|
61
73
|
|
62
74
|
attr_reader :type, :schema_options
|
75
|
+
|
76
|
+
# Gets the list of allowed field types, including both default and custom types
|
77
|
+
#
|
78
|
+
# @return [Array<String>] Array of allowed field type names
|
79
|
+
def allowed_types
|
80
|
+
DEFAULT_TYPES + Verquest.configuration.custom_field_types.keys.map(&:to_s)
|
81
|
+
end
|
63
82
|
end
|
64
83
|
end
|
65
84
|
end
|
@@ -14,17 +14,17 @@ module Verquest
|
|
14
14
|
class Object < Base
|
15
15
|
# Initialize a new Object property
|
16
16
|
#
|
17
|
-
# @param name [String] The name of the property
|
17
|
+
# @param name [String, Symbol] The name of the property
|
18
18
|
# @param required [Boolean] Whether this property is required
|
19
19
|
# @param map [String, nil] The mapping path for this property
|
20
20
|
# @param schema_options [Hash] Additional JSON schema options for this property
|
21
21
|
def initialize(name:, required: false, map: nil, **schema_options)
|
22
22
|
@properties = {}
|
23
23
|
|
24
|
-
@name = name
|
24
|
+
@name = name.to_s
|
25
25
|
@required = required
|
26
26
|
@map = map
|
27
|
-
@schema_options = schema_options
|
27
|
+
@schema_options = schema_options&.transform_keys(&:to_s)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Add a child property to this object
|
@@ -32,7 +32,7 @@ module Verquest
|
|
32
32
|
# @param property [Verquest::Properties::Base] The property to add to this object
|
33
33
|
# @return [Verquest::Properties::Base] The added property
|
34
34
|
def add(property)
|
35
|
-
properties[property.name] = property
|
35
|
+
properties[property.name.to_s] = property
|
36
36
|
end
|
37
37
|
|
38
38
|
# Generate JSON schema definition for this object property
|
@@ -41,9 +41,9 @@ module Verquest
|
|
41
41
|
def to_schema
|
42
42
|
{
|
43
43
|
name => {
|
44
|
-
type
|
45
|
-
required
|
46
|
-
properties
|
44
|
+
"type" => "object",
|
45
|
+
"required" => properties.values.select(&:required).map(&:name),
|
46
|
+
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
47
47
|
}.merge(schema_options)
|
48
48
|
}
|
49
49
|
end
|
@@ -55,16 +55,16 @@ module Verquest
|
|
55
55
|
def to_validation_schema(version: nil)
|
56
56
|
{
|
57
57
|
name => {
|
58
|
-
type
|
59
|
-
required
|
60
|
-
properties
|
58
|
+
"type" => "object",
|
59
|
+
"required" => properties.values.select(&:required).map(&:name),
|
60
|
+
"properties" => properties.transform_values { |property| property.to_validation_schema(version: version)[property.name] }
|
61
61
|
}.merge(schema_options)
|
62
62
|
}
|
63
63
|
end
|
64
64
|
|
65
65
|
# Create mapping for this object property and all its children
|
66
66
|
#
|
67
|
-
# @param key_prefix [Array<
|
67
|
+
# @param key_prefix [Array<String>] Prefix for the source key
|
68
68
|
# @param value_prefix [Array<String>] Prefix for the target value
|
69
69
|
# @param mapping [Hash] The mapping hash to be updated
|
70
70
|
# @param version [String, nil] The version to create mapping for
|
@@ -23,13 +23,13 @@ module Verquest
|
|
23
23
|
class Reference < Base
|
24
24
|
# Initialize a new Reference property
|
25
25
|
#
|
26
|
-
# @param name [String] The name of the property
|
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
29
|
# @param map [String, nil] The mapping path for this property
|
30
30
|
# @param required [Boolean] Whether this property is required
|
31
31
|
def initialize(name:, from:, property: nil, map: nil, required: false)
|
32
|
-
@name = name
|
32
|
+
@name = name.to_s
|
33
33
|
@from = from
|
34
34
|
@property = property
|
35
35
|
@map = map
|
@@ -41,7 +41,7 @@ module Verquest
|
|
41
41
|
# @return [Hash] The schema definition with a $ref pointer
|
42
42
|
def to_schema
|
43
43
|
{
|
44
|
-
name => {"$ref"
|
44
|
+
name => {"$ref" => from.to_ref(property: property)}
|
45
45
|
}
|
46
46
|
end
|
47
47
|
|
@@ -58,7 +58,7 @@ module Verquest
|
|
58
58
|
# Create mapping for this reference property
|
59
59
|
# This delegates to the referenced schema's mapping with appropriate key prefixing
|
60
60
|
#
|
61
|
-
# @param key_prefix [Array<
|
61
|
+
# @param key_prefix [Array<String>] Prefix for the source key
|
62
62
|
# @param value_prefix [Array<String>] Prefix for the target value
|
63
63
|
# @param mapping [Hash] The mapping hash to be updated
|
64
64
|
# @param version [String, nil] The version to create mapping for
|
data/lib/verquest/transformer.rb
CHANGED
@@ -113,12 +113,11 @@ module Verquest
|
|
113
113
|
|
114
114
|
case data
|
115
115
|
when Hash
|
116
|
-
#
|
117
|
-
return nil unless data.key?(key.to_s)
|
116
|
+
# Only check for string keys
|
117
|
+
return nil unless data.key?(key.to_s)
|
118
118
|
|
119
|
-
#
|
120
|
-
|
121
|
-
value = data[actual_key]
|
119
|
+
# Always use string keys
|
120
|
+
value = data[key.to_s]
|
122
121
|
|
123
122
|
if current_part[:array] && value.is_a?(Array)
|
124
123
|
# Process array elements and filter out nil values
|
@@ -153,7 +152,7 @@ module Verquest
|
|
153
152
|
|
154
153
|
current_part = path_parts.first
|
155
154
|
remaining_path = path_parts[1..]
|
156
|
-
key = current_part[:key].
|
155
|
+
key = current_part[:key].to_s # Ensure key is a string for consistency
|
157
156
|
|
158
157
|
if remaining_path.empty?
|
159
158
|
# End of path, set the value directly
|
data/lib/verquest/version.rb
CHANGED
@@ -50,7 +50,7 @@ module Verquest
|
|
50
50
|
# @param name [String] The name/identifier of the version
|
51
51
|
# @return [Version] A new Version instance
|
52
52
|
def initialize(name:)
|
53
|
-
@name = name
|
53
|
+
@name = name.to_s
|
54
54
|
@schema_options = {}
|
55
55
|
@properties = {}
|
56
56
|
end
|
@@ -69,7 +69,7 @@ module Verquest
|
|
69
69
|
# @return [Verquest::Properties::Base] The removed property
|
70
70
|
# @raise [PropertyNotFoundError] If the property doesn't exist
|
71
71
|
def remove(property_name)
|
72
|
-
properties.delete(property_name) || raise(PropertyNotFoundError.new("Property '#{property_name}' is not defined on '#{name}"))
|
72
|
+
properties.delete(property_name.to_s) || raise(PropertyNotFoundError.new("Property '#{property_name}' is not defined on '#{name}'"))
|
73
73
|
end
|
74
74
|
|
75
75
|
# Check if this version has a property with the given name
|
@@ -77,7 +77,7 @@ module Verquest
|
|
77
77
|
# @param property_name [Symbol, String] The name of the property to check
|
78
78
|
# @return [Boolean] true if the property exists, false otherwise
|
79
79
|
def has?(property_name)
|
80
|
-
properties.key?(property_name)
|
80
|
+
properties.key?(property_name.to_s)
|
81
81
|
end
|
82
82
|
|
83
83
|
# Copy properties from another version
|
@@ -90,7 +90,7 @@ module Verquest
|
|
90
90
|
raise ArgumentError, "Expected a Verquest::Version instance" unless version.is_a?(Version)
|
91
91
|
|
92
92
|
version.properties.values.each do |property|
|
93
|
-
next if exclude_properties.include?(property.name)
|
93
|
+
next if exclude_properties.include?(property.name.to_sym)
|
94
94
|
|
95
95
|
add(property)
|
96
96
|
end
|
@@ -113,28 +113,53 @@ module Verquest
|
|
113
113
|
# Validate the schema against the metaschema
|
114
114
|
#
|
115
115
|
# @return [Boolean] true if the schema is valid, false otherwise
|
116
|
-
def
|
117
|
-
|
116
|
+
def valid_schema?
|
117
|
+
JSONSchemer.valid_schema?(
|
118
|
+
validation_schema,
|
119
|
+
meta_schema: Verquest.configuration.json_schema_uri
|
120
|
+
)
|
121
|
+
end
|
118
122
|
|
119
|
-
|
120
|
-
|
123
|
+
# Validate the schema against the metaschema and return detailed errors
|
124
|
+
#
|
125
|
+
# This method validates the schema against the configured JSON Schema metaschema
|
126
|
+
# and returns detailed validation errors if any are found. It uses the JSONSchemer
|
127
|
+
# library with the schema version specified in the configuration.
|
128
|
+
#
|
129
|
+
# @return [Array<Hash>] An array of validation error details, empty if schema is valid
|
130
|
+
# @see #valid_schema?
|
131
|
+
def validate_schema
|
132
|
+
JSONSchemer.validate_schema(
|
133
|
+
validation_schema,
|
134
|
+
meta_schema: Verquest.configuration.json_schema_uri
|
135
|
+
).map do |error|
|
136
|
+
{
|
137
|
+
pointer: error["data_pointer"],
|
138
|
+
type: error["type"],
|
139
|
+
message: error["error"],
|
140
|
+
details: error["details"]
|
141
|
+
}
|
142
|
+
end
|
121
143
|
end
|
122
144
|
|
123
145
|
# Validate request parameters against the version's validation schema
|
124
146
|
#
|
125
147
|
# @param params [Hash] The request parameters to validate
|
126
|
-
# @param component_reference [String] A reference string for components in the schema
|
127
|
-
# @param remove_extra_root_keys [Boolean] Whether to remove extra keys not in the schema
|
128
148
|
# @return [Array<Hash>] An array of validation error details, or empty if valid
|
129
|
-
def validate_params(params
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
149
|
+
def validate_params(params:)
|
150
|
+
schemer = JSONSchemer.schema(
|
151
|
+
validation_schema,
|
152
|
+
meta_schema: Verquest.configuration.json_schema_uri,
|
153
|
+
insert_property_defaults: Verquest.configuration.insert_property_defaults
|
154
|
+
)
|
155
|
+
|
156
|
+
schemer.validate(params).map do |error|
|
157
|
+
{
|
158
|
+
pointer: error["data_pointer"],
|
159
|
+
type: error["type"],
|
160
|
+
message: error["error"],
|
161
|
+
details: error["details"]
|
162
|
+
}
|
138
163
|
end
|
139
164
|
end
|
140
165
|
|
@@ -147,7 +172,7 @@ module Verquest
|
|
147
172
|
raise PropertyNotFoundError.new("Property '#{property}' is not defined on '#{name}'") unless has?(property)
|
148
173
|
|
149
174
|
{}.tap do |mapping|
|
150
|
-
properties[property].mapping(key_prefix: [], value_prefix: [], mapping: mapping, version: name)
|
175
|
+
properties[property.to_s].mapping(key_prefix: [], value_prefix: [], mapping: mapping, version: name)
|
151
176
|
end
|
152
177
|
end
|
153
178
|
|
@@ -170,10 +195,10 @@ module Verquest
|
|
170
195
|
# @return [Hash] The frozen schema hash
|
171
196
|
def prepare_schema
|
172
197
|
@schema = {
|
173
|
-
type
|
174
|
-
description
|
175
|
-
required
|
176
|
-
properties
|
198
|
+
"type" => "object",
|
199
|
+
"description" => description,
|
200
|
+
"required" => properties.values.select(&:required).map(&:name),
|
201
|
+
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
177
202
|
}.merge(schema_options).freeze
|
178
203
|
end
|
179
204
|
|
@@ -185,10 +210,10 @@ module Verquest
|
|
185
210
|
# @return [Hash] The frozen validation schema hash
|
186
211
|
def prepare_validation_schema
|
187
212
|
@validation_schema = {
|
188
|
-
type
|
189
|
-
description
|
190
|
-
required
|
191
|
-
properties
|
213
|
+
"type" => "object",
|
214
|
+
"description" => description,
|
215
|
+
"required" => properties.values.select(&:required).map(&:name),
|
216
|
+
"properties" => properties.transform_values { |property| property.to_validation_schema(version: name)[property.name] }
|
192
217
|
}.merge(schema_options).freeze
|
193
218
|
end
|
194
219
|
|
data/lib/verquest.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "zeitwerk"
|
4
|
-
require "
|
4
|
+
require "json_schemer"
|
5
|
+
|
6
|
+
require_relative "verquest/gem_version"
|
5
7
|
|
6
8
|
loader = Zeitwerk::Loader.new
|
7
9
|
loader.tag = File.basename(__FILE__, ".rb")
|
10
|
+
loader.ignore("#{File.dirname(__FILE__)}/verquest/gem_version.rb")
|
8
11
|
loader.push_dir(File.dirname(__FILE__))
|
9
12
|
loader.setup
|
10
13
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verquest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Hlavicka
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: json_schemer
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '2.4'
|
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: '
|
40
|
+
version: '2.4'
|
41
41
|
description: Verquest helps you version API requests, simplifying the management of
|
42
42
|
changes, handling the mapping for internal versus external names and structures,
|
43
43
|
validating parameters, and exporting your requests to JSON Schema components for
|