grape-oas 1.0.0 → 1.0.1
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 +11 -0
- data/RELEASING.md +2 -2
- data/grape-oas.gemspec +9 -5
- data/lib/grape_oas/api_model_builders/request.rb +1 -1
- data/lib/grape_oas/api_model_builders/request_params.rb +44 -5
- data/lib/grape_oas/api_model_builders/request_params_support/param_location_resolver.rb +30 -4
- data/lib/grape_oas/constants.rb +9 -0
- data/lib/grape_oas/exporter/oas2/parameter.rb +34 -1
- data/lib/grape_oas/exporter/oas3/schema.rb +8 -8
- data/lib/grape_oas/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54679fa806e8eae3cbff18605ccd3dd7e188e7a2cf774d92ea233172fefc94ef
|
|
4
|
+
data.tar.gz: ee59342a9d6c9be4bb79cf081beaaf833fae65bdfbaf12b619765eb37d1119a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 21f0ab789f3812daa0fa3971d2bb9ad9693351ee2178c1a2ac9c3f3e706f51023c1bf67bdb220098a74af19cd3eb90c42521a8bdc9992ef7af8594089e58c897
|
|
7
|
+
data.tar.gz: b6eef41e2c1eac91c3c41aa4e225f55a7650908d64133ec816e1e2c7bb3461282146020e6a181cc30156b85d11259ea2ae3aee50184e8b471420dbf0cd7df2ec
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
|
|
9
|
+
## [1.0.1] - 2025-12-15
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
- [#8](https://github.com/numbata/grape-oas/pull/8): Add OAS2 parameter schema constraint export with enum normalization and retain zero-valued constraints across OAS exporters. - [@numbata](https://github.com/numbata).
|
|
14
|
+
- [#9](https://github.com/numbata/grape-oas/pull/9): Treat GET/HEAD/DELETE as bodyless by default via shared constants and tests - [@numbata](https://github.com/numbata).
|
|
15
|
+
- [#10](https://github.com/numbata/grape-oas/pull/10): Add grape-swagger compatible `in:` location syntax for parameters alongside `param_type` - [@numbata](https://github.com/numbata).
|
|
16
|
+
- [#11](https://github.com/numbata/grape-oas/pull/11): Flatten nested Hash params to bracket-notation query params for GET/HEAD/DELETE requests - [@numbata](https://github.com/numbata).
|
|
17
|
+
- [#12](https://github.com/numbata/grape-oas/pull/12): Add fallback to `spec[:desc]` for parameter descriptions when `documentation[:desc]` is not set - [@numbata](https://github.com/numbata).
|
|
18
|
+
|
|
8
19
|
## [1.0.0] - 2025-12-06
|
|
9
20
|
|
|
10
21
|
### Added
|
data/RELEASING.md
CHANGED
|
@@ -80,7 +80,7 @@ git push origin main
|
|
|
80
80
|
|
|
81
81
|
This task will:
|
|
82
82
|
- Bump the patch version in `lib/grape_oas/version.rb`
|
|
83
|
-
- Ensure CHANGELOG.md has an "Unreleased" section
|
|
83
|
+
- Ensure CHANGELOG.md has an "Unreleased" section with "* Your contribution here" placeholder
|
|
84
84
|
- Commit the changes with message "Prepare for next development iteration"
|
|
85
85
|
|
|
86
86
|
**Note**: The task is idempotent - safe to run multiple times without duplicating work.
|
|
@@ -89,7 +89,7 @@ This task will:
|
|
|
89
89
|
<summary>Manual Post-Release Steps (if needed)</summary>
|
|
90
90
|
|
|
91
91
|
1. **Prepare for next development cycle**:
|
|
92
|
-
- Add "Unreleased" section to CHANGELOG.md
|
|
92
|
+
- Add "Unreleased" section to CHANGELOG.md with "* Your contribution here" placeholder
|
|
93
93
|
- Bump version in `lib/grape_oas/version.rb`
|
|
94
94
|
|
|
95
95
|
2. **Commit post-release changes**:
|
data/grape-oas.gemspec
CHANGED
|
@@ -14,14 +14,18 @@ Gem::Specification.new do |spec|
|
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
spec.required_ruby_version = ">= 3.2"
|
|
16
16
|
|
|
17
|
-
spec.metadata
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
spec.metadata = {
|
|
18
|
+
"homepage_uri" => spec.homepage,
|
|
19
|
+
"source_code_uri" => spec.homepage,
|
|
20
|
+
"github_repo" => "https://github.com/numbata/grape-oas",
|
|
21
|
+
"changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md",
|
|
22
|
+
"documentation_uri" => "#{spec.homepage}#readme",
|
|
23
|
+
"bug_tracker_uri" => "#{spec.homepage}/issues",
|
|
24
|
+
"rubygems_mfa_required" => "true",
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
spec.files = Dir["lib/**/*", "*.md", "LICENSE.txt", "grape-oas.gemspec"]
|
|
22
28
|
|
|
23
29
|
spec.add_dependency "grape", ">= 3.0"
|
|
24
30
|
spec.add_dependency "zeitwerk"
|
|
25
|
-
|
|
26
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
|
27
31
|
end
|
|
@@ -36,7 +36,7 @@ module GrapeOAS
|
|
|
36
36
|
# OAS spec says GET/HEAD/DELETE "MAY ignore" request bodies
|
|
37
37
|
# Skip by default unless explicitly allowed via documentation option
|
|
38
38
|
http_method = operation.http_method.to_s.downcase
|
|
39
|
-
if
|
|
39
|
+
if Constants::HttpMethods::BODYLESS_HTTP_METHODS.include?(http_method)
|
|
40
40
|
allow_body = route.options.dig(:documentation, :request_body) ||
|
|
41
41
|
route.options[:request_body]
|
|
42
42
|
return unless allow_body
|
|
@@ -71,17 +71,34 @@ module GrapeOAS
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
# Extracts non-body params (path, query, header) from flat params.
|
|
74
|
+
# For non-body HTTP methods (GET, HEAD, DELETE), also includes nested params
|
|
75
|
+
# as flat query parameters with bracket notation (e.g., "tax_id[type]"),
|
|
76
|
+
# unless request_body is explicitly enabled.
|
|
74
77
|
def extract_non_body_params(all_params, route_params)
|
|
75
78
|
params = []
|
|
79
|
+
http_method = route.request_method.to_s.downcase
|
|
80
|
+
flatten_nested = should_flatten_nested_to_query?(http_method, all_params)
|
|
76
81
|
|
|
77
82
|
all_params.each do |name, spec|
|
|
78
|
-
# Skip nested params (they go into body)
|
|
79
|
-
next if name.include?("[")
|
|
80
|
-
# Skip Hash/body params
|
|
81
|
-
next if location_resolver.body_param?(spec)
|
|
82
83
|
# Skip hidden params
|
|
83
84
|
next if location_resolver.hidden_parameter?(spec)
|
|
84
85
|
|
|
86
|
+
is_nested = name.include?("[")
|
|
87
|
+
is_hash_param = location_resolver.body_param?(spec)
|
|
88
|
+
|
|
89
|
+
# For nested bracket params (e.g., "tax_id[type]"), include as query params
|
|
90
|
+
# for non-body HTTP methods (unless request_body is explicitly enabled)
|
|
91
|
+
if is_nested
|
|
92
|
+
next unless flatten_nested
|
|
93
|
+
|
|
94
|
+
params << build_parameter(name, "query", spec[:required] || false, schema_builder.build(spec), spec)
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Skip Hash type params (they're handled via nested bracket params above
|
|
99
|
+
# or via body schema for POST/PUT/PATCH)
|
|
100
|
+
next if is_hash_param
|
|
101
|
+
|
|
85
102
|
location = location_resolver.resolve(
|
|
86
103
|
name: name,
|
|
87
104
|
spec: spec,
|
|
@@ -97,13 +114,35 @@ module GrapeOAS
|
|
|
97
114
|
params
|
|
98
115
|
end
|
|
99
116
|
|
|
117
|
+
# Determines whether nested params should be flattened to query params.
|
|
118
|
+
# Returns true for GET/HEAD/DELETE unless body is explicitly requested via:
|
|
119
|
+
# - route-level `request_body: true` option
|
|
120
|
+
# - any parameter with `documentation: { in: 'body' }` or `documentation: { param_type: 'body' }`
|
|
121
|
+
def should_flatten_nested_to_query?(http_method, all_params)
|
|
122
|
+
return false unless Constants::HttpMethods::BODYLESS_HTTP_METHODS.include?(http_method)
|
|
123
|
+
|
|
124
|
+
# If request_body is explicitly enabled at route level, use body schema
|
|
125
|
+
return false if route.options.dig(:documentation, :request_body) || route.options[:request_body]
|
|
126
|
+
|
|
127
|
+
# If any parameter is explicitly marked as body, use body schema
|
|
128
|
+
has_explicit_body_param = all_params.any? do |name, spec|
|
|
129
|
+
next false if name.include?("[") # Skip bracket params, check parent Hash params only
|
|
130
|
+
|
|
131
|
+
param_type = spec.dig(:documentation, :param_type)&.to_s&.downcase
|
|
132
|
+
in_location = spec.dig(:documentation, :in)&.to_s&.downcase
|
|
133
|
+
param_type == "body" || in_location == "body"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
!has_explicit_body_param
|
|
137
|
+
end
|
|
138
|
+
|
|
100
139
|
def build_parameter(name, location, required, schema, spec)
|
|
101
140
|
ApiModel::Parameter.new(
|
|
102
141
|
location: location,
|
|
103
142
|
name: name,
|
|
104
143
|
required: required,
|
|
105
144
|
schema: schema,
|
|
106
|
-
description: spec[:documentation]&.dig(:desc),
|
|
145
|
+
description: spec[:documentation]&.dig(:desc) || spec[:desc],
|
|
107
146
|
collection_format: extract_collection_format(spec),
|
|
108
147
|
)
|
|
109
148
|
end
|
|
@@ -19,20 +19,28 @@ module GrapeOAS
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# Checks if a parameter should be in the request body.
|
|
22
|
+
# Supports both `param_type: 'body'` and `in: 'body'` for grape-swagger compatibility.
|
|
22
23
|
#
|
|
23
24
|
# @param spec [Hash] the parameter specification
|
|
24
25
|
# @return [Boolean] true if it's a body parameter
|
|
25
26
|
def self.body_param?(spec)
|
|
26
|
-
spec.dig(:documentation, :param_type)
|
|
27
|
+
param_type = spec.dig(:documentation, :param_type)&.to_s&.downcase
|
|
28
|
+
in_location = spec.dig(:documentation, :in)&.to_s&.downcase
|
|
29
|
+
|
|
30
|
+
param_type == "body" || in_location == "body" || [Hash, "Hash"].include?(spec[:type])
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
# Checks if a parameter is explicitly marked as NOT a body param.
|
|
34
|
+
# Supports both `param_type` and `in` for grape-swagger compatibility.
|
|
30
35
|
#
|
|
31
36
|
# @param spec [Hash] the parameter specification
|
|
32
37
|
# @return [Boolean] true if explicitly non-body
|
|
33
38
|
def self.explicit_non_body_param?(spec)
|
|
34
39
|
param_type = spec.dig(:documentation, :param_type)&.to_s&.downcase
|
|
35
|
-
|
|
40
|
+
in_location = spec.dig(:documentation, :in)&.to_s&.downcase
|
|
41
|
+
location = param_type || in_location
|
|
42
|
+
|
|
43
|
+
location && %w[query header path].include?(location)
|
|
36
44
|
end
|
|
37
45
|
|
|
38
46
|
# Checks if a parameter should be hidden from documentation.
|
|
@@ -51,11 +59,29 @@ module GrapeOAS
|
|
|
51
59
|
class << self
|
|
52
60
|
private
|
|
53
61
|
|
|
62
|
+
# Extracts the parameter location from the specification.
|
|
63
|
+
# Supports both `param_type` and `in` options for grape-swagger compatibility.
|
|
64
|
+
#
|
|
65
|
+
# Precedence (highest to lowest):
|
|
66
|
+
# 1. `param_type` option (e.g., `documentation: { param_type: 'query' }`)
|
|
67
|
+
# 2. `in` option (e.g., `documentation: { in: 'query' }`)
|
|
68
|
+
# 3. Falls back to "query" if neither is specified
|
|
69
|
+
#
|
|
70
|
+
# Note: If both `param_type` and `in` are specified, `param_type` takes precedence.
|
|
71
|
+
# For example, `{ param_type: 'query', in: 'body' }` will be treated as query.
|
|
72
|
+
#
|
|
73
|
+
# @param spec [Hash] the parameter specification
|
|
74
|
+
# @param route [Object] the Grape route object
|
|
75
|
+
# @return [String] the parameter location
|
|
54
76
|
def extract_from_spec(spec, route)
|
|
55
77
|
# If body_name is set on the route, treat non-path params as body by default
|
|
56
|
-
|
|
78
|
+
param_type = spec.dig(:documentation, :param_type)
|
|
79
|
+
in_location = spec.dig(:documentation, :in)
|
|
80
|
+
return "body" if route.options[:body_name] && !param_type && !in_location
|
|
57
81
|
|
|
58
|
-
|
|
82
|
+
# Support both param_type and in for grape-swagger compatibility
|
|
83
|
+
# param_type takes precedence over in when both are specified
|
|
84
|
+
(param_type || in_location)&.to_s&.downcase || "query"
|
|
59
85
|
end
|
|
60
86
|
end
|
|
61
87
|
end
|
data/lib/grape_oas/constants.rb
CHANGED
|
@@ -16,6 +16,15 @@ module GrapeOAS
|
|
|
16
16
|
ALL = [STRING, INTEGER, NUMBER, BOOLEAN, OBJECT, ARRAY, FILE].freeze
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# HTTP method-related constants
|
|
20
|
+
module HttpMethods
|
|
21
|
+
# HTTP methods that typically don't have request bodies.
|
|
22
|
+
# Per RFC 7231, GET/HEAD/DELETE semantics don't define body behavior,
|
|
23
|
+
# but many implementations ignore them. These are treated specially
|
|
24
|
+
# when generating OpenAPI specs.
|
|
25
|
+
BODYLESS_HTTP_METHODS = %w[get head delete].freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
19
28
|
# Common MIME types
|
|
20
29
|
module MimeTypes
|
|
21
30
|
JSON = "application/json"
|
|
@@ -34,7 +34,7 @@ module GrapeOAS
|
|
|
34
34
|
def build_parameter(param)
|
|
35
35
|
type = param.schema&.type
|
|
36
36
|
format = param.schema&.format
|
|
37
|
-
primitive_types = PRIMITIVE_MAPPINGS.keys + %w[object string boolean file json array]
|
|
37
|
+
primitive_types = PRIMITIVE_MAPPINGS.keys + %w[object string boolean file json array number]
|
|
38
38
|
is_primitive = type && primitive_types.include?(type)
|
|
39
39
|
|
|
40
40
|
if is_primitive && param.location != "body"
|
|
@@ -47,6 +47,7 @@ module GrapeOAS
|
|
|
47
47
|
"type" => mapping ? mapping[:type] : type,
|
|
48
48
|
"format" => format || (mapping ? mapping[:format] : nil)
|
|
49
49
|
}
|
|
50
|
+
apply_schema_constraints(result, param.schema)
|
|
50
51
|
apply_collection_format(result, param, type)
|
|
51
52
|
result.compact
|
|
52
53
|
else
|
|
@@ -63,6 +64,38 @@ module GrapeOAS
|
|
|
63
64
|
end
|
|
64
65
|
end
|
|
65
66
|
|
|
67
|
+
def apply_schema_constraints(result, schema)
|
|
68
|
+
return unless schema
|
|
69
|
+
|
|
70
|
+
result["minimum"] = schema.minimum if schema.respond_to?(:minimum) && !schema.minimum.nil?
|
|
71
|
+
result["maximum"] = schema.maximum if schema.respond_to?(:maximum) && !schema.maximum.nil?
|
|
72
|
+
result["exclusiveMinimum"] = schema.exclusive_minimum if schema.respond_to?(:exclusive_minimum) && schema.exclusive_minimum
|
|
73
|
+
result["exclusiveMaximum"] = schema.exclusive_maximum if schema.respond_to?(:exclusive_maximum) && schema.exclusive_maximum
|
|
74
|
+
result["minLength"] = schema.min_length if schema.respond_to?(:min_length) && !schema.min_length.nil?
|
|
75
|
+
result["maxLength"] = schema.max_length if schema.respond_to?(:max_length) && !schema.max_length.nil?
|
|
76
|
+
result["minItems"] = schema.min_items if schema.respond_to?(:min_items) && !schema.min_items.nil?
|
|
77
|
+
result["maxItems"] = schema.max_items if schema.respond_to?(:max_items) && !schema.max_items.nil?
|
|
78
|
+
result["pattern"] = schema.pattern if schema.respond_to?(:pattern) && schema.pattern
|
|
79
|
+
result["enum"] = normalize_enum(schema.enum, result["type"]) if schema.respond_to?(:enum) && schema.enum
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def normalize_enum(enum_vals, type)
|
|
83
|
+
return nil unless enum_vals.is_a?(Array)
|
|
84
|
+
|
|
85
|
+
coerced = enum_vals.map do |v|
|
|
86
|
+
case type
|
|
87
|
+
when Constants::SchemaTypes::INTEGER then v.to_i if v.respond_to?(:to_i)
|
|
88
|
+
when Constants::SchemaTypes::NUMBER then v.to_f if v.respond_to?(:to_f)
|
|
89
|
+
else v
|
|
90
|
+
end
|
|
91
|
+
end.compact
|
|
92
|
+
|
|
93
|
+
result = coerced.uniq
|
|
94
|
+
return nil if result.empty?
|
|
95
|
+
|
|
96
|
+
result
|
|
97
|
+
end
|
|
98
|
+
|
|
66
99
|
def apply_collection_format(result, param, type)
|
|
67
100
|
return unless type == Constants::SchemaTypes::ARRAY
|
|
68
101
|
return unless param.collection_format
|
|
@@ -167,18 +167,18 @@ module GrapeOAS
|
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
def apply_numeric_constraints(hash)
|
|
170
|
-
hash["minimum"] = @schema.minimum
|
|
171
|
-
hash["maximum"] = @schema.maximum
|
|
170
|
+
hash["minimum"] = @schema.minimum unless @schema.minimum.nil?
|
|
171
|
+
hash["maximum"] = @schema.maximum unless @schema.maximum.nil?
|
|
172
172
|
|
|
173
173
|
if @nullable_keyword
|
|
174
174
|
hash["exclusiveMinimum"] = @schema.exclusive_minimum if @schema.exclusive_minimum
|
|
175
175
|
hash["exclusiveMaximum"] = @schema.exclusive_maximum if @schema.exclusive_maximum
|
|
176
176
|
else
|
|
177
|
-
if @schema.exclusive_minimum &&
|
|
177
|
+
if @schema.exclusive_minimum && !@schema.minimum.nil?
|
|
178
178
|
hash["exclusiveMinimum"] = @schema.minimum
|
|
179
179
|
hash.delete("minimum")
|
|
180
180
|
end
|
|
181
|
-
if @schema.exclusive_maximum &&
|
|
181
|
+
if @schema.exclusive_maximum && !@schema.maximum.nil?
|
|
182
182
|
hash["exclusiveMaximum"] = @schema.maximum
|
|
183
183
|
hash.delete("maximum")
|
|
184
184
|
end
|
|
@@ -186,14 +186,14 @@ module GrapeOAS
|
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
def apply_string_constraints(hash)
|
|
189
|
-
hash["minLength"] = @schema.min_length
|
|
190
|
-
hash["maxLength"] = @schema.max_length
|
|
189
|
+
hash["minLength"] = @schema.min_length unless @schema.min_length.nil?
|
|
190
|
+
hash["maxLength"] = @schema.max_length unless @schema.max_length.nil?
|
|
191
191
|
hash["pattern"] = @schema.pattern if @schema.pattern
|
|
192
192
|
end
|
|
193
193
|
|
|
194
194
|
def apply_array_constraints(hash)
|
|
195
|
-
hash["minItems"] = @schema.min_items
|
|
196
|
-
hash["maxItems"] = @schema.max_items
|
|
195
|
+
hash["minItems"] = @schema.min_items unless @schema.min_items.nil?
|
|
196
|
+
hash["maxItems"] = @schema.max_items unless @schema.max_items.nil?
|
|
197
197
|
end
|
|
198
198
|
|
|
199
199
|
# Ensure enum values match the declared type; drop enum if incompatible to avoid invalid specs
|
data/lib/grape_oas/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: grape-oas
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Subbota
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2025-12-15 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: grape
|
|
@@ -130,8 +131,12 @@ licenses:
|
|
|
130
131
|
metadata:
|
|
131
132
|
homepage_uri: https://github.com/numbata/grape-oas
|
|
132
133
|
source_code_uri: https://github.com/numbata/grape-oas
|
|
134
|
+
github_repo: https://github.com/numbata/grape-oas
|
|
133
135
|
changelog_uri: https://github.com/numbata/grape-oas/blob/main/CHANGELOG.md
|
|
136
|
+
documentation_uri: https://github.com/numbata/grape-oas#readme
|
|
137
|
+
bug_tracker_uri: https://github.com/numbata/grape-oas/issues
|
|
134
138
|
rubygems_mfa_required: 'true'
|
|
139
|
+
post_install_message:
|
|
135
140
|
rdoc_options: []
|
|
136
141
|
require_paths:
|
|
137
142
|
- lib
|
|
@@ -146,7 +151,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
146
151
|
- !ruby/object:Gem::Version
|
|
147
152
|
version: '0'
|
|
148
153
|
requirements: []
|
|
149
|
-
rubygems_version: 3.
|
|
154
|
+
rubygems_version: 3.5.22
|
|
155
|
+
signing_key:
|
|
150
156
|
specification_version: 4
|
|
151
157
|
summary: OpenAPI (Swagger) v2 and v3 documentation for Grape APIs
|
|
152
158
|
test_files: []
|