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.
data/lib/grape-swagger.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'grape'
2
2
  require 'grape-swagger/version'
3
3
  require 'grape-swagger/errors'
4
+ require 'grape-swagger/doc_methods'
4
5
  require 'grape-swagger/markdown'
5
6
  require 'grape-swagger/markdown/kramdown_adapter'
6
7
  require 'grape-swagger/markdown/redcarpet_adapter'
@@ -8,16 +9,19 @@ require 'grape-swagger/markdown/redcarpet_adapter'
8
9
  module Grape
9
10
  class API
10
11
  class << self
11
- attr_reader :combined_routes, :combined_namespaces, :combined_namespace_routes, :combined_namespace_identifiers
12
+ attr_accessor :combined_routes, :combined_namespaces, :combined_namespace_routes, :combined_namespace_identifiers
12
13
 
13
14
  def add_swagger_documentation(options = {})
14
15
  documentation_class = create_documentation_class
15
16
 
16
- documentation_class.setup({ target_class: self }.merge(options))
17
+ options = { target_class: self }.merge(options)
18
+ @target_class = options[:target_class]
19
+
20
+ documentation_class.setup(options)
17
21
  mount(documentation_class)
18
22
 
19
- @combined_routes = {}
20
- routes.each do |route|
23
+ @target_class.combined_routes = {}
24
+ @target_class.routes.each do |route|
21
25
  route_path = route.route_path
