verquest 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +141 -20
- data/lib/verquest/base/private_class_methods.rb +73 -27
- data/lib/verquest/base/public_class_methods.rb +12 -0
- data/lib/verquest/configuration.rb +8 -2
- data/lib/verquest/gem_version.rb +1 -1
- data/lib/verquest/helper_methods/required_properties.rb +48 -0
- data/lib/verquest/properties/array.rb +16 -8
- data/lib/verquest/properties/base.rb +10 -4
- data/lib/verquest/properties/collection.rb +29 -15
- data/lib/verquest/properties/const.rb +53 -0
- data/lib/verquest/properties/enum.rb +65 -0
- data/lib/verquest/properties/field.rb +12 -4
- data/lib/verquest/properties/object.rb +25 -9
- data/lib/verquest/properties/reference.rb +30 -11
- data/lib/verquest/transformer.rb +7 -7
- data/lib/verquest/version.rb +34 -5
- metadata +6 -6
@@ -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
|
@@ -67,12 +75,12 @@ module Verquest
|
|
67
75
|
# @param version [String, nil] The version to create mapping for, defaults to configuration setting
|
68
76
|
# @return [Hash] The updated mapping hash
|
69
77
|
def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
|
70
|
-
mapping[(key_prefix + [name]).join("
|
78
|
+
mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
|
71
79
|
end
|
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,19 +57,23 @@ 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
|
61
67
|
# @return [String] The target mapping key
|
62
68
|
def mapping_value_key(value_prefix:, collection: false)
|
63
69
|
value_key = if map.nil?
|
64
|
-
(value_prefix + [name]).join("
|
70
|
+
(value_prefix + [name]).join("/")
|
65
71
|
elsif map == "/"
|
66
72
|
""
|
67
73
|
elsif map.start_with?("/")
|
68
74
|
map.gsub(%r{^/}, "")
|
69
75
|
else
|
70
|
-
(value_prefix + map.split("
|
76
|
+
(value_prefix + map.split("/")).join("/")
|
71
77
|
end
|
72
78
|
|
73
79
|
if collection
|
@@ -87,9 +93,9 @@ module Verquest
|
|
87
93
|
elsif map == "/"
|
88
94
|
[]
|
89
95
|
elsif map.start_with?("/")
|
90
|
-
map.gsub(%r{^/}, "").split("
|
96
|
+
map.gsub(%r{^/}, "").split("/")
|
91
97
|
else
|
92
|
-
value_prefix + map.split("
|
98
|
+
value_prefix + map.split("/")
|
93
99
|
end
|
94
100
|
|
95
101
|
if collection && value_prefix.any?
|
@@ -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
|
@@ -128,8 +142,8 @@ module Verquest
|
|
128
142
|
value_key_prefix = mapping_value_key(value_prefix: value_prefix, collection: true)
|
129
143
|
|
130
144
|
reference_mapping = item.mapping(version:).dup
|
131
|
-
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("
|
132
|
-
reference_mapping.transform_values! { "#{value_key_prefix}
|
145
|
+
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("/")}[]/#{_1}" }
|
146
|
+
reference_mapping.transform_values! { "#{value_key_prefix}/#{_1}" }
|
133
147
|
|
134
148
|
mapping.merge!(reference_mapping)
|
135
149
|
else
|
@@ -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
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verquest
|
4
|
+
module Properties
|
5
|
+
# The Const class represents a constant property with a fixed value in a JSON schema.
|
6
|
+
# It's used for properties that must have a specific, immutable value.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# const = Const.new(name: "type", value: "user")
|
10
|
+
class Const < Base
|
11
|
+
# Initialize a new constant property
|
12
|
+
#
|
13
|
+
# @param name [String, Symbol] The name of the constant property
|
14
|
+
# @param value [Object] The fixed value of the constant (can be any scalar value)
|
15
|
+
# @param map [Object, nil] Optional mapping information
|
16
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names (can be overridden by custom type)
|
17
|
+
# @param schema_options [Hash] Additional JSON schema options for this property
|
18
|
+
def initialize(name:, value:, map: nil, required: false, **schema_options)
|
19
|
+
@name = name.to_s
|
20
|
+
@value = value
|
21
|
+
@map = map
|
22
|
+
@required = required
|
23
|
+
@schema_options = schema_options&.transform_keys(&:to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generate JSON schema definition for this constant
|
27
|
+
#
|
28
|
+
# @return [Hash] The schema definition for this constant
|
29
|
+
def to_schema
|
30
|
+
{
|
31
|
+
name => {
|
32
|
+
"const" => value
|
33
|
+
}.merge(schema_options)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create mapping for this const property
|
38
|
+
#
|
39
|
+
# @param key_prefix [Array<Symbol>] Prefix for the source key
|
40
|
+
# @param value_prefix [Array<String>] Prefix for the target value
|
41
|
+
# @param mapping [Hash] The mapping hash to be updated
|
42
|
+
# @param version [String, nil] The version to create mapping for
|
43
|
+
# @return [Hash] The updated mapping hash
|
44
|
+
def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
|
45
|
+
mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :value, :schema_options
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verquest
|
4
|
+
module Properties
|
5
|
+
# The Enum class represents a enum property with a list of possible values in a JSON schema.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# enum = Enum.new(name: "type", values: ["member", "admin"])
|
9
|
+
class Enum < Base
|
10
|
+
# Initialize a new Enum property
|
11
|
+
#
|
12
|
+
# @param name [String, Symbol] The name of the property
|
13
|
+
# @param values [Array] The enum values for this property
|
14
|
+
# @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names
|
15
|
+
# @param nullable [Boolean] Whether this property can be null
|
16
|
+
# @param map [String, nil] The mapping path for this property
|
17
|
+
# @param schema_options [Hash] Additional JSON schema options for this property
|
18
|
+
# @raise [ArgumentError] If attempting to map an enum to root without a name
|
19
|
+
# @raise [ArgumentError] If values is empty
|
20
|
+
# @raise [ArgumentError] If values are not unique
|
21
|
+
# @raise [ArgumentError] If only one value is provided (should use const instead)
|
22
|
+
def initialize(name:, values:, required: false, nullable: false, map: nil, **schema_options)
|
23
|
+
raise ArgumentError, "You can not map enums to the root without a name" if map == "/"
|
24
|
+
raise ArgumentError, "Values must not be empty" if values.empty?
|
25
|
+
raise ArgumentError, "Values must be unique" if values.uniq.length != values.length
|
26
|
+
raise ArgumentError, "Use const for a single value" if values.length == 1
|
27
|
+
|
28
|
+
@name = name.to_s
|
29
|
+
@values = values
|
30
|
+
@required = required
|
31
|
+
@nullable = nullable
|
32
|
+
@map = map
|
33
|
+
@schema_options = schema_options&.transform_keys(&:to_s)
|
34
|
+
|
35
|
+
if nullable && !values.include?("null")
|
36
|
+
values << "null"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generate JSON schema definition for this enum
|
41
|
+
#
|
42
|
+
# @return [Hash] The schema definition for this enum
|
43
|
+
def to_schema
|
44
|
+
{
|
45
|
+
name => {"enum" => values}.merge(schema_options)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create mapping for this enum property
|
50
|
+
#
|
51
|
+
# @param key_prefix [Array<Symbol>] Prefix for the source key
|
52
|
+
# @param value_prefix [Array<String>] Prefix for the target value
|
53
|
+
# @param mapping [Hash] The mapping hash to be updated
|
54
|
+
# @param version [String, nil] The version to create mapping for
|
55
|
+
# @return [Hash] The updated mapping hash
|
56
|
+
def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
|
57
|
+
mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :values, :schema_options
|
63
|
+
end
|
64
|
+
end
|
65
|
+
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
|
@@ -66,7 +74,7 @@ module Verquest
|
|
66
74
|
# @param version [String, nil] The version to create mapping for
|
67
75
|
# @return [Hash] The updated mapping hash
|
68
76
|
def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
|
69
|
-
mapping[(key_prefix + [name]).join("
|
77
|
+
mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
|
70
78
|
end
|
71
79
|
|
72
80
|
private
|
@@ -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
|
|
@@ -68,16 +87,16 @@ module Verquest
|
|
68
87
|
value_key_prefix = mapping_value_key(value_prefix:)
|
69
88
|
|
70
89
|
# Single field mapping
|
71
|
-
if property && reference_mapping.size == 1 && !reference_mapping.keys.first.include?("
|
90
|
+
if property && reference_mapping.size == 1 && !reference_mapping.keys.first.include?("/")
|
72
91
|
reference_mapping = {
|
73
|
-
(key_prefix + [name]).join("
|
92
|
+
(key_prefix + [name]).join("/") => value_key_prefix
|
74
93
|
}
|
75
94
|
else
|
76
|
-
if value_key_prefix != "" && !value_key_prefix.end_with?("
|
77
|
-
value_key_prefix = "#{value_key_prefix}
|
95
|
+
if value_key_prefix != "" && !value_key_prefix.end_with?("/")
|
96
|
+
value_key_prefix = "#{value_key_prefix}/"
|
78
97
|
end
|
79
98
|
|
80
|
-
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("
|
99
|
+
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("/")}/#{_1}" }
|
81
100
|
reference_mapping.transform_values! { "#{value_key_prefix}#{_1}" }
|
82
101
|
end
|
83
102
|
|
data/lib/verquest/transformer.rb
CHANGED
@@ -3,13 +3,13 @@ module Verquest
|
|
3
3
|
#
|
4
4
|
# The Transformer class handles the conversion of parameter structures based on
|
5
5
|
# a mapping of source paths to target paths. It supports deep nested structures,
|
6
|
-
# array notations, and complex path expressions using
|
6
|
+
# array notations, and complex path expressions using slash notation.
|
7
7
|
#
|
8
8
|
# @example Basic transformation
|
9
9
|
# mapping = {
|
10
|
-
# "user
|
11
|
-
# "user
|
12
|
-
# "addresses[]
|
10
|
+
# "user/firstName" => "user/first_name",
|
11
|
+
# "user/lastName" => "user/last_name",
|
12
|
+
# "addresses[]/zip" => "addresses[]/postal_code"
|
13
13
|
# }
|
14
14
|
#
|
15
15
|
# transformer = Verquest::Transformer.new(mapping: mapping)
|
@@ -84,13 +84,13 @@ module Verquest
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
# Parses a
|
87
|
+
# Parses a slash-notation path into structured path parts
|
88
88
|
# Uses memoization for performance optimization
|
89
89
|
#
|
90
|
-
# @param path [String] The
|
90
|
+
# @param path [String] The slash-notation path (e.g., "user/address/street")
|
91
91
|
# @return [Array<Hash>] Array of path parts with :key and :array attributes
|
92
92
|
def parse_path(path)
|
93
|
-
path_cache[path] ||= path.split("
|
93
|
+
path_cache[path] ||= path.split("/").map do |part|
|
94
94
|
if part.end_with?("[]")
|
95
95
|
{key: part[0...-2], array: true}
|
96
96
|
else
|
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
|
#
|
@@ -36,7 +38,10 @@ module Verquest
|
|
36
38
|
#
|
37
39
|
# @!attribute [r] transformer
|
38
40
|
# @return [Verquest::Transformer] The transformer that applies the mapping
|
39
|
-
|
41
|
+
#
|
42
|
+
# @!attribute [r] external_mapping
|
43
|
+
# @return [Hash] The mapping from internal attribute paths back to external paths
|
44
|
+
attr_reader :name, :properties, :schema, :validation_schema, :mapping, :transformer, :external_mapping
|
40
45
|
|
41
46
|
# @!attribute [rw] schema_options
|
42
47
|
# @return [Hash] Additional JSON schema options for this version
|
@@ -102,9 +107,16 @@ module Verquest
|
|
102
107
|
def prepare
|
103
108
|
return if frozen?
|
104
109
|
|
110
|
+
unless schema_options.key?("additionalProperties")
|
111
|
+
schema_options["additionalProperties"] = Verquest.configuration.default_additional_properties
|
112
|
+
end
|
113
|
+
|
114
|
+
schema_options.delete_if { |_, v| v.nil? }
|
115
|
+
|
105
116
|
prepare_schema
|
106
117
|
prepare_validation_schema
|
107
118
|
prepare_mapping
|
119
|
+
prepare_external_mapping
|
108
120
|
@transformer = Transformer.new(mapping: mapping)
|
109
121
|
|
110
122
|
freeze
|
@@ -197,9 +209,11 @@ module Verquest
|
|
197
209
|
@schema = {
|
198
210
|
"type" => "object",
|
199
211
|
"description" => description,
|
200
|
-
"required" =>
|
212
|
+
"required" => required_properties,
|
201
213
|
"properties" => properties.transform_values { |property| property.to_schema[property.name] }
|
202
|
-
}.merge(schema_options).
|
214
|
+
}.merge(schema_options).tap do |schema|
|
215
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
216
|
+
end.freeze
|
203
217
|
end
|
204
218
|
|
205
219
|
# Generates the validation schema for this version
|
@@ -212,9 +226,11 @@ module Verquest
|
|
212
226
|
@validation_schema = {
|
213
227
|
"type" => "object",
|
214
228
|
"description" => description,
|
215
|
-
"required" =>
|
229
|
+
"required" => required_properties,
|
216
230
|
"properties" => properties.transform_values { |property| property.to_validation_schema(version: name)[property.name] }
|
217
|
-
}.merge(schema_options).
|
231
|
+
}.merge(schema_options).tap do |schema|
|
232
|
+
schema["dependentRequired"] = dependent_required_properties if dependent_required_properties.any?
|
233
|
+
end.freeze
|
218
234
|
end
|
219
235
|
|
220
236
|
# Prepares the parameter mapping for this version
|
@@ -233,5 +249,18 @@ module Verquest
|
|
233
249
|
raise MappingError.new("Mapping must be unique. Found duplicates in version '#{name}': #{duplicates.join(", ")}")
|
234
250
|
end
|
235
251
|
end
|
252
|
+
|
253
|
+
# Prepares the inverted parameter mapping for this version
|
254
|
+
#
|
255
|
+
# Inverts the standard mapping to create a reverse lookup from internal
|
256
|
+
# attribute names back to external parameter names. This is useful when
|
257
|
+
# transforming internal data back to the external API representation.
|
258
|
+
#
|
259
|
+
# @return [Hash] The frozen inverted mapping where keys are internal attribute
|
260
|
+
# paths and values are the corresponding external schema paths
|
261
|
+
# @see #prepare_mapping
|
262
|
+
def prepare_external_mapping
|
263
|
+
@external_mapping = mapping.invert.freeze
|
264
|
+
end
|
236
265
|
end
|
237
266
|
end
|