grape-swagger 0.11.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -1
  3. data/.rubocop.yml +3 -0
  4. data/.rubocop_todo.yml +14 -22
  5. data/.travis.yml +7 -4
  6. data/CHANGELOG.md +53 -26
  7. data/Gemfile +1 -1
  8. data/README.md +414 -327
  9. data/RELEASING.md +3 -4
  10. data/example/api/endpoints.rb +132 -0
  11. data/example/api/entities.rb +18 -0
  12. data/example/config.ru +36 -2
  13. data/example/example_requests.postman_collection +146 -0
  14. data/example/swagger-example.png +0 -0
  15. data/grape-swagger.gemspec +9 -6
  16. data/lib/grape-swagger.rb +69 -99
  17. data/lib/grape-swagger/doc_methods.rb +69 -544
  18. data/lib/grape-swagger/doc_methods/data_type.rb +77 -0
  19. data/lib/grape-swagger/doc_methods/extensions.rb +75 -0
  20. data/lib/grape-swagger/doc_methods/move_params.rb +153 -0
  21. data/lib/grape-swagger/doc_methods/operation_id.rb +27 -0
  22. data/lib/grape-swagger/doc_methods/optional_object.rb +15 -0
  23. data/lib/grape-swagger/doc_methods/parse_params.rb +113 -0
  24. data/lib/grape-swagger/doc_methods/path_string.rb +29 -0
  25. data/lib/grape-swagger/doc_methods/produces_consumes.rb +12 -0
  26. data/lib/grape-swagger/doc_methods/status_codes.rb +17 -0
  27. data/lib/grape-swagger/doc_methods/tag_name_description.rb +26 -0
  28. data/lib/grape-swagger/endpoint.rb +317 -0
  29. data/lib/grape-swagger/version.rb +1 -1
  30. data/spec/lib/data_type_spec.rb +57 -0
  31. data/spec/lib/endpoint_spec.rb +6 -0
  32. data/spec/lib/extensions_spec.rb +127 -0
  33. data/spec/lib/move_params_spec.rb +298 -0
  34. data/spec/lib/operation_id_spec.rb +24 -0
  35. data/spec/lib/optional_object_spec.rb +40 -0
  36. data/spec/lib/path_string_spec.rb +38 -0
  37. data/spec/lib/produces_consumes_spec.rb +98 -0
  38. data/spec/markdown/kramdown_adapter_spec.rb +2 -9
  39. data/spec/markdown/redcarpet_adapter_spec.rb +2 -16
  40. data/spec/spec_helper.rb +7 -13
  41. data/spec/support/api_swagger_v2_result.rb +204 -0
  42. data/spec/support/namespace_tags.rb +73 -0
  43. data/spec/support/the_api_entities.rb +52 -0
  44. data/spec/support/the_paths_definitions.rb +94 -0
  45. data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +32 -0
  46. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +151 -0
  47. data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +109 -0
  48. data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +124 -0
  49. data/spec/swagger_v2/api_swagger_v2_global_configuration_spec.rb +51 -0
  50. data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +44 -0
  51. data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +56 -0
  52. data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +146 -0
  53. data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +197 -0
  54. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +151 -0
  55. data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +217 -0
  56. data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +64 -0
  57. data/spec/swagger_v2/api_swagger_v2_response_spec.rb +184 -0
  58. data/spec/swagger_v2/api_swagger_v2_spec.rb +207 -0
  59. data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +121 -0
  60. data/spec/{boolean_params_spec.rb → swagger_v2/boolean_params_spec.rb} +2 -2
  61. data/spec/{default_api_spec.rb → swagger_v2/default_api_spec.rb} +40 -36
  62. data/spec/swagger_v2/description_not_initialized.rb +39 -0
  63. data/spec/{float_api_spec.rb → swagger_v2/float_api_spec.rb} +2 -2
  64. data/spec/{form_params_spec.rb → swagger_v2/form_params_spec.rb} +9 -9
  65. data/spec/{grape-swagger_spec.rb → swagger_v2/grape-swagger_spec.rb} +0 -0
  66. data/spec/swagger_v2/hide_api_spec.rb +131 -0
  67. data/spec/swagger_v2/mounted_target_class_spec.rb +76 -0
  68. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +84 -0
  69. data/spec/swagger_v2/namespace_tags_spec.rb +76 -0
  70. data/spec/{namespaced_api_spec.rb → swagger_v2/namespaced_api_spec.rb} +6 -26
  71. data/spec/{param_type_spec.rb → swagger_v2/param_type_spec.rb} +10 -8
  72. data/spec/{param_values_spec.rb → swagger_v2/param_values_spec.rb} +52 -22
  73. data/spec/swagger_v2/params_array_spec.rb +63 -0
  74. data/spec/swagger_v2/params_hash_spec.rb +65 -0
  75. data/spec/swagger_v2/params_nested_spec.rb +63 -0
  76. data/spec/{reference_entity.rb → swagger_v2/reference_entity.rb} +18 -23
  77. data/spec/swagger_v2/response_model_spec.rb +212 -0
  78. data/spec/swagger_v2/simple_mounted_api_spec.rb +264 -0
  79. metadata +175 -90
  80. data/example/api.rb +0 -66
  81. data/lib/grape-swagger/markdown.rb +0 -23
  82. data/spec/api_description_spec.rb +0 -43
  83. data/spec/api_global_models_spec.rb +0 -77
  84. data/spec/api_models_spec.rb +0 -364
  85. data/spec/api_paths_spec.rb +0 -128
  86. data/spec/api_root_spec.rb +0 -30
  87. data/spec/api_with_nil_types.rb +0 -50
  88. data/spec/api_with_path_versioning_spec.rb +0 -33
  89. data/spec/api_with_prefix_and_namespace_spec.rb +0 -32
  90. data/spec/api_with_standalone_namespace_spec.rb +0 -215
  91. data/spec/array_entity_spec.rb +0 -34
  92. data/spec/array_params_spec.rb +0 -85
  93. data/spec/grape-swagger_helper_spec.rb +0 -152
  94. data/spec/group_params_spec.rb +0 -31
  95. data/spec/hash_params_spec.rb +0 -30
  96. data/spec/hide_api_spec.rb +0 -124
  97. data/spec/i18n_spec.rb +0 -364
  98. data/spec/markdown/markdown_spec.rb +0 -27
  99. data/spec/mounted_target_class_spec.rb +0 -63
  100. data/spec/mutually_exclusive_spec.rb +0 -36
  101. data/spec/non_default_api_spec.rb +0 -733
  102. data/spec/response_model_spec.rb +0 -121
  103. data/spec/simple_mounted_api_spec.rb +0 -213
  104. data/spec/support/i18n_helper.rb +0 -8
