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.
- checksums.yaml +4 -4
- data/.gitignore +8 -1
- data/.rubocop.yml +3 -0
- data/.rubocop_todo.yml +14 -22
- data/.travis.yml +7 -4
- data/CHANGELOG.md +53 -26
- data/Gemfile +1 -1
- data/README.md +414 -327
- data/RELEASING.md +3 -4
- data/example/api/endpoints.rb +132 -0
- data/example/api/entities.rb +18 -0
- data/example/config.ru +36 -2
- data/example/example_requests.postman_collection +146 -0
- data/example/swagger-example.png +0 -0
- data/grape-swagger.gemspec +9 -6
- data/lib/grape-swagger.rb +69 -99
- data/lib/grape-swagger/doc_methods.rb +69 -544
- data/lib/grape-swagger/doc_methods/data_type.rb +77 -0
- data/lib/grape-swagger/doc_methods/extensions.rb +75 -0
- data/lib/grape-swagger/doc_methods/move_params.rb +153 -0
- data/lib/grape-swagger/doc_methods/operation_id.rb +27 -0
- data/lib/grape-swagger/doc_methods/optional_object.rb +15 -0
- data/lib/grape-swagger/doc_methods/parse_params.rb +113 -0
- data/lib/grape-swagger/doc_methods/path_string.rb +29 -0
- data/lib/grape-swagger/doc_methods/produces_consumes.rb +12 -0
- data/lib/grape-swagger/doc_methods/status_codes.rb +17 -0
- data/lib/grape-swagger/doc_methods/tag_name_description.rb +26 -0
- data/lib/grape-swagger/endpoint.rb +317 -0
- data/lib/grape-swagger/version.rb +1 -1
- data/spec/lib/data_type_spec.rb +57 -0
- data/spec/lib/endpoint_spec.rb +6 -0
- data/spec/lib/extensions_spec.rb +127 -0
- data/spec/lib/move_params_spec.rb +298 -0
- data/spec/lib/operation_id_spec.rb +24 -0
- data/spec/lib/optional_object_spec.rb +40 -0
- data/spec/lib/path_string_spec.rb +38 -0
- data/spec/lib/produces_consumes_spec.rb +98 -0
- data/spec/markdown/kramdown_adapter_spec.rb +2 -9
- data/spec/markdown/redcarpet_adapter_spec.rb +2 -16
- data/spec/spec_helper.rb +7 -13
- data/spec/support/api_swagger_v2_result.rb +204 -0
- data/spec/support/namespace_tags.rb +73 -0
- data/spec/support/the_api_entities.rb +52 -0
- data/spec/support/the_paths_definitions.rb +94 -0
- data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +32 -0
- data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +151 -0
- data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +109 -0
- data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +124 -0
- data/spec/swagger_v2/api_swagger_v2_global_configuration_spec.rb +51 -0
- data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +44 -0
- data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +56 -0
- data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +146 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +197 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +151 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +217 -0
- data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +64 -0
- data/spec/swagger_v2/api_swagger_v2_response_spec.rb +184 -0
- data/spec/swagger_v2/api_swagger_v2_spec.rb +207 -0
- data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +121 -0
- data/spec/{boolean_params_spec.rb → swagger_v2/boolean_params_spec.rb} +2 -2
- data/spec/{default_api_spec.rb → swagger_v2/default_api_spec.rb} +40 -36
- data/spec/swagger_v2/description_not_initialized.rb +39 -0
- data/spec/{float_api_spec.rb → swagger_v2/float_api_spec.rb} +2 -2
- data/spec/{form_params_spec.rb → swagger_v2/form_params_spec.rb} +9 -9
- data/spec/{grape-swagger_spec.rb → swagger_v2/grape-swagger_spec.rb} +0 -0
- data/spec/swagger_v2/hide_api_spec.rb +131 -0
- data/spec/swagger_v2/mounted_target_class_spec.rb +76 -0
- data/spec/swagger_v2/namespace_tags_prefix_spec.rb +84 -0
- data/spec/swagger_v2/namespace_tags_spec.rb +76 -0
- data/spec/{namespaced_api_spec.rb → swagger_v2/namespaced_api_spec.rb} +6 -26
- data/spec/{param_type_spec.rb → swagger_v2/param_type_spec.rb} +10 -8
- data/spec/{param_values_spec.rb → swagger_v2/param_values_spec.rb} +52 -22
- data/spec/swagger_v2/params_array_spec.rb +63 -0
- data/spec/swagger_v2/params_hash_spec.rb +65 -0
- data/spec/swagger_v2/params_nested_spec.rb +63 -0
- data/spec/{reference_entity.rb → swagger_v2/reference_entity.rb} +18 -23
- data/spec/swagger_v2/response_model_spec.rb +212 -0
- data/spec/swagger_v2/simple_mounted_api_spec.rb +264 -0
- metadata +175 -90
- data/example/api.rb +0 -66
- data/lib/grape-swagger/markdown.rb +0 -23
- data/spec/api_description_spec.rb +0 -43
- data/spec/api_global_models_spec.rb +0 -77
- data/spec/api_models_spec.rb +0 -364
- data/spec/api_paths_spec.rb +0 -128
- data/spec/api_root_spec.rb +0 -30
- data/spec/api_with_nil_types.rb +0 -50
- data/spec/api_with_path_versioning_spec.rb +0 -33
- data/spec/api_with_prefix_and_namespace_spec.rb +0 -32
- data/spec/api_with_standalone_namespace_spec.rb +0 -215
- data/spec/array_entity_spec.rb +0 -34
- data/spec/array_params_spec.rb +0 -85
- data/spec/grape-swagger_helper_spec.rb +0 -152
- data/spec/group_params_spec.rb +0 -31
- data/spec/hash_params_spec.rb +0 -30
- data/spec/hide_api_spec.rb +0 -124
- data/spec/i18n_spec.rb +0 -364
- data/spec/markdown/markdown_spec.rb +0 -27
- data/spec/mounted_target_class_spec.rb +0 -63
- data/spec/mutually_exclusive_spec.rb +0 -36
- data/spec/non_default_api_spec.rb +0 -733
- data/spec/response_model_spec.rb +0 -121
- data/spec/simple_mounted_api_spec.rb +0 -213
- data/spec/support/i18n_helper.rb +0 -8
@@ -1,371 +1,21 @@
|
|
1
|
-
require '
|
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
|
-
|
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
|
-
|
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
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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
|
-
|
460
|
-
|
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 "#{
|
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
|
-
|
544
|
-
|
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
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
operation[:type] = type
|
554
|
-
end
|
555
|
-
end
|
74
|
+
output = swagger_object(
|
75
|
+
target_class,
|
76
|
+
request,
|
77
|
+
options
|
78
|
+
)
|
556
79
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
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
|
-
|
567
|
-
|
568
|
-
|
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
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
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
|
-
|
588
|
-
|
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
|