22
26
  route_match = route_path.split(/^.*?#{route.route_prefix.to_s}/).last
23
27
  next unless route_match
@@ -26,20 +30,20 @@ module Grape
26
30
  resource = route_match.captures.first
27
31
  next if resource.empty?
28
32
  resource.downcase!
29
- @combined_routes[resource] ||= []
30
- next if documentation_class.hide_documentation_path && route.route_path.include?(documentation_class.mount_path)
31
- @combined_routes[resource] << route
33
+ @target_class.combined_routes[resource] ||= []
34
+ next if documentation_class.hide_documentation_path && route.route_path.match(/#{documentation_class.mount_path}($|\/|\(\.)/)
35
+ @target_class.combined_routes[resource] << route
32
36
  end
33
37
 
34
- @combined_namespaces = {}
35
- combine_namespaces(self)
38
+ @target_class.combined_namespaces = {}
39
+ combine_namespaces(@target_class)
36
40
 
37
- @combined_namespace_routes = {}
38
- @combined_namespace_identifiers = {}
39
- combine_namespace_routes(@combined_namespaces)
41
+ @target_class.combined_namespace_routes = {}
42
+ @target_class.combined_namespace_identifiers = {}
43
+ combine_namespace_routes(@target_class.combined_namespaces)
40
44
 
41
- exclusive_route_keys = @combined_routes.keys - @combined_namespaces.keys
42
- exclusive_route_keys.each { |key| @combined_namespace_routes[key] = @combined_routes[key] }
45
+ exclusive_route_keys = @target_class.combined_routes.keys - @target_class.combined_namespaces.keys
46
+ exclusive_route_keys.each { |key| @target_class.combined_namespace_routes[key] = @target_class.combined_routes[key] }
43
47
  documentation_class
44
48
  end
45
49
 
@@ -54,7 +58,7 @@ module Grape
54
58
  end
55
59
  # use the full namespace here (not the latest level only)
56
60
  # and strip leading slash
57
- @combined_namespaces[endpoint.namespace.sub(/^\//, '')] = ns if ns
61
+ @target_class.combined_namespaces[endpoint.namespace.sub(/^\//, '')] = ns if ns
58
62
 
59
63
  combine_namespaces(endpoint.options[:app]) if endpoint.options[:app]
60
64
  end
@@ -65,10 +69,10 @@ module Grape
65
69
  namespaces.each do |name, namespace|
66
70
  # get the parent route for the namespace
67
71
  parent_route_name = name.match(%r{^/?([^/]*).*$})[1]
68
- parent_route = @combined_routes[parent_route_name]
72
+ parent_route = @target_class.combined_routes[parent_route_name]
69
73
  # fetch all routes that are within the current namespace
70
74
  namespace_routes = parent_route.collect do |route|
71
- route if (route.route_path.start_with?("/#{name}") || route.route_path.start_with?("/:version/#{name}")) &&
75
+ route if (route.route_path.start_with?(route.route_prefix ? "/#{route.route_prefix}/#{name}" : "/#{name}") || route.route_path.start_with?((route.route_prefix ? "/#{route.route_prefix}/:version/#{name}" : "/:version/#{name}"))) &&
72
76
  (route.instance_variable_get(:@options)[:namespace] == "/#{name}" || route.instance_variable_get(:@options)[:namespace] == "/:version/#{name}")
73
77
  end.compact
74
78
 
@@ -79,8 +83,8 @@ module Grape
79
83
  else
80
84
  identifier = name.gsub(/_/, '-').gsub(/\//, '_')
81
85
  end
82
- @combined_namespace_identifiers[identifier] = name
83
- @combined_namespace_routes[identifier] = namespace_routes
86
+ @target_class.combined_namespace_identifiers[identifier] = name
87
+ @target_class.combined_namespace_routes[identifier] = namespace_routes
84
88
 
85
89
  # get all nested namespaces below the current namespace
86
90
  sub_namespaces = standalone_sub_namespaces(name, namespaces)
@@ -92,7 +96,7 @@ module Grape
92
96
  route if sub_ns_paths.include?(route.instance_variable_get(:@options)[:namespace]) || sub_ns_paths_versioned.include?(route.instance_variable_get(:@options)[:namespace])
93
97
  end.compact
94
98
  # add all determined routes of the sub namespaces to standalone resource
95
- @combined_namespace_routes[identifier].push(*sub_routes)
99
+ @target_class.combined_namespace_routes[identifier].push(*sub_routes)
96
100
  else
97
101
  # default case when not explicitly specified or nested == true
98
102
  standalone_namespaces = namespaces.reject { |_, ns| !ns.options.key?(:swagger) || !ns.options[:swagger].key?(:nested) || ns.options[:swagger][:nested] != false }
@@ -100,8 +104,8 @@ module Grape
100
104
  # add only to the main route if the namespace is not within any other namespace appearing as standalone resource
101
105
  if parent_standalone_namespaces.empty?
102
106
  # default option, append namespace methods to parent route
103
- @combined_namespace_routes[parent_route_name] = [] unless @combined_namespace_routes.key?(parent_route_name)
104
- @combined_namespace_routes[parent_route_name].push(*namespace_routes)
107
+ @target_class.combined_namespace_routes[parent_route_name] = [] unless @target_class.combined_namespace_routes.key?(parent_route_name)
108
+ @target_class.combined_namespace_routes[parent_route_name].push(*namespace_routes)
105
109
  end
106
110
  end
107
111
  end
@@ -125,7 +129,7 @@ module Grape
125
129
 
126
130
  def get_non_nested_params(params)
127
131
  # Duplicate the params as we are going to modify them
128
- dup_params = params.each_with_object(Hash.new) do |(param, value), dparams|
132
+ dup_params = params.each_with_object({}) do |(param, value), dparams|
129
133
  dparams[param] = value.dup
130
134
  end
131
135
 
@@ -145,11 +149,13 @@ module Grape
145
149
  params.each_key do |k|
146
150
  if params[k].is_a?(Hash) && params[k][:type] == 'Array'
147
151
  array_param = k
152
+ modified_params[k] = params[k]
148
153
  else
149
154
  new_key = k
150
155
  unless array_param.nil?
151
156
  if k.to_s.start_with?(array_param.to_s + '[')
152
157
  new_key = array_param.to_s + '[]' + k.to_s.split(array_param)[1]
158
+ modified_params.delete array_param
153
159
  end
154
160
  end
155
161
  modified_params[new_key] = params[k]
@@ -158,461 +164,29 @@ module Grape
158
164
  modified_params
159
165
  end
160
166
 
161
- def create_documentation_class
162
- Class.new(Grape::API) do
163
- class << self
164
- def name
165
- @@class_name
166
- end
167
-
168
- def as_markdown(description)
169
- description && @@markdown ? @@markdown.as_markdown(strip_heredoc(description)) : description
170
- end
171
-
172
- def parse_params(params, path, method)
173
- params ||= []
174
-
175
- parsed_array_params = parse_array_params(params)
176
-
177
- non_nested_parent_params = get_non_nested_params(parsed_array_params)
178
-
179
- non_nested_parent_params.map do |param, value|
180
- items = {}
181
-
182
- raw_data_type = value.is_a?(Hash) ? (value[:type] || 'string').to_s : 'string'
183
- data_type = case raw_data_type
184
- when 'Hash'
185
- 'object'
186
- when 'Rack::Multipart::UploadedFile'
187
- 'File'
188
- when 'Virtus::Attribute::Boolean'
189
- 'boolean'
190
- when 'Boolean', 'Date', 'Integer', 'String', 'Float'
191
- raw_data_type.downcase
192
- when 'BigDecimal'
193
- 'long'
194
- when 'DateTime'
195
- 'dateTime'
196
- when 'Numeric'
197
- 'double'
198
- when 'Symbol'
199
- 'string'
200
- else
201
- @@documentation_class.parse_entity_name(raw_data_type)
202
- end
203
- description = value.is_a?(Hash) ? value[:desc] || value[:description] : ''
204
- required = value.is_a?(Hash) ? !!value[:required] : false
205
- default_value = value.is_a?(Hash) ? value[:default] : nil
206
- is_array = value.is_a?(Hash) ? (value[:is_array] || false) : false
207
- enum_values = value.is_a?(Hash) ? value[:values] : nil
208
- enum_values = enum_values.to_a if enum_values && enum_values.is_a?(Range)
209
- enum_values = enum_values.call if enum_values && enum_values.is_a?(Proc)
210
-
211
- if value.is_a?(Hash) && value.key?(:param_type)
212
- param_type = value[:param_type]
213
- if is_array
214
- items = { '$ref' => data_type }
215
- data_type = 'array'
216
- end
217
- else
218
- param_type = case
219
- when path.include?(":#{param}")
220
- 'path'
221
- when %w(POST PUT PATCH).include?(method)
222
- if is_primitive?(data_type)
223
- 'form'
224
- else
225
- 'body'
226
- end
227
- else
228
- 'query'
229
- end
230
- end
231
- name = (value.is_a?(Hash) && value[:full_name]) || param
232
-
233
- parsed_params = {
234
- paramType: param_type,
235
- name: name,
236
- description: as_markdown(description),
237
- type: data_type,
238
- required: required,
239
- allowMultiple: is_array
240
- }
241
- parsed_params.merge!(format: 'int32') if data_type == 'integer'
242
- parsed_params.merge!(format: 'int64') if data_type == 'long'
243
- parsed_params.merge!(items: items) if items.present?
244
- parsed_params.merge!(defaultValue: default_value) if default_value
245
- parsed_params.merge!(enum: enum_values) if enum_values
246
- parsed_params
247
- end
248
- end
249
-
250
- def content_types_for(target_class)
251
- content_types = (target_class.content_types || {}).values
252
-
253
- if content_types.empty?
254
- formats = [target_class.format, target_class.default_format].compact.uniq
255
- formats = Grape::Formatter::Base.formatters({}).keys if formats.empty?
256
- content_types = Grape::ContentTypes::CONTENT_TYPES.select { |content_type, _mime_type| formats.include? content_type }.values
257
- end
258
-
259
- content_types.uniq
260
- end
261
-
262
- def parse_info(info)
263
- {
264
- contact: info[:contact],
265
- description: as_markdown(info[:description]),
266
- license: info[:license],
267
- licenseUrl: info[:license_url],
268
- termsOfServiceUrl: info[:terms_of_service_url],
269
- title: info[:title]
270
- }.delete_if { |_, value| value.blank? }
271
- end
272
-
273
- def parse_header_params(params)
274
- params ||= []
275
-
276
- params.map do |param, value|
277
- data_type = 'String'
278
- description = value.is_a?(Hash) ? value[:description] : ''
279
- required = value.is_a?(Hash) ? !!value[:required] : false
280
- default_value = value.is_a?(Hash) ? value[:default] : nil
281
- param_type = 'header'
282
-
283
- parsed_params = {
284
- paramType: param_type,
285
- name: param,
286
- description: as_markdown(description),
287
- type: data_type,
288
- required: required
289
- }
290
-
291
- parsed_params.merge!(defaultValue: default_value) if default_value
292
-
293
- parsed_params
294
- end
295
- end
296
-
297
- def parse_path(path, version)
298
- # adapt format to swagger format
299
- parsed_path = path.gsub('(.:format)', @@hide_format ? '' : '.{format}')
300
- # This is attempting to emulate the behavior of
301
- # Rack::Mount::Strexp. We cannot use Strexp directly because
302
- # all it does is generate regular expressions for parsing URLs.
303
- # TODO: Implement a Racc tokenizer to properly generate the
304
- # parsed path.
305
- parsed_path = parsed_path.gsub(/:([a-zA-Z_]\w*)/, '{\1}')
306
- # add the version
307
- version ? parsed_path.gsub('{version}', version) : parsed_path
308
- end
309
-
310
- def parse_entity_name(model)
311
- if model.respond_to?(:entity_name)
312
- model.entity_name
313
- else
314
- name = model.to_s
315
- entity_parts = name.split('::')
316
- entity_parts.reject! { |p| p == 'Entity' || p == 'Entities' }
317
- entity_parts.join('::')
318
- end
319
- end
320
-
321
- def parse_entity_models(models)
322
- result = {}
323
- models.each do |model|
324
- name = parse_entity_name(model)
325
- properties = {}
326
- required = []
327
-
328
- model.documentation.each do |property_name, property_info|
329
- p = property_info.dup
330
-
331
- required << property_name.to_s if p.delete(:required)
332
-
333
- type = if p[:type]
334
- p.delete(:type)
335
- elsif (entity = model.exposures[property_name][:using])
336
- parse_entity_name(entity)
337
- end
338
-
339
- if p.delete(:is_array)
340
- p[:items] = generate_typeref(type)
341
- p[:type] = 'array'
342
- else
343
- p.merge! generate_typeref(type)
344
- end
345
-
346
- # rename Grape Entity's "desc" to "description"
347
- property_description = p.delete(:desc)
348
- p[:description] = property_description if property_description
349
-
350
- # rename Grape's 'values' to 'enum'
351
- select_values = p.delete(:values)
352
- if select_values
353
- select_values = select_values.call if select_values.is_a?(Proc)
354
- p[:enum] = select_values
355
- end
356
-
357
- properties[property_name] = p
358
- end
359
-
360
- result[name] = {
361
- id: model.instance_variable_get(:@root) || name,
362
- properties: properties
363
- }
364
- result[name].merge!(required: required) unless required.empty?
365
- end
366
-
367
- result
368
- end
369
-
370
- def models_with_included_presenters(models)
371
- all_models = models
372
-
373
- models.each do |model|
374
- # get model references from exposures with a documentation
375
- nested_models = model.exposures.map do |_, config|
376
- if config.key?(:documentation)
377
- model = config[:using]
378
- model.respond_to?(:constantize) ? model.constantize : model
379
- end
380
- end.compact
381
-
382
- # get all nested models recursively
383
- additional_models = nested_models.map do |nested_model|
384
- models_with_included_presenters([nested_model])
385
- end.flatten
386
-
387
- all_models += additional_models
388
- end
389
-
390
- all_models
391
- end
392
-
393
- def is_primitive?(type)
394
- %w(object integer long float double string byte boolean date dateTime).include? type
395
- end
396
-
397
- def generate_typeref(type)
398
- type = type.to_s.sub(/^[A-Z]/) { |f| f.downcase } if type.is_a?(Class)
399
- if is_primitive? type
400
- { 'type' => type }
401
- else
402
- { '$ref' => type }
403
- end
404
- end
405
-
406
- def parse_http_codes(codes, models)
407
- codes ||= {}
408
- codes.map do |k, v, m|
409
- models << m if m
410
- http_code_hash = {
411
- code: k,
412
- message: v
413
- }
414
- http_code_hash[:responseModel] = parse_entity_name(m) if m
415
- http_code_hash
416
- end
417
- end
418
-
419
- def strip_heredoc(string)
420
- indent = string.scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
421
- string.gsub(/^[ \t]{#{indent}}/, '')
422
- end
423
-
424
- def parse_base_path(base_path, request)
425
- if base_path.is_a?(Proc)
426
- base_path.call(request)
427
- elsif base_path.is_a?(String)
428
- URI(base_path).relative? ? URI.join(request.base_url, base_path).to_s : base_path
429
- else
430
- request.base_url
431
- end
432
- end
433
-
434
- def hide_documentation_path
435
- @@hide_documentation_path
436
- end
437
-
438
- def mount_path
439
- @@mount_path
440
- end
441
-
442
- def setup(options)
443
- defaults = {
444
- target_class: nil,
445
- mount_path: '/swagger_doc',
446
- base_path: nil,
447
- api_version: '0.1',
448
- markdown: nil,
449
- hide_documentation_path: false,
450
- hide_format: false,
451
- format: nil,
452
- models: [],
453
- info: {},
454
- authorizations: nil,
455
- root_base_path: true,
456
- api_documentation: { desc: 'Swagger compatible API description' },
457
- specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
458
- }
459
-
460
- options = defaults.merge(options)
461
-
462
- target_class = options[:target_class]
463
- @@mount_path = options[:mount_path]
464
- @@class_name = options[:class_name] || options[:mount_path].gsub('/', '')
465
- @@markdown = options[:markdown] ? GrapeSwagger::Markdown.new(options[:markdown]) : nil
466
- @@hide_format = options[:hide_format]
467
- api_version = options[:api_version]
468
- authorizations = options[:authorizations]
469
- root_base_path = options[:root_base_path]
470
- extra_info = options[:info]
471
- api_doc = options[:api_documentation].dup
472
- specific_api_doc = options[:specific_api_documentation].dup
473
- @@models = options[:models] || []
474
-
475
- @@hide_documentation_path = options[:hide_documentation_path]
476
-
477
- if options[:format]
478
- [:format, :default_format, :default_error_formatter].each do |method|
479
- send(method, options[:format])
480
- end
481
- end
482
-
483
- @@documentation_class = self
484
-
485
- desc api_doc.delete(:desc), api_doc
486
- get @@mount_path do
487
- header['Access-Control-Allow-Origin'] = '*'
488
- header['Access-Control-Request-Method'] = '*'
489
-
490
- namespaces = target_class.combined_namespaces
491
- namespace_routes = target_class.combined_namespace_routes
492
-
493
- if @@hide_documentation_path
494
- namespace_routes.reject! { |route, _value| "/#{route}/".index(@@documentation_class.parse_path(@@mount_path, nil) << '/') == 0 }
495
- end
496
-
497
- namespace_routes_array = namespace_routes.keys.map do |local_route|
498
- next if namespace_routes[local_route].map(&:route_hidden).all? { |value| value.respond_to?(:call) ? value.call : value }
499
-
500
- url_format = '.{format}' unless @@hide_format
501
-
502
- original_namespace_name = target_class.combined_namespace_identifiers.key?(local_route) ? target_class.combined_namespace_identifiers[local_route] : local_route
503
- description = namespaces[original_namespace_name] && namespaces[original_namespace_name].options[:desc]
504
- description ||= "Operations about #{original_namespace_name.pluralize}"
505
-
506
- {
507
- path: "/#{local_route}#{url_format}",
508
- description: description
509
- }
510
- end.compact
511
-
512
- output = {
513
- apiVersion: api_version,
514
- swaggerVersion: '1.2',
515
- produces: @@documentation_class.content_types_for(target_class),
516
- apis: namespace_routes_array,
517
- info: @@documentation_class.parse_info(extra_info)
518
- }
519
-
520
- output[:authorizations] = authorizations unless authorizations.nil? || authorizations.empty?
521
-
522
- output
523
- end
524
-
525
- desc specific_api_doc.delete(:desc), { params: {
526
- 'name' => {
527
- desc: 'Resource name of mounted API',
528
- type: 'string',
529
- required: true
530
- }
531
- }.merge(specific_api_doc.delete(:params) || {}) }.merge(specific_api_doc)
532
-
533
- get "#{@@mount_path}/:name" do
534
- header['Access-Control-Allow-Origin'] = '*'
535
- header['Access-Control-Request-Method'] = '*'
536
-
537
- models = []
538
- routes = target_class.combined_namespace_routes[params[:name]]
539
- error!('Not Found', 404) unless routes
540
-
541
- visible_ops = routes.reject do |route|
542
- route.route_hidden.respond_to?(:call) ? route.route_hidden.call : route.route_hidden
543
- end
544
-
545
- ops = visible_ops.group_by do |route|
546
- @@documentation_class.parse_path(route.route_path, api_version)
547
- end
548
-
549
- error!('Not Found', 404) unless ops.any?
550
-
551
- apis = []
552
-
553
- ops.each do |path, op_routes|
554
- operations = op_routes.map do |route|
555
- notes = @@documentation_class.as_markdown(route.route_notes)
556
-
557
- http_codes = @@documentation_class.parse_http_codes(route.route_http_codes, models)
558
-
559
- models |= @@models if @@models.present?
560
-
561
- models |= Array(route.route_entity) if route.route_entity.present?
562
-
563
- models = @@documentation_class.models_with_included_presenters(models.flatten.compact)
564
-
565
- operation = {
566
- notes: notes.to_s,
567
- summary: route.route_description || '',
568
- nickname: route.route_nickname || (route.route_method + route.route_path.gsub(/[\/:\(\)\.]/, '-')),
569
- method: route.route_method,
570
- parameters: @@documentation_class.parse_header_params(route.route_headers) + @@documentation_class.parse_params(route.route_params, route.route_path, route.route_method),
571
- type: 'void'
572
- }
573
- operation[:authorizations] = route.route_authorizations unless route.route_authorizations.nil? || route.route_authorizations.empty?
574
- if operation[:parameters].any? { | param | param[:type] == 'File' }
575
- operation.merge!(consumes: ['multipart/form-data'])
576
- end
577
- operation.merge!(responseMessages: http_codes) unless http_codes.empty?
578
-
579
- if route.route_entity
580
- type = @@documentation_class.parse_entity_name(Array(route.route_entity).first)
581
- operation.merge!('type' => type)
582
- end
583
-
584
- operation[:nickname] = route.route_nickname if route.route_nickname
585
- operation
586
- end.compact
587
- apis << {
588
- path: path,
589
- operations: operations
590
- }
591
- end
592
-
593
- # use custom resource naming if available
594
- if target_class.combined_namespace_identifiers.key? params[:name]
595
- resource_path = target_class.combined_namespace_identifiers[params[:name]]
596
- else
597
- resource_path = params[:name]
598
- end
599
- api_description = {
600
- apiVersion: api_version,
601
- swaggerVersion: '1.2',
602
- resourcePath: "/#{resource_path}",
603
- produces: @@documentation_class.content_types_for(target_class),
604
- apis: apis
605
- }
167
+ def parse_enum_or_range_values(values)
168
+ case values
169
+ when Range
170
+ parse_range_values(values) if values.first.is_a?(Integer)
171
+ when Proc
172
+ values_result = values.call
173
+ if values_result.is_a?(Range) && values_result.first.is_a?(Integer)
174
+ parse_range_values(values_result)
175
+ else
176
+ { enum: values_result }
177
+ end
178
+ else
179
+ { enum: values } if values
180
+ end
181
+ end
606
182
 
607
- base_path = @@documentation_class.parse_base_path(options[:base_path], request)
608
- api_description[:basePath] = base_path if base_path && base_path.size > 0 && root_base_path != false
609
- api_description[:models] = @@documentation_class.parse_entity_models(models) unless models.empty?
610
- api_description[:authorizations] = authorizations if authorizations
183
+ def parse_range_values(values)
184
+ { minimum: values.first, maximum: values.last }
185
+ end
611
186
 
612
- api_description
613
- end
614
- end
615
- end
187
+ def create_documentation_class
188
+ Class.new(Grape::API) do
189
+ extend GrapeSwagger::DocMethods
616
190
  end
617
191
  end
618
192
  end
@@ -14,7 +14,7 @@ describe 'API Description' do
14
14
  expect(routes.first.route_description).to eq 'Swagger compatible API description'
15
15
  expect(routes.first.route_params).to eq({})
16
16
  expect(routes.last.route_description).to eq 'Swagger compatible API description for specific API'
17
- expect(routes.last.route_params).to eq('name' => { desc: 'Resource name of mounted API', type: 'string', required: true })
17
+ expect(routes.last.route_params).to eq('name' => { desc: 'Resource name of mounted API', type: 'String', required: true })
18
18
  end
19
19
  end
20
20
 
@@ -34,7 +34,7 @@ describe 'API Description' do
34
34
  expect(routes.first.route_params).to eq(x: 1)
35
35
  expect(routes.first.route_xx).to eq(11)
36
36
  expect(routes.last.route_description).to eq 'Second'
37
- expect(routes.last.route_params).to eq('name' => { desc: 'Resource name of mounted API', type: 'string', required: true }, y: 42)
37
+ expect(routes.last.route_params).to eq('name' => { desc: 'Resource name of mounted API', type: 'String', required: true }, y: 42)
38
38
  expect(routes.last.route_yy).to eq(4242)
39
39
  end
40
40
  end