grape-swagger 2.0.3 → 2.1.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 +10 -3
- data/grape-swagger.gemspec +2 -2
- data/lib/grape-swagger/doc_methods/data_type.rb +8 -0
- data/lib/grape-swagger/doc_methods/parse_params.rb +22 -9
- data/lib/grape-swagger/doc_methods/produces_consumes.rb +1 -1
- data/lib/grape-swagger/endpoint.rb +5 -5
- data/lib/grape-swagger/version.rb +1 -1
- data/lib/grape-swagger.rb +71 -43
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d07c4678df57d48124ef1afed6e8d60c0cedc0d5f1ea198b4c1ce2e6b3e30f2d
|
4
|
+
data.tar.gz: 35e02858f204c33f403a6b583c1c2ced2c3d66caaa94613d30de4c227ed96266
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35c4824daa66db7842b786b580981a18223c1cb7c199a4a8dd754b2cc310fc876d8b7bb8dad659f5a92f554d4c8e306e960e739a9764b6a6c6c8bb004a87d37c
|
7
|
+
data.tar.gz: 2b4c8c3e19b0d9ee56a66655598b8293470f18a3815f0cc8c6e5bf003647f8ba46774017bcc5a5b2db7e947aafd75f90e15ad2ef46d6ec683bfc8d868a9205b9
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
-
###
|
1
|
+
### 2.1.1 (Sep 21, 2024)
|
2
|
+
|
3
|
+
#### Fixes
|
4
|
+
|
5
|
+
* [#940](https://github.com/ruby-grape/grape-swagger/pull/940): Grape 2.2.0 compatibility - [@padde](https://github.com/padde)
|
6
|
+
|
7
|
+
### 2.1.0 (May 14, 2024)
|
2
8
|
|
3
9
|
#### Features
|
4
10
|
|
5
|
-
*
|
11
|
+
* [#927](https://github.com/ruby-grape/grape-swagger/pull/927): Set default parameter location based on consumes - [@spaceraccoon](https://github.com/spaceraccoon)
|
12
|
+
* [#929](https://github.com/ruby-grape/grape-swagger/pull/929): Set query parameter for array of primitive types - [@spaceraccoon](https://github.com/spaceraccoon)
|
6
13
|
|
7
14
|
#### Fixes
|
8
15
|
|
9
|
-
*
|
16
|
+
* [#926](https://github.com/ruby-grape/grape-swagger/pull/926): Refactor route and namespace combination logic - [@numbata](https://github.com/numbata)
|
10
17
|
|
11
18
|
|
12
19
|
### 2.0.3 (April 26, 2024)
|
data/grape-swagger.gemspec
CHANGED
@@ -15,8 +15,8 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.metadata['rubygems_mfa_required'] = 'true'
|
16
16
|
|
17
17
|
s.required_ruby_version = '>= 3.0'
|
18
|
-
s.
|
19
|
-
s.
|
18
|
+
s.add_dependency 'grape', '>= 1.7', '< 3.0'
|
19
|
+
s.add_dependency 'rack-test', '~> 2'
|
20
20
|
|
21
21
|
s.files = Dir['lib/**/*', '*.md', 'LICENSE.txt', 'grape-swagger.gemspec']
|
22
22
|
s.require_paths = ['lib']
|
@@ -75,6 +75,14 @@ module GrapeSwagger
|
|
75
75
|
PRIMITIVE_MAPPINGS.keys.map(&:downcase)
|
76
76
|
end
|
77
77
|
|
78
|
+
def query_array_primitive?(type)
|
79
|
+
query_array_primitives.include?(type.to_s.downcase)
|
80
|
+
end
|
81
|
+
|
82
|
+
def query_array_primitives
|
83
|
+
primitives << 'string'
|
84
|
+
end
|
85
|
+
|
78
86
|
def mapping(value)
|
79
87
|
PRIMITIVE_MAPPINGS[value] || 'string'
|
80
88
|
end
|
@@ -4,7 +4,7 @@ module GrapeSwagger
|
|
4
4
|
module DocMethods
|
5
5
|
class ParseParams
|
6
6
|
class << self
|
7
|
-
def call(param, settings, path, route, definitions)
|
7
|
+
def call(param, settings, path, route, definitions, consumes) # rubocop:disable Metrics/ParameterLists
|
8
8
|
method = route.request_method
|
9
9
|
additional_documentation = settings.fetch(:documentation, {})
|
10
10
|
settings.merge!(additional_documentation)
|
@@ -14,7 +14,7 @@ module GrapeSwagger
|
|
14
14
|
|
15
15
|
# required properties
|
16
16
|
@parsed_param = {
|
17
|
-
in: param_type(value_type),
|
17
|
+
in: param_type(value_type, consumes),
|
18
18
|
name: settings[:full_name] || param
|
19
19
|
}
|
20
20
|
|
@@ -25,6 +25,7 @@ module GrapeSwagger
|
|
25
25
|
document_default_value(settings) unless value_type[:is_array]
|
26
26
|
document_range_values(settings) unless value_type[:is_array]
|
27
27
|
document_required(settings)
|
28
|
+
document_length_limits(value_type)
|
28
29
|
document_additional_properties(definitions, settings) unless value_type[:is_array]
|
29
30
|
document_add_extensions(settings)
|
30
31
|
document_example(settings)
|
@@ -70,21 +71,17 @@ module GrapeSwagger
|
|
70
71
|
|
71
72
|
def document_array_param(value_type, definitions)
|
72
73
|
if value_type[:documentation].present?
|
73
|
-
param_type = value_type[:documentation][:param_type] || value_type[:documentation][:in]
|
74
74
|
doc_type = value_type[:documentation][:type]
|
75
75
|
type = DataType.mapping(doc_type) if doc_type && !DataType.request_primitive?(doc_type)
|
76
76
|
collection_format = value_type[:documentation][:collectionFormat]
|
77
77
|
end
|
78
78
|
|
79
|
-
param_type ||= value_type[:param_type]
|
80
|
-
|
81
79
|
array_items = parse_array_item(
|
82
80
|
definitions,
|
83
81
|
type,
|
84
82
|
value_type
|
85
83
|
)
|
86
84
|
|
87
|
-
@parsed_param[:in] = param_type || 'formData'
|
88
85
|
@parsed_param[:items] = array_items
|
89
86
|
@parsed_param[:type] = 'array'
|
90
87
|
@parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
|
@@ -148,19 +145,35 @@ module GrapeSwagger
|
|
148
145
|
@parsed_param[:example] = example if example
|
149
146
|
end
|
150
147
|
|
151
|
-
def param_type(value_type)
|
148
|
+
def param_type(value_type, consumes)
|
152
149
|
param_type = value_type[:param_type] || value_type[:in]
|
153
|
-
if value_type[:path].include?("{#{value_type[:param_name]}}")
|
150
|
+
if !value_type[:is_array] && value_type[:path].include?("{#{value_type[:param_name]}}")
|
154
151
|
'path'
|
155
152
|
elsif param_type
|
156
153
|
param_type
|
157
154
|
elsif %w[POST PUT PATCH].include?(value_type[:method])
|
158
|
-
|
155
|
+
if consumes.include?('application/x-www-form-urlencoded') || consumes.include?('multipart/form-data')
|
156
|
+
'formData'
|
157
|
+
else
|
158
|
+
'body'
|
159
|
+
end
|
160
|
+
elsif value_type[:is_array] && !DataType.query_array_primitive?(value_type[:data_type])
|
161
|
+
'formData'
|
159
162
|
else
|
160
163
|
'query'
|
161
164
|
end
|
162
165
|
end
|
163
166
|
|
167
|
+
def document_length_limits(value_type)
|
168
|
+
if value_type[:is_array]
|
169
|
+
@parsed_param[:minItems] = value_type[:min_length] if value_type.key?(:min_length)
|
170
|
+
@parsed_param[:maxItems] = value_type[:max_length] if value_type.key?(:max_length)
|
171
|
+
else
|
172
|
+
@parsed_param[:minLength] = value_type[:min_length] if value_type.key?(:min_length)
|
173
|
+
@parsed_param[:maxLength] = value_type[:max_length] if value_type.key?(:max_length)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
164
177
|
def parse_enum_or_range_values(values)
|
165
178
|
case values
|
166
179
|
when Proc
|
@@ -7,7 +7,7 @@ module GrapeSwagger
|
|
7
7
|
def call(*args)
|
8
8
|
return ['application/json'] unless args.flatten.present?
|
9
9
|
|
10
|
-
args.flatten.map { |x|
|
10
|
+
args.flatten.map { |x| GrapeSwagger::CONTENT_TYPE_DEFAULTS[x] || x }.uniq
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -11,8 +11,8 @@ module Grape
|
|
11
11
|
|
12
12
|
if content_types.empty?
|
13
13
|
formats = [target_class.format, target_class.default_format].compact.uniq
|
14
|
-
formats =
|
15
|
-
content_types =
|
14
|
+
formats = GrapeSwagger::FORMATTER_DEFAULTS.keys if formats.empty?
|
15
|
+
content_types = GrapeSwagger::CONTENT_TYPE_DEFAULTS.select do |content_type, _mime_type|
|
16
16
|
formats.include? content_type
|
17
17
|
end.values
|
18
18
|
end
|
@@ -119,7 +119,7 @@ module Grape
|
|
119
119
|
method[:description] = description_object(route)
|
120
120
|
method[:produces] = produces_object(route, options[:produces] || options[:format])
|
121
121
|
method[:consumes] = consumes_object(route, options[:consumes] || options[:format])
|
122
|
-
method[:parameters] = params_object(route, options, path)
|
122
|
+
method[:parameters] = params_object(route, options, path, method[:consumes])
|
123
123
|
method[:security] = security_object(route)
|
124
124
|
method[:responses] = response_object(route, options)
|
125
125
|
method[:tags] = route.options.fetch(:tags, tag_object(route, path))
|
@@ -175,7 +175,7 @@ module Grape
|
|
175
175
|
GrapeSwagger::DocMethods::ProducesConsumes.call(route.settings.dig(:description, :consumes) || format)
|
176
176
|
end
|
177
177
|
|
178
|
-
def params_object(route, options, path)
|
178
|
+
def params_object(route, options, path, consumes)
|
179
179
|
parameters = build_request_params(route, options).each_with_object([]) do |(param, value), memo|
|
180
180
|
next if hidden_parameter?(value)
|
181
181
|
|
@@ -187,7 +187,7 @@ module Grape
|
|
187
187
|
elsif value[:type]
|
188
188
|
expose_params(value[:type])
|
189
189
|
end
|
190
|
-
memo << GrapeSwagger::DocMethods::ParseParams.call(param, value, path, route, @definitions)
|
190
|
+
memo << GrapeSwagger::DocMethods::ParseParams.call(param, value, path, route, @definitions, consumes)
|
191
191
|
end
|
192
192
|
|
193
193
|
if GrapeSwagger::DocMethods::MoveParams.can_be_moved?(route.request_method, parameters)
|
data/lib/grape-swagger.rb
CHANGED
@@ -18,13 +18,31 @@ module GrapeSwagger
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
autoload :Rake, 'grape-swagger/rake/oapi_tasks'
|
21
|
+
|
22
|
+
# Copied from https://github.com/ruby-grape/grape/blob/v2.2.0/lib/grape/formatter.rb
|
23
|
+
FORMATTER_DEFAULTS = {
|
24
|
+
json: Grape::Formatter::Json,
|
25
|
+
jsonapi: Grape::Formatter::Json,
|
26
|
+
serializable_hash: Grape::Formatter::SerializableHash,
|
27
|
+
txt: Grape::Formatter::Txt,
|
28
|
+
xml: Grape::Formatter::Xml
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
# Copied from https://github.com/ruby-grape/grape/blob/v2.2.0/lib/grape/content_types.rb
|
32
|
+
CONTENT_TYPE_DEFAULTS = {
|
33
|
+
xml: 'application/xml',
|
34
|
+
serializable_hash: 'application/json',
|
35
|
+
json: 'application/json',
|
36
|
+
binary: 'application/octet-stream',
|
37
|
+
txt: 'text/plain'
|
38
|
+
}.freeze
|
21
39
|
end
|
22
40
|
|
23
41
|
module SwaggerRouting
|
24
42
|
private
|
25
43
|
|
26
44
|
def combine_routes(app, doc_klass)
|
27
|
-
app.routes.
|
45
|
+
app.routes.each_with_object({}) do |route, combined_routes|
|
28
46
|
route_path = route.path
|
29
47
|
route_match = route_path.split(/^.*?#{route.prefix}/).last
|
30
48
|
next unless route_match
|
@@ -37,31 +55,30 @@ module SwaggerRouting
|
|
37
55
|
|
38
56
|
resource = route_match.captures.first
|
39
57
|
resource = '/' if resource.empty?
|
40
|
-
|
58
|
+
combined_routes[resource] ||= []
|
41
59
|
next if doc_klass.hide_documentation_path && route.path.match(/#{doc_klass.mount_path}($|\/|\(\.)/)
|
42
60
|
|
43
|
-
|
61
|
+
combined_routes[resource] << route
|
44
62
|
end
|
45
63
|
end
|
46
64
|
|
47
|
-
def determine_namespaced_routes(name, parent_route)
|
48
|
-
if parent_route.nil?
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
!route_path_start_with?(route, name) || !route_instance_variable_equals?(route, name)
|
53
|
-
end
|
65
|
+
def determine_namespaced_routes(name, parent_route, routes)
|
66
|
+
return routes.values.flatten if parent_route.nil?
|
67
|
+
|
68
|
+
parent_route.select do |route|
|
69
|
+
route_path_start_with?(route, name) || route_namespace_equals?(route, name)
|
54
70
|
end
|
55
71
|
end
|
56
72
|
|
57
|
-
def combine_namespace_routes(namespaces)
|
73
|
+
def combine_namespace_routes(namespaces, routes)
|
74
|
+
combined_namespace_routes = {}
|
58
75
|
# iterate over each single namespace
|
59
76
|
namespaces.each_key do |name, _|
|
60
77
|
# get the parent route for the namespace
|
61
78
|
parent_route_name = extract_parent_route(name)
|
62
|
-
parent_route =
|
79
|
+
parent_route = routes[parent_route_name]
|
63
80
|
# fetch all routes that are within the current namespace
|
64
|
-
namespace_routes = determine_namespaced_routes(name, parent_route)
|
81
|
+
namespace_routes = determine_namespaced_routes(name, parent_route, routes)
|
65
82
|
|
66
83
|
# default case when not explicitly specified or nested == true
|
67
84
|
standalone_namespaces = namespaces.reject do |_, ns|
|
@@ -76,12 +93,13 @@ module SwaggerRouting
|
|
76
93
|
# rubocop:disable Style/Next
|
77
94
|
if parent_standalone_namespaces.empty?
|
78
95
|
# default option, append namespace methods to parent route
|
79
|
-
|
80
|
-
|
81
|
-
@target_class.combined_namespace_routes[parent_route_name].push(*namespace_routes)
|
96
|
+
combined_namespace_routes[parent_route_name] ||= []
|
97
|
+
combined_namespace_routes[parent_route_name].push(*namespace_routes)
|
82
98
|
end
|
83
99
|
# rubocop:enable Style/Next
|
84
100
|
end
|
101
|
+
|
102
|
+
combined_namespace_routes
|
85
103
|
end
|
86
104
|
|
87
105
|
def extract_parent_route(name)
|
@@ -92,25 +110,32 @@ module SwaggerRouting
|
|
92
110
|
matches.nil? ? route_name : matches[0].delete('/')
|
93
111
|
end
|
94
112
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
113
|
+
def route_namespace_equals?(route, name)
|
114
|
+
patterns = Enumerator.new do |yielder|
|
115
|
+
yielder << "/#{name}"
|
116
|
+
yielder << "/:version/#{name}"
|
117
|
+
end
|
98
118
|
|
99
|
-
|
100
|
-
route_instance_variable(route) == "/#{name}" ||
|
101
|
-
route_instance_variable(route) == "/:version/#{name}"
|
119
|
+
patterns.any? { |p| route.namespace == p }
|
102
120
|
end
|
103
121
|
|
104
122
|
def route_path_start_with?(route, name)
|
105
|
-
|
106
|
-
|
123
|
+
patterns = Enumerator.new do |yielder|
|
124
|
+
if route.prefix
|
125
|
+
yielder << "/#{route.prefix}/#{name}"
|
126
|
+
yielder << "/#{route.prefix}/:version/#{name}"
|
127
|
+
else
|
128
|
+
yielder << "/#{name}"
|
129
|
+
yielder << "/:version/#{name}"
|
130
|
+
end
|
131
|
+
end
|
107
132
|
|
108
|
-
route.path.start_with?(
|
133
|
+
patterns.any? { |p| route.path.start_with?(p) }
|
109
134
|
end
|
110
135
|
end
|
111
136
|
|
112
137
|
module SwaggerDocumentationAdder
|
113
|
-
attr_accessor :combined_namespaces, :
|
138
|
+
attr_accessor :combined_namespaces, :combined_routes, :combined_namespace_routes
|
114
139
|
|
115
140
|
include SwaggerRouting
|
116
141
|
|
@@ -127,20 +152,16 @@ module SwaggerDocumentationAdder
|
|
127
152
|
documentation_class.setup(options)
|
128
153
|
mount(documentation_class)
|
129
154
|
|
130
|
-
|
131
|
-
|
155
|
+
combined_routes = combine_routes(@target_class, documentation_class)
|
156
|
+
combined_namespaces = combine_namespaces(@target_class)
|
157
|
+
combined_namespace_routes = combine_namespace_routes(combined_namespaces, combined_routes)
|
158
|
+
exclusive_route_keys = combined_routes.keys - combined_namespaces.keys
|
159
|
+
@target_class.combined_namespace_routes = combined_namespace_routes.merge(
|
160
|
+
combined_routes.slice(*exclusive_route_keys)
|
161
|
+
)
|
162
|
+
@target_class.combined_routes = combined_routes
|
163
|
+
@target_class.combined_namespaces = combined_namespaces
|
132
164
|
|
133
|
-
@target_class.combined_namespaces = {}
|
134
|
-
combine_namespaces(@target_class)
|
135
|
-
|
136
|
-
@target_class.combined_namespace_routes = {}
|
137
|
-
@target_class.combined_namespace_identifiers = {}
|
138
|
-
combine_namespace_routes(@target_class.combined_namespaces)
|
139
|
-
|
140
|
-
exclusive_route_keys = @target_class.combined_routes.keys - @target_class.combined_namespaces.keys
|
141
|
-
exclusive_route_keys.each do |key|
|
142
|
-
@target_class.combined_namespace_routes[key] = @target_class.combined_routes[key]
|
143
|
-
end
|
144
165
|
documentation_class
|
145
166
|
end
|
146
167
|
|
@@ -151,17 +172,24 @@ module SwaggerDocumentationAdder
|
|
151
172
|
end
|
152
173
|
|
153
174
|
def combine_namespaces(app)
|
154
|
-
|
175
|
+
combined_namespaces = {}
|
176
|
+
endpoints = app.endpoints.clone
|
177
|
+
|
178
|
+
while endpoints.any?
|
179
|
+
endpoint = endpoints.shift
|
180
|
+
|
181
|
+
endpoints.push(*endpoint.options[:app].endpoints) if endpoint.options[:app]
|
155
182
|
ns = endpoint.namespace_stackable(:namespace).last
|
183
|
+
next unless ns
|
156
184
|
|
157
185
|
# use the full namespace here (not the latest level only)
|
158
186
|
# and strip leading slash
|
159
187
|
mount_path = (endpoint.namespace_stackable(:mount_path) || []).join('/')
|
160
188
|
full_namespace = (mount_path + endpoint.namespace).sub(/\/{2,}/, '/').sub(/^\//, '')
|
161
|
-
|
162
|
-
|
163
|
-
combine_namespaces(endpoint.options[:app]) if endpoint.options[:app]
|
189
|
+
combined_namespaces[full_namespace] = ns
|
164
190
|
end
|
191
|
+
|
192
|
+
combined_namespaces
|
165
193
|
end
|
166
194
|
|
167
195
|
def create_documentation_class
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape-swagger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LeFnord
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-09-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: grape
|
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
requirements: []
|
106
|
-
rubygems_version: 3.
|
106
|
+
rubygems_version: 3.3.7
|
107
107
|
signing_key:
|
108
108
|
specification_version: 4
|
109
109
|
summary: Add auto generated documentation to your Grape API that can be displayed
|