grape-swagger 0.10.0 → 0.10.2
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/.rubocop_todo.yml +10 -10
- data/.travis.yml +4 -1
- data/CHANGELOG.md +92 -63
- data/CONTRIBUTING.md +8 -8
- data/Gemfile +1 -1
- data/README.md +90 -16
- data/RELEASING.md +1 -1
- data/UPGRADING.md +26 -5
- data/grape-swagger.gemspec +1 -1
- data/lib/grape-swagger/doc_methods.rb +495 -0
- data/lib/grape-swagger/version.rb +1 -1
- data/lib/grape-swagger.rb +50 -476
- data/spec/api_description_spec.rb +2 -2
- data/spec/api_global_models_spec.rb +21 -21
- data/spec/api_models_spec.rb +62 -42
- data/spec/api_paths_spec.rb +64 -0
- data/spec/api_with_nil_types.rb +50 -0
- data/spec/api_with_prefix_and_namespace_spec.rb +32 -0
- data/spec/api_with_standalone_namespace_spec.rb +16 -16
- data/spec/array_entity_spec.rb +34 -0
- data/spec/array_params_spec.rb +35 -6
- data/spec/default_api_spec.rb +42 -0
- data/spec/float_api_spec.rb +1 -1
- data/spec/grape-swagger_helper_spec.rb +14 -1
- data/spec/mounted_target_class_spec.rb +63 -0
- data/spec/non_default_api_spec.rb +47 -16
- data/spec/param_type_spec.rb +52 -0
- data/spec/param_values_spec.rb +130 -0
- data/spec/reference_entity.rb +80 -0
- data/spec/simple_mounted_api_spec.rb +2 -2
- data/spec/support/grape_version.rb +11 -0
- metadata +54 -40
- data/.ruby-version +0 -1
- data/spec/range_values_spec.rb +0 -49
data/UPGRADING.md
CHANGED
@@ -1,13 +1,30 @@
|
|
1
1
|
Upgrading Grape-swagger
|
2
2
|
=======================
|
3
3
|
|
4
|
+
### Upgrading to >= 0.10.2
|
5
|
+
|
6
|
+
With grape >= 0.12.0, support for `notes` is replaced by passing a block `detail` option specified. For future compatibility, update your code:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
desc 'Get all kittens!', notes: 'this will expose all the kittens'
|
10
|
+
```
|
11
|
+
|
12
|
+
to
|
13
|
+
|
14
|
+
``` ruby
|
15
|
+
desc 'Get all kittens!' do
|
16
|
+
detail 'this will expose all the kittens'
|
17
|
+
end
|
18
|
+
```
|
19
|
+
Be aware of https://github.com/ruby-grape/grape/issues/920, currently grape accepts either an option hash OR a block for `desc`.
|
20
|
+
|
4
21
|
### Upgrading to >= 0.9.0
|
5
22
|
|
6
|
-
####
|
23
|
+
#### Grape-Swagger-Rails
|
7
24
|
|
8
|
-
If you're using [grape-swagger-rails](https://github.com/
|
25
|
+
If you're using [grape-swagger-rails](https://github.com/ruby-grape/grape-swagger-rails), remove the `.json` extension from `GrapeSwaggerRails.options.url`.
|
9
26
|
|
10
|
-
For example, change
|
27
|
+
For example, change
|
11
28
|
|
12
29
|
```ruby
|
13
30
|
GrapeSwaggerRails.options.url = '/api/v1/swagger_doc.json'
|
@@ -19,7 +36,11 @@ to
|
|
19
36
|
GrapeSwaggerRails.options.url = '/api/v1/swagger_doc'
|
20
37
|
```
|
21
38
|
|
22
|
-
See [#187](https://github.com/
|
39
|
+
See [#187](https://github.com/ruby-grape/grape-swagger/issues/187) for more information.
|
40
|
+
|
41
|
+
#### Grape 0.10.0
|
42
|
+
|
43
|
+
If your API uses Grape 0.10.0 or newer with a single `format :json` directive, add `hide_format: true` to `add_swagger_documentation`. Otherwise nested routes will render with `.json` links to your API documentation, which will fail with a 404 Not Found.
|
23
44
|
|
24
45
|
### Upgrading to >= 0.8.0
|
25
46
|
|
@@ -64,4 +85,4 @@ add_swagger_documentation (
|
|
64
85
|
)
|
65
86
|
```
|
66
87
|
|
67
|
-
See [#142](https://github.com/
|
88
|
+
See [#142](https://github.com/ruby-grape/grape-swagger/pull/142) and documentation section [Markdown in Notes](https://github.com/ruby-grape/grape-swagger#markdown-in-notes) for more information.
|
data/grape-swagger.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.authors = ['Tim Vandecasteele']
|
9
9
|
s.email = ['tim.vandecasteele@gmail.com']
|
10
|
-
s.homepage = 'https://github.com/
|
10
|
+
s.homepage = 'https://github.com/ruby-grape/grape-swagger'
|
11
11
|
s.summary = 'A simple way to add auto generated documentation to your Grape API that can be displayed with Swagger.'
|
12
12
|
s.license = 'MIT'
|
13
13
|
|
@@ -0,0 +1,495 @@
|
|
1
|
+
module GrapeSwagger
|
2
|
+
module DocMethods
|
3
|
+
PRIMITIVE_MAPPINGS = {
|
4
|
+
'integer' => %w(integer int32),
|
5
|
+
'long' => %w(integer int64),
|
6
|
+
'float' => %w(number float),
|
7
|
+
'double' => %w(number double),
|
8
|
+
'byte' => %w(string byte),
|
9
|
+
'date' => %w(string date),
|
10
|
+
'dateTime' => %w(string date-time)
|
11
|
+
}
|
12
|
+
|
13
|
+
def name
|
14
|
+
@@class_name
|
15
|
+
end
|
16
|
+
|
17
|
+
def as_markdown(description)
|
18
|
+
description && @@markdown ? @@markdown.as_markdown(strip_heredoc(description)) : description
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_params(params, path, method)
|
22
|
+
params ||= []
|
23
|
+
|
24
|
+
parsed_array_params = parse_array_params(params)
|
25
|
+
|
26
|
+
non_nested_parent_params = get_non_nested_params(parsed_array_params)
|
27
|
+
|
28
|
+
non_nested_parent_params.map do |param, value|
|
29
|
+
items = {}
|
30
|
+
|
31
|
+
raw_data_type = value[:type] if value.is_a?(Hash)
|
32
|
+
raw_data_type ||= 'string'
|
33
|
+
data_type = case raw_data_type.to_s
|
34
|
+
when 'Hash'
|
35
|
+
'object'
|
36
|
+
when 'Rack::Multipart::UploadedFile'
|
37
|
+
'File'
|
38
|
+
when 'Virtus::Attribute::Boolean'
|
39
|
+
'boolean'
|
40
|
+
when 'Boolean', 'Date', 'Integer', 'String', 'Float'
|
41
|
+
raw_data_type.to_s.downcase
|
42
|
+
when 'BigDecimal'
|
43
|
+
'long'
|
44
|
+
when 'DateTime'
|
45
|
+
'dateTime'
|
46
|
+
when 'Numeric'
|
47
|
+
'double'
|
48
|
+
when 'Symbol'
|
49
|
+
'string'
|
50
|
+
when /^\[(?<type>.*)\]$/
|
51
|
+
items[:type] = Regexp.last_match[:type].downcase
|
52
|
+
if PRIMITIVE_MAPPINGS.key?(items[:type])
|
53
|
+
items[:type], items[:format] = PRIMITIVE_MAPPINGS[items[:type]]
|
54
|
+
end
|
55
|
+
'array'
|
56
|
+
else
|
57
|
+
@@documentation_class.parse_entity_name(raw_data_type)
|
58
|
+
end
|
59
|
+
|
60
|
+
additional_documentation = value.is_a?(Hash) ? value[:documentation] : nil
|
61
|
+
|
62
|
+
if additional_documentation && value.is_a?(Hash)
|
63
|
+
value = additional_documentation.merge(value)
|
64
|
+
end
|
65
|
+
|
66
|
+
description = value.is_a?(Hash) ? value[:desc] || value[:description] : ''
|
67
|
+
required = value.is_a?(Hash) ? !!value[:required] : false
|
68
|
+
default_value = value.is_a?(Hash) ? value[:default] : nil
|
69
|
+
example = value.is_a?(Hash) ? value[:example] : nil
|
70
|
+
is_array = value.is_a?(Hash) ? (value[:is_array] || false) : false
|
71
|
+
values = value.is_a?(Hash) ? value[:values] : nil
|
72
|
+
enum_or_range_values = parse_enum_or_range_values(values)
|
73
|
+
|
74
|
+
if value.is_a?(Hash) && value.key?(:documentation) && value[:documentation].key?(:param_type)
|
75
|
+
param_type = value[:documentation][:param_type]
|
76
|
+
if is_array
|
77
|
+
items = { '$ref' => data_type }
|
78
|
+
data_type = 'array'
|
79
|
+
end
|
80
|
+
else
|
81
|
+
param_type = case
|
82
|
+
when path.include?(":#{param}")
|
83
|
+
'path'
|
84
|
+
when %w(POST PUT PATCH).include?(method)
|
85
|
+
if is_primitive?(data_type)
|
86
|
+
'form'
|
87
|
+
else
|
88
|
+
'body'
|
89
|
+
end
|
90
|
+
else
|
91
|
+
'query'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
name = (value.is_a?(Hash) && value[:full_name]) || param
|
95
|
+
|
96
|
+
parsed_params = {
|
97
|
+
paramType: param_type,
|
98
|
+
name: name,
|
99
|
+
description: as_markdown(description),
|
100
|
+
type: data_type,
|
101
|
+
required: required,
|
102
|
+
allowMultiple: is_array
|
103
|
+
}
|
104
|
+
|
105
|
+
if PRIMITIVE_MAPPINGS.key?(data_type)
|
106
|
+
parsed_params[:type], parsed_params[:format] = PRIMITIVE_MAPPINGS[data_type]
|
107
|
+
end
|
108
|
+
|
109
|
+
parsed_params[:items] = items if items.present?
|
110
|
+
|
111
|
+
parsed_params[:defaultValue] = example if example
|
112
|
+
if default_value && example.blank?
|
113
|
+
parsed_params[:defaultValue] = default_value
|
114
|
+
end
|
115
|
+
|
116
|
+
parsed_params.merge!(enum_or_range_values) if enum_or_range_values
|
117
|
+
parsed_params
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def content_types_for(target_class)
|
122
|
+
content_types = (target_class.content_types || {}).values
|
123
|
+
|
124
|
+
if content_types.empty?
|
125
|
+
formats = [target_class.format, target_class.default_format].compact.uniq
|
126
|
+
formats = Grape::Formatter::Base.formatters({}).keys if formats.empty?
|
127
|
+
content_types = Grape::ContentTypes::CONTENT_TYPES.select { |content_type, _mime_type| formats.include? content_type }.values
|
128
|
+
end
|
129
|
+
|
130
|
+
content_types.uniq
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_info(info)
|
134
|
+
{
|
135
|
+
contact: info[:contact],
|
136
|
+
description: as_markdown(info[:description]),
|
137
|
+
license: info[:license],
|
138
|
+
licenseUrl: info[:license_url],
|
139
|
+
termsOfServiceUrl: info[:terms_of_service_url],
|
140
|
+
title: info[:title]
|
141
|
+
}.delete_if { |_, value| value.blank? }
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_header_params(params)
|
145
|
+
params ||= []
|
146
|
+
|
147
|
+
params.map do |param, value|
|
148
|
+
data_type = 'string'
|
149
|
+
description = value.is_a?(Hash) ? value[:description] : ''
|
150
|
+
required = value.is_a?(Hash) ? !!value[:required] : false
|
151
|
+
default_value = value.is_a?(Hash) ? value[:default] : nil
|
152
|
+
param_type = 'header'
|
153
|
+
|
154
|
+
parsed_params = {
|
155
|
+
paramType: param_type,
|
156
|
+
name: param,
|
157
|
+
description: as_markdown(description),
|
158
|
+
type: data_type,
|
159
|
+
required: required
|
160
|
+
}
|
161
|
+
|
162
|
+
parsed_params.merge!(defaultValue: default_value) if default_value
|
163
|
+
|
164
|
+
parsed_params
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def parse_path(path, version)
|
169
|
+
# adapt format to swagger format
|
170
|
+
parsed_path = path.sub(/\(\..*\)$/, @@hide_format ? '' : '.{format}')
|
171
|
+
|
172
|
+
# This is attempting to emulate the behavior of
|
173
|
+
# Rack::Mount::Strexp. We cannot use Strexp directly because
|
174
|
+
# all it does is generate regular expressions for parsing URLs.
|
175
|
+
# TODO: Implement a Racc tokenizer to properly generate the
|
176
|
+
# parsed path.
|
177
|
+
parsed_path = parsed_path.gsub(/:([a-zA-Z_]\w*)/, '{\1}')
|
178
|
+
|
179
|
+
# add the version
|
180
|
+
version ? parsed_path.gsub('{version}', version) : parsed_path
|
181
|
+
end
|
182
|
+
|
183
|
+
def parse_entity_name(model)
|
184
|
+
if model.respond_to?(:entity_name)
|
185
|
+
model.entity_name
|
186
|
+
else
|
187
|
+
name = model.to_s
|
188
|
+
entity_parts = name.split('::')
|
189
|
+
entity_parts.reject! { |p| p == 'Entity' || p == 'Entities' }
|
190
|
+
entity_parts.join('::')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def parse_entity_models(models)
|
195
|
+
result = {}
|
196
|
+
models.each do |model|
|
197
|
+
name = (model.instance_variable_get(:@root) || parse_entity_name(model))
|
198
|
+
properties = {}
|
199
|
+
required = []
|
200
|
+
|
201
|
+
model.documentation.each do |property_name, property_info|
|
202
|
+
p = property_info.dup
|
203
|
+
|
204
|
+
required << property_name.to_s if p.delete(:required)
|
205
|
+
|
206
|
+
type = if p[:type]
|
207
|
+
p.delete(:type)
|
208
|
+
else
|
209
|
+
exposure = model.exposures[property_name]
|
210
|
+
parse_entity_name(exposure[:using]) if exposure
|
211
|
+
end
|
212
|
+
|
213
|
+
if p.delete(:is_array)
|
214
|
+
p[:items] = generate_typeref(type)
|
215
|
+
p[:type] = 'array'
|
216
|
+
else
|
217
|
+
p.merge! generate_typeref(type)
|
218
|
+
end
|
219
|
+
|
220
|
+
# rename Grape Entity's "desc" to "description"
|
221
|
+
property_description = p.delete(:desc)
|
222
|
+
p[:description] = property_description if property_description
|
223
|
+
|
224
|
+
# rename Grape's 'values' to 'enum'
|
225
|
+
select_values = p.delete(:values)
|
226
|
+
if select_values
|
227
|
+
select_values = select_values.call if select_values.is_a?(Proc)
|
228
|
+
p[:enum] = select_values
|
229
|
+
end
|
230
|
+
|
231
|
+
if PRIMITIVE_MAPPINGS.key?(p['type'])
|
232
|
+
p['type'], p['format'] = PRIMITIVE_MAPPINGS[p['type']]
|
233
|
+
end
|
234
|
+
|
235
|
+
properties[property_name] = p
|
236
|
+
end
|
237
|
+
|
238
|
+
result[name] = {
|
239
|
+
id: name,
|
240
|
+
properties: properties
|
241
|
+
}
|
242
|
+
result[name].merge!(required: required) unless required.empty?
|
243
|
+
end
|
244
|
+
|
245
|
+
result
|
246
|
+
end
|
247
|
+
|
248
|
+
def models_with_included_presenters(models)
|
249
|
+
all_models = models
|
250
|
+
|
251
|
+
models.each do |model|
|
252
|
+
# get model references from exposures with a documentation
|
253
|
+
nested_models = model.exposures.map do |_, config|
|
254
|
+
if config.key?(:documentation)
|
255
|
+
model = config[:using]
|
256
|
+
model.respond_to?(:constantize) ? model.constantize : model
|
257
|
+
end
|
258
|
+
end.compact
|
259
|
+
|
260
|
+
# get all nested models recursively
|
261
|
+
additional_models = nested_models.map do |nested_model|
|
262
|
+
models_with_included_presenters([nested_model])
|
263
|
+
end.flatten
|
264
|
+
|
265
|
+
all_models += additional_models
|
266
|
+
end
|
267
|
+
|
268
|
+
all_models
|
269
|
+
end
|
270
|
+
|
271
|
+
def is_primitive?(type)
|
272
|
+
%w(object integer long float double string byte boolean date dateTime).include? type
|
273
|
+
end
|
274
|
+
|
275
|
+
def generate_typeref(type)
|
276
|
+
type_s = type.to_s.sub(/^[A-Z]/) { |f| f.downcase }
|
277
|
+
if is_primitive? type_s
|
278
|
+
{ 'type' => type_s }
|
279
|
+
else
|
280
|
+
{ '$ref' => parse_entity_name(type) }
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def parse_http_codes(codes, models)
|
285
|
+
codes ||= {}
|
286
|
+
codes.map do |k, v, m|
|
287
|
+
models << m if m
|
288
|
+
http_code_hash = {
|
289
|
+
code: k,
|
290
|
+
message: v
|
291
|
+
}
|
292
|
+
http_code_hash[:responseModel] = parse_entity_name(m) if m
|
293
|
+
http_code_hash
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def strip_heredoc(string)
|
298
|
+
indent = string.scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
|
299
|
+
string.gsub(/^[ \t]{#{indent}}/, '')
|
300
|
+
end
|
301
|
+
|
302
|
+
def parse_base_path(base_path, request)
|
303
|
+
if base_path.is_a?(Proc)
|
304
|
+
base_path.call(request)
|
305
|
+
elsif base_path.is_a?(String)
|
306
|
+
URI(base_path).relative? ? URI.join(request.base_url, base_path).to_s : base_path
|
307
|
+
else
|
308
|
+
request.base_url
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def hide_documentation_path
|
313
|
+
@@hide_documentation_path
|
314
|
+
end
|
315
|
+
|
316
|
+
def mount_path
|
317
|
+
@@mount_path
|
318
|
+
end
|
319
|
+
|
320
|
+
def setup(options)
|
321
|
+
defaults = {
|
322
|
+
target_class: nil,
|
323
|
+
mount_path: '/swagger_doc',
|
324
|
+
base_path: nil,
|
325
|
+
api_version: '0.1',
|
326
|
+
markdown: nil,
|
327
|
+
hide_documentation_path: false,
|
328
|
+
hide_format: false,
|
329
|
+
format: nil,
|
330
|
+
models: [],
|
331
|
+
info: {},
|
332
|
+
authorizations: nil,
|
333
|
+
root_base_path: true,
|
334
|
+
api_documentation: { desc: 'Swagger compatible API description' },
|
335
|
+
specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
|
336
|
+
}
|
337
|
+
|
338
|
+
options = defaults.merge(options)
|
339
|
+
|
340
|
+
target_class = options[:target_class]
|
341
|
+
@@mount_path = options[:mount_path]
|
342
|
+
@@class_name = options[:class_name] || options[:mount_path].gsub('/', '')
|
343
|
+
@@markdown = options[:markdown] ? GrapeSwagger::Markdown.new(options[:markdown]) : nil
|
344
|
+
@@hide_format = options[:hide_format]
|
345
|
+
api_version = options[:api_version]
|
346
|
+
authorizations = options[:authorizations]
|
347
|
+
root_base_path = options[:root_base_path]
|
348
|
+
extra_info = options[:info]
|
349
|
+
api_doc = options[:api_documentation].dup
|
350
|
+
specific_api_doc = options[:specific_api_documentation].dup
|
351
|
+
@@models = options[:models] || []
|
352
|
+
|
353
|
+
@@hide_documentation_path = options[:hide_documentation_path]
|
354
|
+
|
355
|
+
if options[:format]
|
356
|
+
[:format, :default_format, :default_error_formatter].each do |method|
|
357
|
+
send(method, options[:format])
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
@@documentation_class = self
|
362
|
+
|
363
|
+
desc api_doc.delete(:desc), api_doc
|
364
|
+
get @@mount_path do
|
365
|
+
header['Access-Control-Allow-Origin'] = '*'
|
366
|
+
header['Access-Control-Request-Method'] = '*'
|
367
|
+
|
368
|
+
namespaces = target_class.combined_namespaces
|
369
|
+
namespace_routes = target_class.combined_namespace_routes
|
370
|
+
|
371
|
+
if @@hide_documentation_path
|
372
|
+
namespace_routes.reject! { |route, _value| "/#{route}/".index(@@documentation_class.parse_path(@@mount_path, nil) << '/') == 0 }
|
373
|
+
end
|
374
|
+
|
375
|
+
namespace_routes_array = namespace_routes.keys.map do |local_route|
|
376
|
+
next if namespace_routes[local_route].map(&:route_hidden).all? { |value| value.respond_to?(:call) ? value.call : value }
|
377
|
+
|
378
|
+
url_format = '.{format}' unless @@hide_format
|
379
|
+
|
380
|
+
original_namespace_name = target_class.combined_namespace_identifiers.key?(local_route) ? target_class.combined_namespace_identifiers[local_route] : local_route
|
381
|
+
description = namespaces[original_namespace_name] && namespaces[original_namespace_name].options[:desc]
|
382
|
+
description ||= "Operations about #{original_namespace_name.pluralize}"
|
383
|
+
|
384
|
+
{
|
385
|
+
path: "/#{local_route}#{url_format}",
|
386
|
+
description: description
|
387
|
+
}
|
388
|
+
end.compact
|
389
|
+
|
390
|
+
output = {
|
391
|
+
apiVersion: api_version,
|
392
|
+
swaggerVersion: '1.2',
|
393
|
+
produces: @@documentation_class.content_types_for(target_class),
|
394
|
+
apis: namespace_routes_array,
|
395
|
+
info: @@documentation_class.parse_info(extra_info)
|
396
|
+
}
|
397
|
+
|
398
|
+
output[:authorizations] = authorizations unless authorizations.nil? || authorizations.empty?
|
399
|
+
|
400
|
+
output
|
401
|
+
end
|
402
|
+
|
403
|
+
desc specific_api_doc.delete(:desc), { params:
|
404
|
+
specific_api_doc.delete(:params) || {} }.merge(specific_api_doc)
|
405
|
+
params do
|
406
|
+
requires :name, type: String, desc: 'Resource name of mounted API'
|
407
|
+
end
|
408
|
+
get "#{@@mount_path}/:name" do
|
409
|
+
header['Access-Control-Allow-Origin'] = '*'
|
410
|
+
header['Access-Control-Request-Method'] = '*'
|
411
|
+
|
412
|
+
models = []
|
413
|
+
routes = target_class.combined_namespace_routes[params[:name]]
|
414
|
+
error!('Not Found', 404) unless routes
|
415
|
+
|
416
|
+
visible_ops = routes.reject do |route|
|
417
|
+
route.route_hidden.respond_to?(:call) ? route.route_hidden.call : route.route_hidden
|
418
|
+
end
|
419
|
+
|
420
|
+
ops = visible_ops.group_by do |route|
|
421
|
+
@@documentation_class.parse_path(route.route_path, api_version)
|
422
|
+
end
|
423
|
+
|
424
|
+
error!('Not Found', 404) unless ops.any?
|
425
|
+
|
426
|
+
apis = []
|
427
|
+
|
428
|
+
ops.each do |path, op_routes|
|
429
|
+
operations = op_routes.map do |route|
|
430
|
+
notes = @@documentation_class.as_markdown(route.route_detail || route.route_notes)
|
431
|
+
|
432
|
+
http_codes = @@documentation_class.parse_http_codes(route.route_http_codes, models)
|
433
|
+
|
434
|
+
models |= @@models if @@models.present?
|
435
|
+
|
436
|
+
models |= Array(route.route_entity) if route.route_entity.present?
|
437
|
+
|
438
|
+
models = @@documentation_class.models_with_included_presenters(models.flatten.compact)
|
439
|
+
|
440
|
+
operation = {
|
441
|
+
notes: notes.to_s,
|
442
|
+
summary: route.route_description || '',
|
443
|
+
nickname: route.route_nickname || (route.route_method + route.route_path.gsub(/[\/:\(\)\.]/, '-')),
|
444
|
+
method: route.route_method,
|
445
|
+
parameters: @@documentation_class.parse_header_params(route.route_headers) + @@documentation_class.parse_params(route.route_params, route.route_path, route.route_method),
|
446
|
+
type: route.route_is_array ? 'array' : 'void'
|
447
|
+
}
|
448
|
+
operation[:authorizations] = route.route_authorizations unless route.route_authorizations.nil? || route.route_authorizations.empty?
|
449
|
+
if operation[:parameters].any? { |param| param[:type] == 'File' }
|
450
|
+
operation.merge!(consumes: ['multipart/form-data'])
|
451
|
+
end
|
452
|
+
operation.merge!(responseMessages: http_codes) unless http_codes.empty?
|
453
|
+
|
454
|
+
if route.route_entity
|
455
|
+
type = @@documentation_class.parse_entity_name(Array(route.route_entity).first)
|
456
|
+
if route.route_is_array
|
457
|
+
operation.merge!(items: { '$ref' => type })
|
458
|
+
else
|
459
|
+
operation.merge!(type: type)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
operation[:nickname] = route.route_nickname if route.route_nickname
|
464
|
+
operation
|
465
|
+
end.compact
|
466
|
+
apis << {
|
467
|
+
path: path,
|
468
|
+
operations: operations
|
469
|
+
}
|
470
|
+
end
|
471
|
+
|
472
|
+
# use custom resource naming if available
|
473
|
+
if target_class.combined_namespace_identifiers.key? params[:name]
|
474
|
+
resource_path = target_class.combined_namespace_identifiers[params[:name]]
|
475
|
+
else
|
476
|
+
resource_path = params[:name]
|
477
|
+
end
|
478
|
+
api_description = {
|
479
|
+
apiVersion: api_version,
|
480
|
+
swaggerVersion: '1.2',
|
481
|
+
resourcePath: "/#{resource_path}",
|
482
|
+
produces: @@documentation_class.content_types_for(target_class),
|
483
|
+
apis: apis
|
484
|
+
}
|
485
|
+
|
486
|
+
base_path = @@documentation_class.parse_base_path(options[:base_path], request)
|
487
|
+
api_description[:basePath] = base_path if base_path && base_path.size > 0 && root_base_path != false
|
488
|
+
api_description[:models] = @@documentation_class.parse_entity_models(models) unless models.empty?
|
489
|
+
api_description[:authorizations] = authorizations if authorizations
|
490
|
+
|
491
|
+
api_description
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|