@@ -1,371 +1,21 @@
1
- require 'set'
1
+ require 'grape-swagger/doc_methods/status_codes'
2
+
3
+ require 'grape-swagger/doc_methods/produces_consumes'
4
+ require 'grape-swagger/doc_methods/data_type'
5
+ require 'grape-swagger/doc_methods/extensions'
6
+ require 'grape-swagger/doc_methods/operation_id'
7
+ require 'grape-swagger/doc_methods/optional_object'
8
+ require 'grape-swagger/doc_methods/path_string'
9
+ require 'grape-swagger/doc_methods/tag_name_description'
10
+ require 'grape-swagger/doc_methods/parse_params'
11
+ require 'grape-swagger/doc_methods/move_params'
2
12
 
3
13
  module GrapeSwagger
4
14
  module DocMethods
5
- PRIMITIVE_MAPPINGS = {
6
- 'integer' => %w(integer int32),
7
- 'long' => %w(integer int64),
8
- 'float' => %w(number float),
9
- 'double' => %w(number double),
10
- 'byte' => %w(string byte),
11
- 'date' => %w(string date),
12
- 'dateTime' => %w(string date-time)
13
- }.freeze
14
-
15
15
  def name
16
16
  @@class_name
17
17
  end
18
18
 
19
- def translate(message, scope, default, params = {})
20
- if message.is_a?(String)
21
- text = message
22
- elsif message.is_a?(Symbol)
23
- key = message
24
- elsif message.is_a?(Hash)
25
- message = message.dup
26
- key = message.delete(:key)
27
- text = message.delete(:default)
28
- skip_translate = !message.delete(:translate) if message.key?(:translate)
29
- scope = message.delete(:scope) if message.key?(:scope)
30
- params = params.merge(message) unless message.empty?
31
- end
32
-
33
- return text if skip_translate
34
-
35
- default = Array(default).dup << (text || '')
36
- I18n.t(key, params.merge(scope: scope, default: default))
37
- end
38
-
39
- def expand_scope(scope)
40
- scopes = []
41
- scope = scope.to_s
42
- until scope.blank?
43
- scopes << scope.to_sym
44
- scope = scope.rpartition('.')[0]
45
- end
46
- scopes << :''
47
- end
48
-
49
- def as_markdown(description)
50
- description && @@markdown ? @@markdown.as_markdown(strip_heredoc(description)) : description
51
- end
52
-
53
- def parse_params(params, path, method, options = {})
54
- scope = options[:scope]
55
- i18n_keys = expand_scope(options[:key])
56
- params ||= []
57
-
58
- parsed_array_params = parse_array_params(params)
59
-
60
- non_nested_parent_params = get_non_nested_params(parsed_array_params)
61
-
62
- non_nested_parent_params.map do |param, value|
63
- items = {}
64
-
65
- raw_data_type = value[:type] if value.is_a?(Hash)
66
- raw_data_type ||= 'string'
67
- data_type = case raw_data_type.to_s
68
- when 'Hash'
69
- 'object'
70
- when 'Rack::Multipart::UploadedFile'
71
- 'File'
72
- when 'Virtus::Attribute::Boolean'
73
- 'boolean'
74
- when 'Boolean', 'Date', 'Integer', 'String', 'Float'
75
- raw_data_type.to_s.downcase
76
- when 'BigDecimal'
77
- 'long'
78
- when 'DateTime'
79
- 'dateTime'
80
- when 'Numeric'
81
- 'double'
82
- when 'Symbol'
83
- 'string'
84
- when /^\[(?<type>.*)\]$/
85
- items[:type] = Regexp.last_match[:type].downcase
86
- if PRIMITIVE_MAPPINGS.key?(items[:type])
87
- items[:type], items[:format] = PRIMITIVE_MAPPINGS[items[:type]]
88
- end
89
- 'array'
90
- else
91
- @@documentation_class.parse_entity_name(raw_data_type)
92
- end
93
-
94
- additional_documentation = value.is_a?(Hash) ? value[:documentation] : nil
95
-
96
- if additional_documentation && value.is_a?(Hash)
97
- value = additional_documentation.merge(value)
98
- end
99
-
100
- description = value.is_a?(Hash) ? value[:desc] || value[:description] : ''
101
- required = value.is_a?(Hash) ? !!value[:required] : false
102
- default_value = value.is_a?(Hash) ? value[:default] : nil
103
- example = value.is_a?(Hash) ? value[:example] : nil
104
- is_array = value.is_a?(Hash) ? (value[:is_array] || false) : false
105
- values = value.is_a?(Hash) ? value[:values] : nil
106
- enum_or_range_values = parse_enum_or_range_values(values)
107
-
108
- if value.is_a?(Hash) && value.key?(:param_type)
109
- param_type = value[:param_type]
110
- if is_array
111
- items = { '$ref' => data_type }
112
- data_type = 'array'
113
- end
114
- else
115
- param_type = case
116
- when path.include?(":#{param}")
117
- 'path'
118
- when %w(POST PUT PATCH).include?(method)
119
- if is_primitive?(data_type)
120
- 'form'
121
- else
122
- 'body'
123
- end
124
- else
125
- 'query'
126
- end
127
- end
128
- name = (value.is_a?(Hash) && value[:full_name]) || param
129
- description = translate(description, scope,
130
- i18n_keys.map { |key| :"#{key}.params.#{name}" })
131
-
132
- parsed_params = {
133
- paramType: param_type,
134
- name: name,
135
- description: as_markdown(description),
136
- type: data_type,
137
- required: required,
138
- allowMultiple: is_array && data_type != 'array' && %w(query header path).include?(param_type)
139
- }
140
-
141
- if PRIMITIVE_MAPPINGS.key?(data_type)
142
- parsed_params[:type], parsed_params[:format] = PRIMITIVE_MAPPINGS[data_type]
143
- end
144
-
145
- parsed_params[:items] = items if items.present?
146
-
147
- parsed_params[:defaultValue] = example if example
148
- if default_value && example.blank?
149
- parsed_params[:defaultValue] = default_value
150
- end
151
-
152
- parsed_params.merge!(enum_or_range_values) if enum_or_range_values
153
- parsed_params
154
- end
155
- end
156
-
157
- def content_types_for(target_class)
158
- content_types = (target_class.content_types || {}).values
159
-
160
- if content_types.empty?
161
- formats = [target_class.format, target_class.default_format].compact.uniq
162
- formats = Grape::Formatter::Base.formatters({}).keys if formats.empty?
163
- content_types = Grape::ContentTypes::CONTENT_TYPES.select { |content_type, _mime_type| formats.include? content_type }.values
164
- end
165
-
166
- content_types.uniq
167
- end
168
-
169
- def parse_info(info, options = {})
170
- scope = options[:scope]
171
-
172
- {
173
- contact: translate(info[:contact], scope, :'info.contact'),
174
- description: as_markdown(translate(info[:description], scope, [:'info.desc', :'info.description'])),
175
- license: translate(info[:license], scope, :'info.license'),
176
- licenseUrl: translate(info[:license_url], scope, :'info.license_url'),
177
- termsOfServiceUrl: translate(info[:terms_of_service_url], scope, :'info.terms_of_service_url'),
178
- title: translate(info[:title], scope, :'info.title')
179
- }.delete_if { |_, value| value.blank? }
180
- end
181
-
182
- def parse_header_params(params, options = {})
183
- scope = options[:scope]
184
- i18n_keys = expand_scope(options[:key])
185
- params ||= []
186
-
187
- params.map do |param, value|
188
- data_type = 'string'
189
- description = value.is_a?(Hash) ? value[:description] : ''
190
- required = value.is_a?(Hash) ? !!value[:required] : false
191
- default_value = value.is_a?(Hash) ? value[:default] : nil
192
- param_type = 'header'
193
-
194
- description = translate(description, scope,
195
- i18n_keys.map { |key| :"#{key}.params.#{param}" })
196
-
197
- parsed_params = {
198
- paramType: param_type,
199
- name: param,
200
- description: as_markdown(description),
201
- type: data_type,
202
- required: required
203
- }
204
-
205
- parsed_params[:defaultValue] = default_value if default_value
206
-
207
- parsed_params
208
- end
209
- end
210
-
211
- def parse_path(path, version)
212
- # adapt format to swagger format
213
- parsed_path = path.sub(/\(\..*\)$/, @@hide_format ? '' : '.{format}')
214
-
215
- # This is attempting to emulate the behavior of
216
- # Rack::Mount::Strexp. We cannot use Strexp directly because
217
- # all it does is generate regular expressions for parsing URLs.
218
- # TODO: Implement a Racc tokenizer to properly generate the
219
- # parsed path.
220
- parsed_path = parsed_path.gsub(/:([a-zA-Z_]\w*)/, '{\1}')
221
-
222
- # add the version
223
- version ? parsed_path.gsub('{version}', version) : parsed_path
224
- end
225
-
226
- def parse_entity_name(model)
227
- if model.respond_to?(:entity_name)
228
- model.entity_name
229
- elsif (root = model.instance_variable_get(:@root))
230
- root
231
- else
232
- name = model.to_s
233
- entity_parts = name.split('::')
234
- entity_parts.reject! { |p| p == 'Entity' || p == 'Entities' }
235
- entity_parts.join('::')
236
- end
237
- end
238
-
239
- def parse_entity_models(models, options = {})
240
- scope = options[:scope]
241
- result = {}
242
- models.each do |model|
243
- name = parse_entity_name(model)
244
- properties = {}
245
- required = []
246
-
247
- i18n_keys = []
248
- klass = model
249
- until %w(entity object).include? klass.name.demodulize.underscore
250
- i18n_keys << klass.name.demodulize.underscore.to_sym
251
- klass = klass.superclass
252
- end
253
- i18n_keys << :default
254
-
255
- model.exposures.each do |property_name, property_info|
256
- next unless property_info.key? :documentation
257
- property_name = property_info[:as] if property_info.key? :as
258
- p = property_info[:documentation].dup
259
-
260
- required << property_name.to_s if p.delete(:required)
261
-
262
- type = if p[:type]
263
- p.delete(:type)
264
- else
265
- parse_entity_name(property_info[:using])
266
- end
267
-
268
- if p.delete(:is_array)
269
- p[:items] = generate_typeref(type)
270
- p[:type] = 'array'
271
- else
272
- p.merge! generate_typeref(type)
273
- end
274
-
275
- # rename Grape Entity's "desc" to "description"
276
- property_description = p.delete(:desc)
277
- property_description = translate(property_description, scope,
278
- i18n_keys.map { |key| :"entities.#{key}.#{property_name}" })
279
- p[:description] = property_description unless property_description.blank?
280
-
281
- # rename Grape's 'values' to 'enum'
282
- select_values = p.delete(:values)
283
- if select_values
284
- select_values = select_values.call if select_values.is_a?(Proc)
285
- p[:enum] = select_values
286
- end
287
-
288
- if PRIMITIVE_MAPPINGS.key?(p['type'])
289
- p['type'], p['format'] = PRIMITIVE_MAPPINGS[p['type']]
290
- end
291
-
292
- properties[property_name] = p
293
- end
294
-
295
- result[name] = {
296
- id: name,
297
- properties: properties
298
- }
299
- result[name].merge!(required: required) unless required.empty?
300
- end
301
-
302
- result
303
- end
304
-
305
- def models_with_included_presenters(models)
306
- all_models = models
307
-
308
- models.each do |model|
309
- # get model references from exposures with a documentation
310
- nested_models = model.exposures.map do |_, config|
311
- if config.key?(:documentation)
312
- model = config[:using]
313
- model.respond_to?(:constantize) ? model.constantize : model
314
- end
315
- end.compact
316
-
317
- # get all nested models recursively
318
- additional_models = nested_models.map do |nested_model|
319
- models_with_included_presenters([nested_model])
320
- end.flatten
321
-
322
- all_models += additional_models
323
- end
324
-
325
- all_models
326
- end
327
-
328
- def is_primitive?(type)
329
- %w(object integer long float double string byte boolean date dateTime).include? type
330
- end
331
-
332
- def generate_typeref(type)
333
- type_s = type.to_s.sub(/^[A-Z]/, &:downcase)
334
- if is_primitive? type_s
335
- { 'type' => type_s }
336
- else
337
- { '$ref' => parse_entity_name(type) }
338
- end
339
- end
340
-
341
- def parse_http_codes(codes, models)
342
- codes ||= {}
343
- codes.map do |k, v, m|
344
- models << m if m
345
- http_code_hash = {
346
- code: k,
347
- message: v
348
- }
349
- http_code_hash[:responseModel] = parse_entity_name(m) if m
350
- http_code_hash
351
- end
352
- end
353
-
354
- def strip_heredoc(string)
355
- indent = string.scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
356
- string.gsub(/^[ \t]{#{indent}}/, '')
357
- end
358
-
359
- def parse_base_path(base_path, request)
360
- if base_path.is_a?(Proc)
361
- base_path.call(request)
362
- elsif base_path.is_a?(String)
363
- URI(base_path).relative? ? URI.join(request.base_url, base_path).to_s : base_path
364
- else
365
- request.base_url
366
- end
367
- end
368
-
369
19
  def hide_documentation_path
370
20
  @@hide_documentation_path
371
21
  end
@@ -375,217 +25,92 @@ module GrapeSwagger
375
25
  end
376
26
 
377
27
  def setup(options)
378
- defaults = {
379
- target_class: nil,
380
- mount_path: '/swagger_doc',
381
- base_path: nil,
382
- api_version: '0.1',
383
- markdown: nil,
384
- i18n_scope: :api,
385
- hide_documentation_path: false,
386
- hide_format: false,
387
- format: nil,
388
- models: [],
389
- info: {},
390
- authorizations: nil,
391
- root_base_path: true,
392
- api_documentation: { desc: 'Swagger compatible API description' },
393
- specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
394
- }
395
-
396
28
  options = defaults.merge(options)
397
29
 
30
+ # options could be set on #add_swagger_documentation call,
31
+ # for available options see #defaults
398
32
  target_class = options[:target_class]
399
- @@mount_path = options[:mount_path]
400
- @@class_name = options[:class_name] || options[:mount_path].delete('/')
401
- @@markdown = options[:markdown] ? GrapeSwagger::Markdown.new(options[:markdown]) : nil
402
- @@hide_format = options[:hide_format]
403
- api_version = options[:api_version]
404
- authorizations = options[:authorizations]
405
- root_base_path = options[:root_base_path]
406
- extra_info = options[:info]
407
33
  api_doc = options[:api_documentation].dup
408
34
  specific_api_doc = options[:specific_api_documentation].dup
409
- @@models = options[:models] || []
410
- i18n_scope = options[:i18n_scope]
411
-
412
- @@hide_documentation_path = options[:hide_documentation_path]
413
-
414
- if options[:format]
415
- [:format, :default_format, :default_error_formatter].each do |method|
416
- send(method, options[:format])
417
- end
418
- end
419
35
 
420
- @@documentation_class = self
36
+ class_variables_from(options)
421
37
 
38
+ [:format, :default_format, :default_error_formatter].each do |method|
39
+ send(method, options[:format])
40
+ end if options[:format]
41
+ # getting of the whole swagger2.0 spec file
422
42
  desc api_doc.delete(:desc), api_doc
423
- params do
424
- optional :locale, type: Symbol, desc: 'Locale of API documentation'
425
- end
426
- get @@mount_path.to_s do
427
- I18n.locale = params[:locale] || I18n.default_locale
43
+ get mount_path do
428
44
  header['Access-Control-Allow-Origin'] = '*'
429
45
  header['Access-Control-Request-Method'] = '*'
430
46
 
431
- namespaces = target_class.combined_namespaces
432
- namespace_routes = target_class.combined_namespace_routes
433
-
434
- if @@hide_documentation_path
435
- namespace_routes.reject! { |route, _value| "/#{route}/".index(@@documentation_class.parse_path(@@mount_path, nil) << '/') == 0 }
436
- end
437
-
438
- namespace_routes_array = namespace_routes.keys.map do |local_route|
439
- next if namespace_routes[local_route].map do |route|
440
- route.settings[:description] && route.settings[:description][:hidden]
441
- end.all? { |value| value.respond_to?(:call) ? value.call : value }
442
-
443
- url_format = '.{format}' unless @@hide_format
444
- url_locale = "?locale=#{params[:locale]}" unless params[:locale].blank?
445
-
446
- original_namespace_name = target_class.combined_namespace_identifiers.key?(local_route) ? target_class.combined_namespace_identifiers[local_route] : local_route
447
- description = namespaces[original_namespace_name] && namespaces[original_namespace_name].options[:desc]
448
- description ||= "Operations about #{original_namespace_name.pluralize}"
449
- description = @@documentation_class.translate(
450
- description, i18n_scope,
451
- [
452
- :"#{original_namespace_name}.desc",
453
- :"#{original_namespace_name}.description"
454
- ],
455
- namespace: original_namespace_name.pluralize
456
- )
47
+ output = swagger_object(
48
+ target_class,
49
+ request,
50
+ options
51
+ )
457
52
 
458
- {
459
- path: "/#{local_route}#{url_format}#{url_locale}",
460
- description: description
461
- }
462
- end.compact
463
-
464
- output = {
465
- apiVersion: api_version,
466
- swaggerVersion: '1.2',
467
- produces: @@documentation_class.content_types_for(target_class),
468
- apis: namespace_routes_array,
469
- info: @@documentation_class.parse_info(extra_info, scope: i18n_scope)
470
- }
471
-
472
- output[:authorizations] = authorizations unless authorizations.nil? || authorizations.empty?
53
+ target_routes = target_class.combined_namespace_routes
54
+ paths, definitions = path_and_definition_objects(target_routes, options)
55
+ output[:paths] = paths unless paths.blank?
56
+ output[:definitions] = definitions unless definitions.blank?
473
57
 
474
58
  output
475
59
  end
476
60
 
61
+ # getting of a specific/named route of the swagger2.0 spec file
477
62
  desc specific_api_doc.delete(:desc), { params:
478
63
  specific_api_doc.delete(:params) || {} }.merge(specific_api_doc)
479
64
  params do
480
- optional :locale, type: Symbol, desc: 'Locale of API documentation'
481
65
  requires :name, type: String, desc: 'Resource name of mounted API'
66
+ optional :locale, type: Symbol, desc: 'Locale of API documentation'
482
67
  end
483
- get "#{@@mount_path}/:name" do
68
+ get "#{mount_path}/:name" do
484
69
  I18n.locale = params[:locale] || I18n.default_locale
485
- header['Access-Control-Allow-Origin'] = '*'
486
- header['Access-Control-Request-Method'] = '*'
487
-
488
- models = Set.new(@@models.dup)
489
- routes = target_class.combined_namespace_routes[params[:name]]
490
- error!('Not Found', 404) unless routes
491
-
492
- visible_ops = routes.reject do |route|
493
- hidden = route.options[:hidden]
494
- hidden && hidden.respond_to?(:call) ? hidden.call : hidden
495
- end
496
-
497
- ops = visible_ops.group_by do |route|
498
- @@documentation_class.parse_path(route.path, api_version)
499
- end
500
-
501
- error!('Not Found', 404) unless ops.any?
502
-
503
- apis = []
504
-
505
- ops.each do |path, op_routes|
506
- operations = op_routes.map do |route|
507
- route_settings_description = route.settings[:description] || {}
508
- endpoint = target_class.endpoint_mapping[route.to_s.sub('(.:format)', '')]
509
- endpoint_path = endpoint.options[:path] unless endpoint.nil?
510
- i18n_key = [route.namespace, endpoint_path, route.request_method.downcase].flatten.join('/')
511
- i18n_key = i18n_key.split('/').reject(&:empty?).join('.')
512
-
513
- summary = @@documentation_class.translate(
514
- route.description, i18n_scope,
515
- [:"#{i18n_key}.desc", :"#{i18n_key}.description"]
516
- )
517
- notes = @@documentation_class.translate(
518
- route_settings_description[:detail] || route_settings_description[:notes], i18n_scope,
519
- [:"#{i18n_key}.detail", :"#{i18n_key}.notes"]
520
- )
521
- notes = @@documentation_class.as_markdown(notes)
522
-
523
- http_codes = @@documentation_class.parse_http_codes(route.http_codes, models)
524
-
525
- models.merge(Array(route.entity)) if route.entity.present?
526
-
527
- route_settings_description = route.settings[:description] || {}
528
-
529
- operation = {
530
- notes: notes.to_s,
531
- summary: summary,
532
- nickname: route_settings_description[:nickname] || (route.request_method + route.path.gsub(/[\/:\(\)\.]/, '-')),
533
- method: route.request_method,
534
- parameters: @@documentation_class.parse_header_params(route.headers, scope: i18n_scope, key: i18n_key) +
535
- @@documentation_class.parse_params(route.params, route.path, route.request_method,
536
- scope: i18n_scope, key: i18n_key),
537
- type: route_settings_description[:is_array] ? 'array' : 'void'
538
- }
539
-
540
- authorizations = route_settings_description[:authorizations]
541
- operation[:authorizations] = authorizations if authorizations && authorizations.any?
542
70
 
543
- if operation[:parameters].any? { |param| param[:type] == 'File' }
544
- operation[:consumes] = ['multipart/form-data']
545
- end
546
- operation[:responseMessages] = http_codes unless http_codes.empty?
71
+ combined_routes = target_class.combined_namespace_routes[params[:name]]
72
+ error!({ error: 'named resource not exist' }, 400) if combined_routes.nil?
547
73
 
548
- if route.entity
549
- type = @@documentation_class.parse_entity_name(Array(route.entity).first)
550
- if route_settings_description[:is_array]
551
- operation[:items] = { '$ref' => type }
552
- else
553
- operation[:type] = type
554
- end
555
- end
74
+ output = swagger_object(
75
+ target_class,
76
+ request,
77
+ options
78
+ )
556
79
 
557
- operation[:nickname] = route_settings_description[:nickname] if route_settings_description.key?(:nickname)
558
- operation
559
- end.compact
560
- apis << {
561
- path: path,
562
- operations: operations
563
- }
564
- end
80
+ target_routes = { params[:name] => combined_routes }
81
+ paths, definitions = path_and_definition_objects(target_routes, options)
82
+ output[:paths] = paths unless paths.blank?
83
+ output[:definitions] = definitions unless definitions.blank?
565
84
 
566
- models = @@documentation_class.models_with_included_presenters(models.to_a.flatten.compact)
567
-
568
- # use custom resource naming if available
569
- resource_path = if target_class.combined_namespace_identifiers.key? params[:name]
570
- target_class.combined_namespace_identifiers[params[:name]]
571
- else
572
- params[:name]
573
- end
574
- api_description = {
575
- apiVersion: api_version,
576
- swaggerVersion: '1.2',
577
- resourcePath: "/#{resource_path}",
578
- produces: @@documentation_class.content_types_for(target_class),
579
- apis: apis
580
- }
85
+ output
86
+ end
87
+ end
581
88
 
582
- base_path = @@documentation_class.parse_base_path(options[:base_path], request)
583
- api_description[:basePath] = base_path if base_path && !base_path.empty? && root_base_path != false
584
- api_description[:models] = @@documentation_class.parse_entity_models(models, scope: i18n_scope) unless models.empty?
585
- api_description[:authorizations] = authorizations if authorizations
89
+ def defaults
90
+ {
91
+ info: {},
92
+ models: [],
93
+ schemes: %w( https http ),
94
+ api_version: 'v1',
95
+ target_class: nil,
96
+ mount_path: '/swagger_doc',
97
+ host: nil,
98
+ base_path: nil,
99
+ add_base_path: false,
100
+ add_version: true,
101
+ markdown: false,
102
+ hide_documentation_path: true,
103
+ format: :json,
104
+ authorizations: nil,
105
+ api_documentation: { desc: 'Swagger compatible API description' },
106
+ specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
107
+ }
108
+ end
586
109
 
587
- api_description
588
- end
110
+ def class_variables_from(options)
111
+ @@mount_path = options[:mount_path]
112
+ @@class_name = options[:class_name] || options[:mount_path].delete('/')
113
+ @@hide_documentation_path = options[:hide_documentation_path]
589
114
  end
590
115
  end
591
116
  end