verquest 0.5.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 +10 -0
- data/README.md +42 -19
- data/lib/verquest/base/private_class_methods.rb +36 -0
- data/lib/verquest/base/public_class_methods.rb +12 -0
- data/lib/verquest/gem_version.rb +1 -1
- data/lib/verquest/properties/array.rb +1 -1
- data/lib/verquest/properties/base.rb +4 -4
- data/lib/verquest/properties/collection.rb +2 -2
- data/lib/verquest/properties/const.rb +53 -0
- data/lib/verquest/properties/enum.rb +65 -0
- data/lib/verquest/properties/field.rb +1 -1
- data/lib/verquest/properties/reference.rb +5 -5
- data/lib/verquest/transformer.rb +7 -7
- data/lib/verquest/version.rb +18 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92e6047d9812ae82cf55b5d0512c459a61306c7b2cc78c9cd224e2b52b4b716c
|
4
|
+
data.tar.gz: db8943d346e30972f3b6b516d9365d7ef37dedf89fa7f4a09c5db4cb1adbe545
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0163416a3ea0f4d5cb5619b74e31ca7f2999ddccba55091d8fa8f10244a36eeb59dadd1b6d9eae6bd22d1f49e5ea7213aeb42218731f248be69d54c2127bb216
|
7
|
+
data.tar.gz: ee7adc256954aafa194a3175f50649479a8f45292d8bed51cf076f17c1d0693fbe7099e64675d249448c1e66cb9b1dce4749da71e76f36fc2d976b7387f23382
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.6.0] - 2025-07-18
|
4
|
+
|
5
|
+
### Breaking Changes
|
6
|
+
- **BREAKING:** Switching to slash notation for mapping to improve consistency with how properties are referenced in JSON Schema. Before: `example.nested.property`, now: `example/nested/property`. ([#12](https://github.com/CiTroNaK/verquest/pull/12), [@CiTroNaK](https://github.com/CiTroNaK))
|
7
|
+
|
8
|
+
### New Features
|
9
|
+
- Add support for `enum` properties. ([#13](https://github.com/CiTroNaK/verquest/pull/13), [@CiTroNaK](https://github.com/CiTroNaK))
|
10
|
+
- Add support for inverted mapping, that can be used to map internal structure to external representation (e.g., for errors). ([#10](https://github.com/CiTroNaK/verquest/pull/10), [@CiTroNaK](https://github.com/CiTroNaK))
|
11
|
+
- Add support for constants in the schema, allowing to define fixed values for properties. ([#11](https://github.com/CiTroNaK/verquest/pull/11), [@CiTroNaK](https://github.com/CiTroNaK))
|
12
|
+
|
3
13
|
## [0.5.0] - 2025-07-01
|
4
14
|
|
5
15
|
### Fixed
|
data/README.md
CHANGED
@@ -8,18 +8,18 @@ Verquest is a Ruby gem that offers an elegant solution for versioning API reques
|
|
8
8
|
- Defining versioned request structures
|
9
9
|
- Gracefully handling API versioning
|
10
10
|
- Mapping between external and internal parameter structures
|
11
|
-
- Validating parameters against [JSON Schema](https://json-schema.org/
|
11
|
+
- Validating parameters against [JSON Schema](https://json-schema.org/)
|
12
12
|
- Generating components for OpenAPI documentation
|
13
|
-
- Mapping error keys back to the external API structure
|
13
|
+
- Mapping error keys back to the external API structure
|
14
14
|
|
15
|
-
> The gem is still in development. Until version 1.0, the API may change. There are some features like `oneOf`, `anyOf`, `allOf` that are not implemented yet.
|
15
|
+
> The gem is still in development. Until version 1.0, the API may change. There are some features like `oneOf`, `anyOf`, `allOf` that are not implemented yet. See open [issues](https://github.com/CiTroNaK/verquest/issues?q=sort:updated-desc%20is:issue%20is:open%20label:enhancement).
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
|
19
19
|
Add this line to your application's Gemfile:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
gem "verquest", "~> 0.
|
22
|
+
gem "verquest", "~> 0.6"
|
23
23
|
```
|
24
24
|
|
25
25
|
And then execute:
|
@@ -75,7 +75,7 @@ class UserCreateRequest < Verquest::Base
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
|
78
|
+
enum :role, values: %w[member manager], default: "member", description: "Role of the user", required: true
|
79
79
|
|
80
80
|
object :profile_details do
|
81
81
|
field :bio, type: :string, description: "Short biography of the user"
|
@@ -89,6 +89,8 @@ class UserCreateRequest < Verquest::Base
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
92
|
+
|
93
|
+
const :company, value: "Awesome Inc."
|
92
94
|
end
|
93
95
|
end
|
94
96
|
```
|
@@ -130,7 +132,7 @@ Output:
|
|
130
132
|
{
|
131
133
|
"type" => "object",
|
132
134
|
"description" => "User Create Request",
|
133
|
-
"required" => ["first_name", "last_name", "email", "address"],
|
135
|
+
"required" => ["first_name", "last_name", "email", "address", "role"],
|
134
136
|
"properties" => {
|
135
137
|
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
136
138
|
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
@@ -151,10 +153,9 @@ Output:
|
|
151
153
|
"description" => "Permissions associated with the user"
|
152
154
|
},
|
153
155
|
"role" => {
|
154
|
-
"
|
155
|
-
"
|
156
|
-
"
|
157
|
-
"default" => "member"
|
156
|
+
"enum" => ["member", "manager"],
|
157
|
+
"default" => "member",
|
158
|
+
"description" => "Role of the user"
|
158
159
|
},
|
159
160
|
"profile_details" => {
|
160
161
|
"type" => "object",
|
@@ -172,7 +173,8 @@ Output:
|
|
172
173
|
"description" => "Some social networks"
|
173
174
|
}
|
174
175
|
}
|
175
|
-
}
|
176
|
+
},
|
177
|
+
"company" => {"const" => "Awesome Inc."}
|
176
178
|
},
|
177
179
|
"additionalProperties" => false
|
178
180
|
}
|
@@ -191,7 +193,7 @@ Output:
|
|
191
193
|
{
|
192
194
|
"type" => "object",
|
193
195
|
"description" => "User Create Request",
|
194
|
-
"required" => ["first_name", "last_name", "email", "address"],
|
196
|
+
"required" => ["first_name", "last_name", "email", "address", "role"],
|
195
197
|
"properties" => {
|
196
198
|
"first_name" => {"type" => "string", "description" => "The first name of the user", "maxLength" => 50},
|
197
199
|
"last_name" => {"type" => "string", "description" => "The last name of the user", "maxLength" => 50},
|
@@ -222,10 +224,9 @@ Output:
|
|
222
224
|
"description" => "Permissions associated with the user"
|
223
225
|
},
|
224
226
|
"role" => {
|
225
|
-
"type" => "string",
|
226
|
-
"description" => "Role of the user",
|
227
227
|
"enum" => ["member", "manager"],
|
228
|
-
"default" => "member"
|
228
|
+
"default" => "member",
|
229
|
+
"description" => "Role of the user"
|
229
230
|
},
|
230
231
|
"profile_details" => {"type" => "object",
|
231
232
|
"required" => [],
|
@@ -240,7 +241,8 @@ Output:
|
|
240
241
|
"mastodon" => {"type" => "string", "format" => "uri", "description" => "Mastodon profile URL"}
|
241
242
|
},
|
242
243
|
"description" => "Some social networks"}
|
243
|
-
}
|
244
|
+
},
|
245
|
+
"company" => {"const" => "Awesome Inc."}
|
244
246
|
}
|
245
247
|
},
|
246
248
|
"additionalProperties" => false
|
@@ -265,10 +267,12 @@ The JSON schema can be used for both validation of incoming parameters and for g
|
|
265
267
|
#### Component types
|
266
268
|
|
267
269
|
- `field`: Represents a scalar value (string, integer, boolean, etc.).
|
270
|
+
- `enum`: Represents a property with a limited set of values (enumeration).
|
268
271
|
- `object`: Represents a JSON object with properties.
|
269
272
|
- `array`: Represents a JSON array with scalar items.
|
270
273
|
- `collection`: Represents a array of objects defined manually or by a reference to another request.
|
271
274
|
- `reference`: Represents a reference to another request, allowing you to reuse existing request structures.
|
275
|
+
- `const`: Represents a [constant](https://json-schema.org/understanding-json-schema/reference/const#constant-values) value that is always present in the request.
|
272
276
|
|
273
277
|
#### Helper methods
|
274
278
|
|
@@ -276,7 +280,7 @@ The JSON schema can be used for both validation of incoming parameters and for g
|
|
276
280
|
- `schema_options`: Allows you to set additional options for the JSON Schema, such as `additional_properties` for request or per version. All fields (except `reference`) can be defined with options like `required`, `format`, `min_lenght`, `max_length`, etc. all in snake case.
|
277
281
|
- `with_options`: Allows you to define multiple fields with the same options, reducing repetition.
|
278
282
|
|
279
|
-
|
283
|
+
#### Required properties
|
280
284
|
|
281
285
|
You can define required properties in your request schema by setting the `required` option to `true`, or provide a list of dependent required properties. This feature is based on the latest [JSON Schema specification](https://json-schema.org/understanding-json-schema/reference/conditionals#dependentRequired), which is also used in OpenAPI 3.1.
|
282
286
|
|
@@ -538,9 +542,28 @@ Will be transformed to:
|
|
538
542
|
|
539
543
|
What you can use:
|
540
544
|
- `/` to reference the root of the request structure
|
541
|
-
- `nested
|
545
|
+
- `nested/structure` use slash notation to reference nested structures
|
542
546
|
- if the `map` is not set, the field name will be used as the key in the internal structure
|
543
547
|
|
548
|
+
To get the mapping to map the request structure back to the external API structure, you can use the `external_mapping` method:
|
549
|
+
|
550
|
+
```ruby
|
551
|
+
UserCreateRequest.external_mapping(version: "2025-06")
|
552
|
+
```
|
553
|
+
|
554
|
+
Will produce the following mapping:
|
555
|
+
|
556
|
+
```ruby
|
557
|
+
{
|
558
|
+
"name" => "full_name",
|
559
|
+
"email" => "email",
|
560
|
+
"phone" => "phone",
|
561
|
+
"address_street" => "address/street",
|
562
|
+
"address_city" => "address/city",
|
563
|
+
"address_zip" => "address/postal_code"
|
564
|
+
}
|
565
|
+
```
|
566
|
+
|
544
567
|
There are some limitations and the implementation can be improved, but it should works for most common use cases.
|
545
568
|
|
546
569
|
See the mapping test (in `test/verquest/base_test.rb`) for more examples of mapping.
|
@@ -586,7 +609,7 @@ end
|
|
586
609
|
|
587
610
|
## Documentation
|
588
611
|
|
589
|
-
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.
|
612
|
+
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest/0.6.0/).
|
590
613
|
|
591
614
|
## Development
|
592
615
|
|
@@ -138,6 +138,42 @@ module Verquest
|
|
138
138
|
current_scope.add(field)
|
139
139
|
end
|
140
140
|
|
141
|
+
# Defines a new enum property for the current version scope
|
142
|
+
#
|
143
|
+
# @param name [Symbol] The name of the enum
|
144
|
+
# @param values [Array] The possible values for the enum
|
145
|
+
# @param map [String, nil] An optional mapping to another property
|
146
|
+
# @param required [Boolean, Array<Symbol>] Whether the enum is required
|
147
|
+
# @param nullable [Boolean] Whether the enum can be null
|
148
|
+
# @param schema_options [Hash] Additional schema options for the enum
|
149
|
+
# @return [void]
|
150
|
+
def enum(name, values:, map: nil, required: nil, nullable: nil, **schema_options)
|
151
|
+
camelize(schema_options)
|
152
|
+
|
153
|
+
required = default_options.fetch(:required, false) if required.nil?
|
154
|
+
nullable = default_options.fetch(:nullable, false) if nullable.nil?
|
155
|
+
schema_options = default_options.except(:required, :nullable).merge(schema_options)
|
156
|
+
|
157
|
+
enum_property = Properties::Enum.new(name:, values:, map:, required:, nullable:, **schema_options)
|
158
|
+
current_scope.add(enum_property)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Defines a new constant property for the current version scope
|
162
|
+
#
|
163
|
+
# @param name [Symbol] The name of the constant
|
164
|
+
# @param value [Object] The value of the constant
|
165
|
+
# @param map [String, nil] An optional mapping to another constant
|
166
|
+
# @param required [Boolean, Array<Symbol>] Whether the constant is required
|
167
|
+
# @param schema_options [Hash] Additional schema options for the constant
|
168
|
+
# @return [void]
|
169
|
+
def const(name, value:, map: nil, required: nil, **schema_options)
|
170
|
+
camelize(schema_options)
|
171
|
+
required = default_options.fetch(:required, false) if required.nil?
|
172
|
+
|
173
|
+
const = Properties::Const.new(name:, value:, map:, required:, **schema_options)
|
174
|
+
current_scope.add(const)
|
175
|
+
end
|
176
|
+
|
141
177
|
# Defines a new object for the current version scope
|
142
178
|
#
|
143
179
|
# @param name [Symbol] The name of the object
|
@@ -102,6 +102,18 @@ module Verquest
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
# Returns the external mapping for a specific version
|
106
|
+
#
|
107
|
+
# This method returns a mapping hash that translates from internal attribute names back to external parameter names.
|
108
|
+
#
|
109
|
+
# @param version [String, nil] Specific version to use, defaults to configuration setting
|
110
|
+
# @return [Hash] The inverted mapping configuration where keys are internal names and values are external names
|
111
|
+
# @see #mapping
|
112
|
+
def external_mapping(version: nil)
|
113
|
+
version = resolve(version)
|
114
|
+
version.external_mapping
|
115
|
+
end
|
116
|
+
|
105
117
|
# Returns the JSON reference for the request or a specific property
|
106
118
|
#
|
107
119
|
# @param property [String, Symbol, nil] Specific property to retrieve reference for
|
data/lib/verquest/gem_version.rb
CHANGED
@@ -75,7 +75,7 @@ module Verquest
|
|
75
75
|
# @param version [String, nil] The version to create mapping for, defaults to configuration setting
|
76
76
|
# @return [Hash] The updated mapping hash
|
77
77
|
def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
|
78
|
-
mapping[(key_prefix + [name]).join("
|
78
|
+
mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
|
79
79
|
end
|
80
80
|
|
81
81
|
private
|
@@ -67,13 +67,13 @@ module Verquest
|
|
67
67
|
# @return [String] The target mapping key
|
68
68
|
def mapping_value_key(value_prefix:, collection: false)
|
69
69
|
value_key = if map.nil?
|
70
|
-
(value_prefix + [name]).join("
|
70
|
+
(value_prefix + [name]).join("/")
|
71
71
|
elsif map == "/"
|
72
72
|
""
|
73
73
|
elsif map.start_with?("/")
|
74
74
|
map.gsub(%r{^/}, "")
|
75
75
|
else
|
76
|
-
(value_prefix + map.split("
|
76
|
+
(value_prefix + map.split("/")).join("/")
|
77
77
|
end
|
78
78
|
|
79
79
|
if collection
|
@@ -93,9 +93,9 @@ module Verquest
|
|
93
93
|
elsif map == "/"
|
94
94
|
[]
|
95
95
|
elsif map.start_with?("/")
|
96
|
-
map.gsub(%r{^/}, "").split("
|
96
|
+
map.gsub(%r{^/}, "").split("/")
|
97
97
|
else
|
98
|
-
value_prefix + map.split("
|
98
|
+
value_prefix + map.split("/")
|
99
99
|
end
|
100
100
|
|
101
101
|
if collection && value_prefix.any?
|
@@ -142,8 +142,8 @@ module Verquest
|
|
142
142
|
value_key_prefix = mapping_value_key(value_prefix: value_prefix, collection: true)
|
143
143
|
|
144
144
|
reference_mapping = item.mapping(version:).dup
|
145
|
-
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("
|
146
|
-
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}" }
|
147
147
|
|
148
148
|
mapping.merge!(reference_mapping)
|
149
149
|
else
|
@@ -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
|
@@ -74,7 +74,7 @@ module Verquest
|
|
74
74
|
# @param version [String, nil] The version to create mapping for
|
75
75
|
# @return [Hash] The updated mapping hash
|
76
76
|
def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
|
77
|
-
mapping[(key_prefix + [name]).join("
|
77
|
+
mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
|
78
78
|
end
|
79
79
|
|
80
80
|
private
|
@@ -87,16 +87,16 @@ module Verquest
|
|
87
87
|
value_key_prefix = mapping_value_key(value_prefix:)
|
88
88
|
|
89
89
|
# Single field mapping
|
90
|
-
if property && reference_mapping.size == 1 && !reference_mapping.keys.first.include?("
|
90
|
+
if property && reference_mapping.size == 1 && !reference_mapping.keys.first.include?("/")
|
91
91
|
reference_mapping = {
|
92
|
-
(key_prefix + [name]).join("
|
92
|
+
(key_prefix + [name]).join("/") => value_key_prefix
|
93
93
|
}
|
94
94
|
else
|
95
|
-
if value_key_prefix != "" && !value_key_prefix.end_with?("
|
96
|
-
value_key_prefix = "#{value_key_prefix}
|
95
|
+
if value_key_prefix != "" && !value_key_prefix.end_with?("/")
|
96
|
+
value_key_prefix = "#{value_key_prefix}/"
|
97
97
|
end
|
98
98
|
|
99
|
-
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("
|
99
|
+
reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("/")}/#{_1}" }
|
100
100
|
reference_mapping.transform_values! { "#{value_key_prefix}#{_1}" }
|
101
101
|
end
|
102
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
@@ -38,7 +38,10 @@ module Verquest
|
|
38
38
|
#
|
39
39
|
# @!attribute [r] transformer
|
40
40
|
# @return [Verquest::Transformer] The transformer that applies the mapping
|
41
|
-
|
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
|
42
45
|
|
43
46
|
# @!attribute [rw] schema_options
|
44
47
|
# @return [Hash] Additional JSON schema options for this version
|
@@ -113,6 +116,7 @@ module Verquest
|
|
113
116
|
prepare_schema
|
114
117
|
prepare_validation_schema
|
115
118
|
prepare_mapping
|
119
|
+
prepare_external_mapping
|
116
120
|
@transformer = Transformer.new(mapping: mapping)
|
117
121
|
|
118
122
|
freeze
|
@@ -245,5 +249,18 @@ module Verquest
|
|
245
249
|
raise MappingError.new("Mapping must be unique. Found duplicates in version '#{name}': #{duplicates.join(", ")}")
|
246
250
|
end
|
247
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
|
248
265
|
end
|
249
266
|
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.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Hlavicka
|
@@ -66,6 +66,8 @@ files:
|
|
66
66
|
- lib/verquest/properties/array.rb
|
67
67
|
- lib/verquest/properties/base.rb
|
68
68
|
- lib/verquest/properties/collection.rb
|
69
|
+
- lib/verquest/properties/const.rb
|
70
|
+
- lib/verquest/properties/enum.rb
|
69
71
|
- lib/verquest/properties/field.rb
|
70
72
|
- lib/verquest/properties/object.rb
|
71
73
|
- lib/verquest/properties/reference.rb
|
@@ -95,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
97
|
- !ruby/object:Gem::Version
|
96
98
|
version: '0'
|
97
99
|
requirements: []
|
98
|
-
rubygems_version: 3.6.
|
100
|
+
rubygems_version: 3.6.7
|
99
101
|
specification_version: 4
|
100
102
|
summary: Verquest is a Ruby gem that offers an elegant solution for versioning API
|
101
103
|
requests
|