verquest 0.6.1 → 0.6.3
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 +10 -0
- data/Rakefile +26 -3
- data/lib/verquest/base/private_class_methods.rb +33 -2
- data/lib/verquest/base/public_class_methods.rb +18 -9
- data/lib/verquest/configuration.rb +2 -0
- data/lib/verquest/gem_version.rb +1 -1
- data/lib/verquest/helper_methods/required_properties.rb +1 -1
- data/lib/verquest/properties/array.rb +2 -2
- data/lib/verquest/properties/base.rb +1 -1
- data/lib/verquest/properties/collection.rb +44 -5
- data/lib/verquest/properties/const.rb +2 -2
- data/lib/verquest/properties/enum.rb +2 -2
- data/lib/verquest/properties/field.rb +2 -2
- data/lib/verquest/properties/object.rb +1 -1
- data/lib/verquest/properties/one_of.rb +437 -0
- data/lib/verquest/properties/reference.rb +2 -2
- data/lib/verquest/transformer.rb +813 -48
- data/lib/verquest/version.rb +240 -11
- metadata +2 -1
data/lib/verquest/version.rb
CHANGED
|
@@ -113,11 +113,19 @@ module Verquest
|
|
|
113
113
|
|
|
114
114
|
schema_options.delete_if { |_, v| v.nil? }
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
if combination?
|
|
117
|
+
prepare_combination_schema
|
|
118
|
+
prepare_combination_validation_schema
|
|
119
|
+
prepare_combination_mapping
|
|
120
|
+
prepare_combination_external_mapping
|
|
121
|
+
@transformer = Transformer.new(mapping: mapping, discriminator: combination_discriminator)
|
|
122
|
+
else
|
|
123
|
+
prepare_schema
|
|
124
|
+
prepare_validation_schema
|
|
125
|
+
prepare_mapping
|
|
126
|
+
prepare_external_mapping
|
|
127
|
+
@transformer = Transformer.new(mapping: mapping)
|
|
128
|
+
end
|
|
121
129
|
|
|
122
130
|
freeze
|
|
123
131
|
end
|
|
@@ -196,6 +204,45 @@ module Verquest
|
|
|
196
204
|
transformer.call(params)
|
|
197
205
|
end
|
|
198
206
|
|
|
207
|
+
# Check if this version is a combination schema (root-level oneOf)
|
|
208
|
+
#
|
|
209
|
+
# A combination schema has a single root-level oneOf property (name is nil)
|
|
210
|
+
# where the entire request body matches one of the defined schemas.
|
|
211
|
+
#
|
|
212
|
+
# @return [Boolean] true if this is a combination schema
|
|
213
|
+
def combination?
|
|
214
|
+
return @_combination if defined?(@_combination)
|
|
215
|
+
|
|
216
|
+
@_combination = properties.values.count == 1 &&
|
|
217
|
+
properties.values.first&.is_a?(Verquest::Properties::OneOf) &&
|
|
218
|
+
properties.values.first.name.nil?
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Check if this version has a nested oneOf property (oneOf with a name)
|
|
222
|
+
#
|
|
223
|
+
# @return [Boolean] true if there's a nested oneOf property
|
|
224
|
+
def has_nested_one_of?
|
|
225
|
+
nested_one_of_count > 0
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Check if this version has multiple nested oneOf properties
|
|
229
|
+
#
|
|
230
|
+
# @return [Boolean] true if there are multiple nested oneOf properties
|
|
231
|
+
def has_multiple_nested_one_of?
|
|
232
|
+
nested_one_of_count > 1
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Returns the count of nested oneOf properties (computed once and cached)
|
|
236
|
+
#
|
|
237
|
+
# @return [Integer] Number of nested oneOf properties
|
|
238
|
+
def nested_one_of_count
|
|
239
|
+
return @_nested_one_of_count if defined?(@_nested_one_of_count)
|
|
240
|
+
|
|
241
|
+
@_nested_one_of_count = properties.values.count do |p|
|
|
242
|
+
p.is_a?(Verquest::Properties::OneOf) && !p.name.nil?
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
199
246
|
private
|
|
200
247
|
|
|
201
248
|
# Generates the JSON schema for this version
|
|
@@ -208,14 +255,24 @@ module Verquest
|
|
|
208
255
|
def prepare_schema
|
|
209
256
|
@schema = {
|
|
210
257
|
"type" => "object",
|
|
211
|
-
"description" => description,
|
|
212
258
|
"required" => required_properties,
|
|
213
259
|
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
|
214
260
|
}.merge(schema_options).tap do |schema|
|
|
215
261
|
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
|
262
|
+
schema["description"] = description if description
|
|
216
263
|
end.freeze
|
|
217
264
|
end
|
|
218
265
|
|
|
266
|
+
# Generates the JSON schema for combination schemas (oneOf at root level)
|
|
267
|
+
#
|
|
268
|
+
# For combination schemas, the schema is delegated directly to the oneOf
|
|
269
|
+
# property since it represents the entire request structure.
|
|
270
|
+
#
|
|
271
|
+
# @return [Hash] The schema from the oneOf property
|
|
272
|
+
def prepare_combination_schema
|
|
273
|
+
@schema = properties.values.first.to_schema
|
|
274
|
+
end
|
|
275
|
+
|
|
219
276
|
# Generates the validation schema for this version
|
|
220
277
|
#
|
|
221
278
|
# Similar to prepare_schema but specifically for validation purposes.
|
|
@@ -225,28 +282,114 @@ module Verquest
|
|
|
225
282
|
def prepare_validation_schema
|
|
226
283
|
@validation_schema = {
|
|
227
284
|
"type" => "object",
|
|
228
|
-
"description" => description,
|
|
229
285
|
"required" => required_properties,
|
|
230
286
|
"properties" => properties.transform_values { |property| property.to_validation_schema(version: name)[property.name] }
|
|
231
287
|
}.merge(schema_options).tap do |schema|
|
|
232
288
|
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
|
289
|
+
schema["description"] = description if description
|
|
233
290
|
end.freeze
|
|
234
291
|
end
|
|
235
292
|
|
|
293
|
+
# Generates the validation schema for combination schemas (oneOf at root level)
|
|
294
|
+
#
|
|
295
|
+
# For combination schemas, the validation schema is delegated directly to
|
|
296
|
+
# the oneOf property, which includes inline schema definitions for each option.
|
|
297
|
+
#
|
|
298
|
+
# @return [Hash] The validation schema from the oneOf property
|
|
299
|
+
def prepare_combination_validation_schema
|
|
300
|
+
@validation_schema = properties.values.first.to_validation_schema(version: name)
|
|
301
|
+
end
|
|
302
|
+
|
|
236
303
|
# Prepares the parameter mapping for this version
|
|
237
304
|
#
|
|
238
305
|
# Collects mappings from all properties in this version and checks for
|
|
239
306
|
# duplicate mappings, which would cause conflicts during transformation.
|
|
240
307
|
#
|
|
308
|
+
# When nested oneOf properties are present, the mapping includes a _oneOfs array
|
|
309
|
+
# containing each oneOf's metadata and variant mappings, plus base properties.
|
|
310
|
+
#
|
|
241
311
|
# @return [Hash] The mapping from schema property paths to internal paths
|
|
242
312
|
# @raise [MappingError] If duplicate mappings are detected
|
|
243
313
|
def prepare_mapping
|
|
244
|
-
|
|
314
|
+
# Separate oneOf properties from regular properties
|
|
315
|
+
one_of_properties = properties.values.select { |p| p.is_a?(Verquest::Properties::OneOf) }
|
|
316
|
+
regular_properties = properties.values.reject { |p| p.is_a?(Verquest::Properties::OneOf) }
|
|
317
|
+
|
|
318
|
+
if one_of_properties.size == 1
|
|
319
|
+
prepare_single_nested_one_of_mapping(one_of_properties.first, regular_properties)
|
|
320
|
+
elsif one_of_properties.size > 1
|
|
321
|
+
prepare_multiple_nested_one_of_mapping(one_of_properties, regular_properties)
|
|
322
|
+
else
|
|
323
|
+
prepare_flat_mapping(regular_properties)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Prepares mapping for versions with a single nested oneOf property (legacy format)
|
|
328
|
+
#
|
|
329
|
+
# @param one_of_property [Verquest::Properties::OneOf] The nested oneOf property
|
|
330
|
+
# @param regular_properties [Array<Verquest::Properties::Base>] Non-oneOf properties
|
|
331
|
+
# @return [void]
|
|
332
|
+
def prepare_single_nested_one_of_mapping(one_of_property, regular_properties)
|
|
333
|
+
# Collect regular property mappings
|
|
334
|
+
regular_mapping = {}
|
|
335
|
+
regular_properties.each do |property|
|
|
336
|
+
property.mapping(key_prefix: [], value_prefix: [], mapping: regular_mapping, version: name)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Collect oneOf property mappings
|
|
340
|
+
one_of_mapping = {}
|
|
341
|
+
one_of_property.mapping(key_prefix: [], value_prefix: [], mapping: one_of_mapping, version: name)
|
|
342
|
+
|
|
343
|
+
# Merge regular mappings into each oneOf variant
|
|
344
|
+
@mapping = {}
|
|
345
|
+
|
|
346
|
+
# Preserve metadata keys
|
|
347
|
+
%w[_discriminator _variant_schemas _variant_path _nullable _nullable_path _nullable_target_path].each do |metadata_key|
|
|
348
|
+
@mapping[metadata_key] = one_of_mapping[metadata_key] if one_of_mapping.key?(metadata_key)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
one_of_mapping.each do |discriminator_value, variant_mapping|
|
|
352
|
+
next if discriminator_value.start_with?("_") # Skip metadata keys
|
|
353
|
+
|
|
354
|
+
@mapping[discriminator_value] = regular_mapping.merge(variant_mapping)
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Prepares mapping for versions with multiple nested oneOf properties
|
|
359
|
+
#
|
|
360
|
+
# @param one_of_properties [Array<Verquest::Properties::OneOf>] The nested oneOf properties
|
|
361
|
+
# @param regular_properties [Array<Verquest::Properties::Base>] Non-oneOf properties
|
|
362
|
+
# @return [void]
|
|
363
|
+
def prepare_multiple_nested_one_of_mapping(one_of_properties, regular_properties)
|
|
364
|
+
@mapping = {}
|
|
365
|
+
|
|
366
|
+
# Collect regular property mappings at root level
|
|
367
|
+
regular_properties.each do |property|
|
|
368
|
+
property.mapping(key_prefix: [], value_prefix: [], mapping: @mapping, version: name)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# Collect each oneOf's mapping into _oneOfs array
|
|
372
|
+
@mapping["_oneOfs"] = one_of_properties.map do |one_of_property|
|
|
373
|
+
one_of_mapping = {}
|
|
374
|
+
one_of_property.mapping(key_prefix: [], value_prefix: [], mapping: one_of_mapping, version: name)
|
|
375
|
+
one_of_mapping
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Prepares flat mapping for versions without nested oneOf
|
|
380
|
+
#
|
|
381
|
+
# @param properties_list [Array<Verquest::Properties::Base>] Properties to map
|
|
382
|
+
# @return [void]
|
|
383
|
+
# @raise [MappingError] If duplicate mappings are detected
|
|
384
|
+
def prepare_flat_mapping(properties_list)
|
|
385
|
+
@mapping = properties_list.each_with_object({}) do |property, mapping|
|
|
245
386
|
property.mapping(key_prefix: [], value_prefix: [], mapping: mapping, version: name)
|
|
246
387
|
end
|
|
247
388
|
|
|
248
|
-
|
|
249
|
-
|
|
389
|
+
seen = Set.new
|
|
390
|
+
duplicates = mapping.values.select { |v| !seen.add?(v) }
|
|
391
|
+
if duplicates.any?
|
|
392
|
+
raise MappingError.new("Mapping must be unique. Found duplicates in version '#{name}': #{duplicates.uniq.join(", ")}")
|
|
250
393
|
end
|
|
251
394
|
end
|
|
252
395
|
|
|
@@ -256,11 +399,97 @@ module Verquest
|
|
|
256
399
|
# attribute names back to external parameter names. This is useful when
|
|
257
400
|
# transforming internal data back to the external API representation.
|
|
258
401
|
#
|
|
402
|
+
# For nested oneOf schemas, inverts each discriminator value's mapping.
|
|
403
|
+
# Skips metadata keys that are not variant mappings.
|
|
404
|
+
#
|
|
259
405
|
# @return [Hash] The frozen inverted mapping where keys are internal attribute
|
|
260
406
|
# paths and values are the corresponding external schema paths
|
|
261
407
|
# @see #prepare_mapping
|
|
262
408
|
def prepare_external_mapping
|
|
263
|
-
@external_mapping =
|
|
409
|
+
@external_mapping = if has_multiple_nested_one_of?
|
|
410
|
+
invert_multiple_one_of_mapping
|
|
411
|
+
elsif has_nested_one_of?
|
|
412
|
+
invert_single_one_of_mapping
|
|
413
|
+
else
|
|
414
|
+
mapping.invert.freeze
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Inverts mapping for single nested oneOf
|
|
419
|
+
#
|
|
420
|
+
# @return [Hash] The inverted mapping
|
|
421
|
+
def invert_single_one_of_mapping
|
|
422
|
+
mapping.each_with_object({}) do |(key, value), result|
|
|
423
|
+
result[key] = if key.start_with?("_")
|
|
424
|
+
value
|
|
425
|
+
else
|
|
426
|
+
value.invert
|
|
427
|
+
end
|
|
428
|
+
end.freeze
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# Inverts mapping for multiple nested oneOf
|
|
432
|
+
#
|
|
433
|
+
# @return [Hash] The inverted mapping
|
|
434
|
+
def invert_multiple_one_of_mapping
|
|
435
|
+
result = {}
|
|
436
|
+
|
|
437
|
+
mapping.each do |key, value|
|
|
438
|
+
if key == "_oneOfs"
|
|
439
|
+
result["_oneOfs"] = value.map do |one_of_mapping|
|
|
440
|
+
one_of_mapping.each_with_object({}) do |(k, v), inverted|
|
|
441
|
+
inverted[k] = if k.start_with?("_")
|
|
442
|
+
v
|
|
443
|
+
else
|
|
444
|
+
v.invert
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
elsif key.start_with?("_")
|
|
449
|
+
result[key] = value
|
|
450
|
+
else
|
|
451
|
+
result[value] = key # Invert base properties
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
result.freeze
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Prepares the parameter mapping for combination schemas (oneOf)
|
|
459
|
+
#
|
|
460
|
+
# For combination schemas, the mapping is keyed by the discriminator value
|
|
461
|
+
# so the transformer can select the appropriate mapping based on the input.
|
|
462
|
+
#
|
|
463
|
+
# @return [Hash] A hash where keys are discriminator values and values are mapping hashes
|
|
464
|
+
def prepare_combination_mapping
|
|
465
|
+
@mapping = {}
|
|
466
|
+
properties.values.first.mapping(key_prefix: [], value_prefix: [], mapping: @mapping, version: name)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Prepares the inverted parameter mapping for combination schemas
|
|
470
|
+
#
|
|
471
|
+
# For combination schemas, inverts each discriminator value's mapping.
|
|
472
|
+
# Skips metadata keys that are not variant mappings.
|
|
473
|
+
#
|
|
474
|
+
# @return [Hash] The frozen inverted mapping for each discriminator value
|
|
475
|
+
def prepare_combination_external_mapping
|
|
476
|
+
@external_mapping = mapping.each_with_object({}) do |(key, value), result|
|
|
477
|
+
# Skip metadata keys, only invert variant mapping hashes
|
|
478
|
+
result[key] = if key.start_with?("_")
|
|
479
|
+
value
|
|
480
|
+
else
|
|
481
|
+
value.invert
|
|
482
|
+
end
|
|
483
|
+
end.freeze
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Returns the discriminator property name for combination schemas
|
|
487
|
+
#
|
|
488
|
+
# @return [String, nil] The discriminator property name
|
|
489
|
+
def combination_discriminator
|
|
490
|
+
return nil unless combination?
|
|
491
|
+
|
|
492
|
+
properties.values.first.send(:discriminator)
|
|
264
493
|
end
|
|
265
494
|
end
|
|
266
495
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: verquest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Petr Hlavicka
|
|
@@ -70,6 +70,7 @@ files:
|
|
|
70
70
|
- lib/verquest/properties/enum.rb
|
|
71
71
|
- lib/verquest/properties/field.rb
|
|
72
72
|
- lib/verquest/properties/object.rb
|
|
73
|
+
- lib/verquest/properties/one_of.rb
|
|
73
74
|
- lib/verquest/properties/reference.rb
|
|
74
75
|
- lib/verquest/result.rb
|
|
75
76
|
- lib/verquest/transformer.rb
|