grape-swagger 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +1 -1
  4. data/CHANGELOG.md +24 -2
  5. data/Gemfile +5 -0
  6. data/README.md +105 -3
  7. data/example/api/endpoints.rb +16 -16
  8. data/lib/grape-swagger.rb +0 -1
  9. data/lib/grape-swagger/doc_methods.rb +1 -0
  10. data/lib/grape-swagger/doc_methods/data_type.rb +15 -3
  11. data/lib/grape-swagger/doc_methods/extensions.rb +16 -12
  12. data/lib/grape-swagger/doc_methods/move_params.rb +139 -94
  13. data/lib/grape-swagger/doc_methods/parse_params.rb +9 -7
  14. data/lib/grape-swagger/endpoint.rb +36 -17
  15. data/lib/grape-swagger/version.rb +1 -1
  16. data/spec/lib/data_type_spec.rb +24 -0
  17. data/spec/lib/endpoint_spec.rb +13 -0
  18. data/spec/lib/move_params_spec.rb +124 -116
  19. data/spec/support/model_parsers/entity_parser.rb +8 -2
  20. data/spec/support/model_parsers/mock_parser.rb +10 -0
  21. data/spec/support/model_parsers/representable_parser.rb +7 -0
  22. data/spec/support/the_paths_definitions.rb +1 -2
  23. data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +2 -1
  24. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +1 -1
  25. data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +22 -1
  26. data/spec/swagger_v2/api_swagger_v2_global_configuration_spec.rb +4 -1
  27. data/spec/swagger_v2/api_swagger_v2_hash_and_array_spec.rb +60 -0
  28. data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +14 -9
  29. data/spec/swagger_v2/api_swagger_v2_hide_param_spec.rb +90 -0
  30. data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +185 -110
  31. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +11 -13
  32. data/spec/swagger_v2/api_swagger_v2_response_spec.rb +5 -5
  33. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +19 -18
  34. data/spec/swagger_v2/namespaced_api_spec.rb +20 -0
  35. data/spec/swagger_v2/param_multi_type_spec.rb +73 -0
  36. data/spec/swagger_v2/param_type_spec.rb +54 -27
  37. data/spec/swagger_v2/params_array_spec.rb +96 -6
  38. data/spec/swagger_v2/params_nested_spec.rb +2 -2
  39. metadata +9 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21b83f7c1596856df6020d2a6191b69a2a9502af
4
- data.tar.gz: d3f9532f1a8d5430baa061237d28b8c5fa8fced5
3
+ metadata.gz: 96f5daf8e980cca96f1dc18f0a7dad2faf86b1f3
4
+ data.tar.gz: 665c50fb9409756ff95f526b2a13600a37db8a40
5
5
  SHA512:
6
- metadata.gz: a48aa84a04dcb5b0a6c12fb142e0fefbb92a42f8c363a9a0b34cf3931ea57a60996f2991b5f00e0d5b1154dd4f85b655d16f634101c6913e2cdbd0867ba7d735
7
- data.tar.gz: 705b3490be3a33bf84ec5bd56df3d7ea9b698b0b2a79eef822facebb22554933fa1ff2a00e462e36f4df8c8af30d0c06f0c11eae9c4fe8c131eecf22e5fb05eb
6
+ metadata.gz: f4e73ba96c6060cd24e22733de9160f267d72fdd9bc73f8baf76ecb6fe31ac908dab1506171ea6a9384631140ea338768fe7452513c6fbc9eb4a81ae70bc4819
7
+ data.tar.gz: 6d1a9f3df56f08ade9c36f43573a669b9a3a3c1172218cd02dc885dcee3e99897500249bcc5340b6cb4bb3dabafd098a1c529bd1ebf9e07eb50a212630409bb0
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  Exclude:
3
3
  - vendor/**/*
4
+ - example/**/*
4
5
 
5
6
  inherit_from: .rubocop_todo.yml
data/.rubocop_todo.yml CHANGED
@@ -28,7 +28,7 @@ Metrics/AbcSize:
28
28
  # Offense count: 3
29
29
  # Configuration parameters: CountComments.
30
30
  Metrics/ClassLength:
31
- Max: 206
31
+ Max: 220
32
32
 
33
33
  # Offense count: 10
34
34
  Metrics/CyclomaticComplexity:
data/CHANGELOG.md CHANGED
@@ -8,6 +8,30 @@
8
8
 
9
9
  * Your contribution here.
10
10
 
11
+ ### 0.22.0 (July 12, 2016)
12
+
13
+ #### Features
14
+
15
+ * [#470](https://github.com/ruby-grape/grape-swagger/pull/470): Document request definitions inline - [@LeFnord](https://github.com/LeFnord).
16
+ * [#448](https://github.com/ruby-grape/grape-swagger/pull/448): Header parameters are now prepended to the parameter list - [@anakinj](https://github.com/anakinj).
17
+ * [#444](https://github.com/ruby-grape/grape-swagger/pull/444): With multi types parameter the first type is use as the documentation type - [@scauglog](https://github.com/scauglog).
18
+ * [#463](https://github.com/ruby-grape/grape-swagger/pull/463): Added 'hidden' option for parameter to be exclude from generated documentation - [@anakinj](https://github.com/anakinj).
19
+ * [#471](https://github.com/ruby-grape/grape-swagger/pull/471): Allow Security Definitions Objects to be defined - [@bendodd](https://github.com/bendodd).
20
+
21
+ #### Fixes
22
+
23
+ * [#472](https://github.com/ruby-grape/grape-swagger/pull/472): Fixes required property for request definitions - [@LeFnord](https://github.com/LeFnord).
24
+ * [#467](https://github.com/ruby-grape/grape-swagger/pull/467): Refactors moving of body params - [@LeFnord](https://github.com/LeFnord).
25
+ * [#464](https://github.com/ruby-grape/grape-swagger/pull/464): Fixes array params, sets correct type and format for items - [@LeFnord](https://github.com/LeFnord).
26
+ * [#461](https://github.com/ruby-grape/grape-swagger/pull/461): Fixes issue by adding extensions to definitions. It appeared, if for the given status code, no definition could be found - [@LeFnord](https://github.com/LeFnord).
27
+ * [#455](https://github.com/ruby-grape/grape-swagger/pull/455): Setting `type:` option as `Array[Class]` creates `array` type in JSON - [@tyspring](https://github.com/tyspring).
28
+ * [#450](https://github.com/ruby-grape/grape-swagger/pull/438): Do not add :description to definitions if :description is missing on path - [@texpert](https://github.com/texpert).
29
+ * [#447](https://github.com/ruby-grape/grape-swagger/pull/447): Version part of the url is now ignored when generating tags for endpoint - [@anakinj](https://github.com/anakinj).
30
+ * [#444](https://github.com/ruby-grape/grape-swagger//pull/444): Default value provided in the documentation hash, override the grape default [@scauglog](https://github.com/scauglog).
31
+ * [#443](https://github.com/ruby-grape/grape-swagger/issues/443): Type provided in the documentation hash, override the grape type [@scauglog](https://github.com/scauglog).
32
+ * [#454](https://github.com/ruby-grape/grape-swagger/pull/454): Include documented Hashes in documentation output - [@aschuster3](https://github.com/aschuster3).
33
+ * [#457](https://github.com/ruby-grape/grape-swagger/issues/457): Using camel case on namespace throws exception on add_swagger_documentation method - [@rayko](https://github.com/rayko/).
34
+
11
35
  ### 0.21.0 (June 1, 2016)
12
36
 
13
37
  #### Features
@@ -15,7 +39,6 @@
15
39
  * [#413](https://github.com/ruby-grape/grape-swagger/pull/413): Move all model parsing logic to separate gems `grape-swagger-entity` and added representable parser `grape-swagger` - [@Bugagazavr](https://github.com/Bugagazavr).
16
40
  * [#434](https://github.com/ruby-grape/grape-swagger/pull/434): Add summary to the operation object generator to be more compliant with [OpenAPI v2](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object) - [@aschuster3](https://github.com/aschuster3).
17
41
  * [#441](https://github.com/ruby-grape/grape-swagger/pull/441): Accepting `String`, `lambda` and `proc` for `host` and `base_path` - [@LeFnord](https://github.com/LeFnord).
18
- * Your contribution here.
19
42
 
20
43
  #### Fixes
21
44
 
@@ -23,7 +46,6 @@
23
46
  * [#419](https://github.com/ruby-grape/grape-swagger/pull/419): Replaced github ref to rubygems for external gems - [@Bugagazavr](https://github.com/Bugagazavr).
24
47
  * [#420](https://github.com/ruby-grape/grape-swagger/pull/420): Raise SwaggerSpec exception if swagger spec isn't satisfied, when no parser for model is registered or response model is empty - [@Bugagazavr](https://github.com/Bugagazavr).
25
48
  * [#438](https://github.com/ruby-grape/grape-swagger/pull/438): Route version was missing in :options passed to PathString, so Endpoint.path_and_definitions_objects wasn't returning a versioned path when required - [@texpert](https://github.com/texpert).
26
- * Your contribution here.
27
49
 
28
50
  ### 0.20.3 (May 9, 2016)
29
51
 
data/Gemfile CHANGED
@@ -10,3 +10,8 @@ else
10
10
  end
11
11
 
12
12
  gem ENV['MODEL_PARSER'] if ENV.key?('MODEL_PARSER')
13
+
14
+ if RUBY_VERSION < '2.2.2'
15
+ gem 'rack', '<2.0.0'
16
+ gem 'activesupport', '<5.0.0'
17
+ end
data/README.md CHANGED
@@ -49,7 +49,8 @@ grape-swagger | swagger spec | grape | grape-entity | represen
49
49
  0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0 | n/a |
50
50
  0.20.1 | 2.0 | >= 0.12.0 ... <= 0.14.0 | <= 0.5.1 | n/a |
51
51
  0.20.3 | 2.0 | >= 0.12.0 ... ~> 0.16.2 | ~> 0.5.1 | n/a |
52
- 0.21.0 (next) | 2.0 | >= 0.12.0 ... <= 0.16.2 | <= 0.5.1 | >= 2.4.1 |
52
+ 0.21.0 | 2.0 | >= 0.12.0 ... <= 0.16.2 | <= 0.5.1 | >= 2.4.1 |
53
+ 0.21.1 (next) | 2.0 | >= 0.12.0 ... <= 0.16.2 | <= 0.5.1 | >= 2.4.1 |
53
54
 
54
55
  <a name="swagger-spec" />
55
56
  ## Swagger-Spec
@@ -191,6 +192,7 @@ end
191
192
  * [add_version](#add_version)
192
193
  * [doc_version](#doc_version)
193
194
  * [markdown](#markdown)
195
+ * [security_definitions](#security_definitions)
194
196
  * [models](#models)
195
197
  * [hide_documentation_path](#hide_documentation_path)
196
198
  * [info](#info)
@@ -272,12 +274,24 @@ add_swagger_documentation \
272
274
  markdown: GrapeSwagger::Markdown::RedcarpetAdapter.new
273
275
  ```
274
276
 
277
+ <a name="security_definitions" />
278
+ #### security_definitions:
279
+ Specify the [Security Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object)
280
+
281
+ ```ruby
282
+ add_swagger_documentation \
283
+ security_definitions: {
284
+ api_key: {
285
+ type: "apiKey",
286
+ name: "api_key",
287
+ in: "header"
288
+ }
289
+ }
290
+ ```
275
291
 
276
292
  #### *authorizations*:
277
293
  This value is added to the `authorizations` key in the JSON documentation.
278
294
 
279
-
280
-
281
295
  <a name="models" />
282
296
  #### models:
283
297
  A list of entities to document. Combine with the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
@@ -343,9 +357,14 @@ add_swagger_documentation \
343
357
 
344
358
  * [Swagger Header Parameters](#headers)
345
359
  * [Hiding an Endpoint](#hiding)
360
+ * [Overriding Auto-Generated Nicknames](#overriding-auto-generated-nicknames)
346
361
  * [Defining an endpoint as array](#array)
347
362
  * [Using an options hash](#options)
348
363
  * [Specify endpoint details](#details)
364
+ * [Overriding param type](#overriding-param-type)
365
+ * [Overriding type](#overriding-type)
366
+ * [Multi types](#multi-types)
367
+ * [Hiding parameters](#hiding-parameters)
349
368
  * [Response documentation](#response)
350
369
 
351
370
 
@@ -453,6 +472,89 @@ post :act do
453
472
  end
454
473
  ```
455
474
 
475
+ #### Overriding type
476
+
477
+ You can override type, using the documentation hash.
478
+
479
+ ```ruby
480
+ params do
481
+ requires :input, type: String, documentation: { type: 'integer' }
482
+ end
483
+ post :act do
484
+ ...
485
+ end
486
+ ```
487
+
488
+ ```json
489
+ {
490
+ "in": "formData",
491
+ "name": "input",
492
+ "type": "integer",
493
+ "format": "int32",
494
+ "required": true
495
+ }
496
+ ```
497
+
498
+ #### Array type
499
+
500
+ Array types are also supported.
501
+
502
+ ```ruby
503
+ params do
504
+ requires :action_ids, type: Array[Integer]
505
+ end
506
+ post :act do
507
+ ...
508
+ end
509
+ ```
510
+
511
+ ```json
512
+ {
513
+ "in": "formData",
514
+ "name": "action_ids",
515
+ "type": "array",
516
+ "items": {
517
+ "type": "integer"
518
+ },
519
+ "required": true
520
+ }
521
+ ```
522
+
523
+ #### Multi types
524
+
525
+ By default when you set multi types, the first type is selected as swagger type
526
+
527
+ ```ruby
528
+ params do
529
+ requires :action, types: [String, Integer]
530
+ end
531
+ post :act do
532
+ ...
533
+ end
534
+ ```
535
+
536
+ ```json
537
+ {
538
+ "in": "formData",
539
+ "name": "action",
540
+ "type": "string",
541
+ "required": true
542
+ }
543
+ ```
544
+
545
+ #### Hiding parameters
546
+
547
+ Exclude single optional parameter from the documentation
548
+
549
+ ```ruby
550
+ params do
551
+ optional :one, documentation: { hidden: true }
552
+ optional :two, documentation: { hidden: -> { true } }
553
+ end
554
+ post :act do
555
+ ...
556
+ end
557
+ ```
456
558
 
457
559
  #### Overriding the route summary
458
560
 
@@ -23,21 +23,21 @@ module Api
23
23
  namespace :splines do
24
24
  #
25
25
  desc 'Get all splines',
26
- is_array: true,
27
- http_codes: [
28
- { code: 200, message: 'get Splines', model: Api::Entities::Splines },
29
- { code: 422, message: 'SplinesOutError' }
30
- ]
26
+ is_array: true,
27
+ http_codes: [
28
+ { code: 200, message: 'get Splines', model: Api::Entities::Splines },
29
+ { code: 422, message: 'SplinesOutError' }
30
+ ]
31
31
  get do
32
32
  present :items, @@splines, with: Entities::Splines
33
33
  end
34
34
 
35
35
  #
36
36
  desc 'Return a spline.',
37
- http_codes: [
38
- { code: 200, message: 'get Splines' },
39
- { code: 422, message: 'SplinesOutError' }
40
- ]
37
+ http_codes: [
38
+ { code: 200, message: 'get Splines' },
39
+ { code: 422, message: 'SplinesOutError' }
40
+ ]
41
41
  params do
42
42
  requires :id, type: Integer, desc: 'Spline id.'
43
43
  end
@@ -49,9 +49,9 @@ module Api
49
49
 
50
50
  #
51
51
  desc 'Create a spline.',
52
- http_codes: [
53
- { code: 201, message: 'Spline created', model: Api::Entities::Splines }
54
- ]
52
+ http_codes: [
53
+ { code: 201, message: 'Spline created', model: Api::Entities::Splines }
54
+ ]
55
55
  params do
56
56
  requires :spline, type: Hash do
57
57
  requires :x, type: Numeric
@@ -73,10 +73,10 @@ module Api
73
73
 
74
74
  #
75
75
  desc 'Update a spline.',
76
- http_codes: [
77
- { code: 200, message: 'update Splines', model: Api::Entities::Splines },
78
- { code: 422, message: 'SplinesOutError' }
79
- ]
76
+ http_codes: [
77
+ { code: 200, message: 'update Splines', model: Api::Entities::Splines },
78
+ { code: 422, message: 'SplinesOutError' }
79
+ ]
80
80
  params do
81
81
  requires :id, type: Integer, desc: 'Spline id.'
82
82
  optional :spline, type: Hash do
data/lib/grape-swagger.rb CHANGED
@@ -65,7 +65,6 @@ module Grape
65
65
  next unless route_match
66
66
  resource = route_match.captures.first
67
67
  next if resource.empty?
68
- resource.downcase!
69
68
  @target_class.combined_routes[resource] ||= []
70
69
  next if doc_klass.hide_documentation_path && route.path.match(/#{doc_klass.mount_path}($|\/|\(\.)/)
71
70
  @target_class.combined_routes[resource] << route
@@ -102,6 +102,7 @@ module GrapeSwagger
102
102
  hide_documentation_path: true,
103
103
  format: :json,
104
104
  authorizations: nil,
105
+ security_definitions: nil,
105
106
  api_documentation: { desc: 'Swagger compatible API description' },
106
107
  specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
107
108
  }
@@ -3,9 +3,10 @@ module GrapeSwagger
3
3
  class DataType
4
4
  class << self
5
5
  def call(value)
6
- raw_data_type = value[:type] if value.is_a?(Hash)
7
- raw_data_type = value unless value.is_a?(Hash)
8
- raw_data_type ||= 'string'
6
+ raw_data_type = value.is_a?(Hash) ? value[:type] : value
7
+ raw_data_type ||= 'String'
8
+ raw_data_type = parse_multi_type(raw_data_type)
9
+
9
10
  case raw_data_type.to_s
10
11
  when 'Boolean', 'Date', 'Integer', 'String', 'Float', 'JSON', 'Array'
11
12
  raw_data_type.to_s.downcase
@@ -28,6 +29,17 @@ module GrapeSwagger
28
29
  end
29
30
  end
30
31
 
32
+ def parse_multi_type(raw_data_type)
33
+ case raw_data_type
34
+ when /\A\[.*\]\z/
35
+ raw_data_type.gsub(/[(\A\[)(\s+)(\]\z)]/, '').split(',').first
36
+ when Array
37
+ raw_data_type.first
38
+ else
39
+ raw_data_type
40
+ end
41
+ end
42
+
31
43
  def parse_entity_name(model)
32
44
  if model.respond_to?(:entity_name)
33
45
  model.entity_name
@@ -4,6 +4,7 @@ module GrapeSwagger
4
4
  class << self
5
5
  def add(path, definitions, route)
6
6
  @route = route
7
+
7
8
  description = route.settings[:description]
8
9
  add_extension_to(path[method], extension(description)) if description && extended?(description, :x)
9
10
 
@@ -20,29 +21,32 @@ module GrapeSwagger
20
21
  def_extension = extension(settings, :x_def)
21
22
 
22
23
  if def_extension[:x_def].is_a?(Array)
23
- def_extension[:x_def].each do |extension|
24
- next unless extension.key?(:for)
25
- status = extension.delete(:for)
26
- definition = find_definition(status, path)
27
- add_extension_to(definitions[definition], x_def: extension)
28
- end
24
+ def_extension[:x_def].each { |extension| setup_definition(extension, path, definitions) }
29
25
  else
30
- return unless def_extension[:x_def].key?(:for)
31
- status = def_extension[:x_def].delete(:for)
32
- definition = find_definition(status, path)
33
- add_extension_to(definitions[definition], def_extension)
26
+ setup_definition(def_extension[:x_def], path, definitions)
34
27
  end
35
28
  end
36
29
 
30
+ def setup_definition(def_extension, path, definitions)
31
+ return unless def_extension.key?(:for)
32
+ status = def_extension[:for]
33
+
34
+ definition = find_definition(status, path)
35
+ add_extension_to(definitions[definition], x_def: def_extension)
36
+ end
37
+
37
38
  def find_definition(status, path)
38
39
  response = path[method][:responses][status]
40
+ return if response.nil?
39
41
 
40
- response[:schema]['$ref'].split('/').last
42
+ return response[:schema]['$ref'].split('/').last if response[:schema].key?('$ref')
43
+ return response[:schema]['items']['$ref'].split('/').last if response[:schema].key?('items')
41
44
  end
42
45
 
43
46
  def add_extension_to(part, extensions)
47
+ return if part.nil?
44
48
  concatenate(extensions).each do |key, value|
45
- part[key] = value
49
+ part[key] = value unless key.start_with?('x-for')
46
50
  end
47
51
  end
48
52
 
@@ -2,159 +2,204 @@ module GrapeSwagger
2
2
  module DocMethods
3
3
  class MoveParams
4
4
  class << self
5
- def to_definition(paths, definitions)
5
+ attr_accessor :definitions
6
+
7
+ def can_be_moved?(params, http_verb)
8
+ move_methods.include?(http_verb) && includes_body_param?(params)
9
+ end
10
+
11
+ def to_definition(params, route, definitions)
6
12
  @definitions = definitions
13
+ unify!(params)
7
14
 
8
- find_post_put(paths) do |method_definition|
9
- verb = method_definition.keys.first
10
- method_object = method_definition[verb]
15
+ params_to_move = movable_params(params)
16
+ params << parent_definition_of_params(params_to_move, route)
11
17
 
12
- find_definition_and_params(method_object, verb)
13
- end
18
+ params
14
19
  end
15
20
 
16
- def find_post_put(paths)
17
- paths.each do |x|
18
- found = x.last.select { |y| move_methods.include?(y) }
19
- yield found unless found.empty?
21
+ private
22
+
23
+ def parent_definition_of_params(params, route)
24
+ definition_name = GrapeSwagger::DocMethods::OperationId.manipulate(parse_model(route.path))
25
+ referenced_definition = build_definition(definition_name, params, route.request_method.downcase)
26
+ definition = @definitions[referenced_definition]
27
+
28
+ move_params_to_new(definition, params)
29
+
30
+ definition[:description] = route.description if route.respond_to?(:description)
31
+
32
+ build_body_parameter(referenced_definition, definition_name)
33
+ end
34
+
35
+ def move_params_to_new(definition, params)
36
+ params, nested_params = params.partition { |x| !x[:name].include?('[') }
37
+
38
+ unless params.blank?
39
+ properties, required = build_properties(params)
40
+ add_properties_to_definition(definition, properties, required)
20
41
  end
42
+
43
+ nested_properties = build_nested_properties(nested_params) unless nested_params.blank?
44
+ add_properties_to_definition(definition, nested_properties, []) unless nested_params.blank?
21
45
  end
22
46
 
23
- def find_definition_and_params(path, verb)
24
- params = path[:parameters]
47
+ def build_nested_properties(params, properties = {})
48
+ property = params.bsearch { |x| x[:name].include?('[') }[:name].split('[').first
25
49
 
26
- return if params.nil?
27
- return unless should_move?(params)
50
+ nested_params, params = params.partition { |x| x[:name].start_with?("#{property}[") }
51
+ prepare_nested_names(property, nested_params)
28
52
 
29
- unify!(params)
53
+ recursive_call(properties, property, nested_params) unless nested_params.empty?
54
+ build_nested_properties(params, properties) unless params.empty?
30
55
 
31
- status_code = GrapeSwagger::DocMethods::StatusCodes.get[verb.to_sym][:code]
32
- response = path[:responses][status_code]
56
+ properties
57
+ end
33
58
 
34
- if response[:schema] && response[:schema]['$ref']
35
- referenced_definition = parse_model(response[:schema]['$ref'])
36
- name = build_definition(referenced_definition, verb)
59
+ def recursive_call(properties, property, nested_params)
60
+ if should_expose_as_array?(nested_params)
61
+ properties[property] = array_type
62
+ move_params_to_new(properties[property][:items], nested_params)
37
63
  else
38
- referenced_definition = path[:operationId]
39
- name = build_definition(referenced_definition)
64
+ properties[property] = object_type
65
+ move_params_to_new(properties[property], nested_params)
40
66
  end
67
+ end
41
68
 
42
- move_params_to_new(name, params)
69
+ def movable_params(params)
70
+ to_delete = params.each_with_object([]) { |x, memo| memo << x if deletable?(x) }
71
+ delete_from(params, to_delete)
43
72
 
44
- @definitions[name][:description] = path[:description]
45
- path[:parameters] << build_body_parameter(response.dup, name)
73
+ to_delete
46
74
  end
47
75
 
48
- def move_params_to_new(name, params)
49
- properties = {}
50
- definition = @definitions[name]
76
+ def delete_from(params, to_delete)
77
+ to_delete.each { |x| params.delete(x) }
78
+ end
51
79
 
52
- nested_definitions(name, params, properties)
80
+ def add_properties_to_definition(definition, properties, required)
81
+ if definition.key?(:items)
82
+ definition[:items][:properties].merge!(properties)
83
+ add_to_required(definition[:items], required)
84
+ else
85
+ definition[:properties].merge!(properties)
86
+ add_to_required(definition, required)
87
+ end
88
+ end
53
89
 
54
- params.dup.each do |param|
55
- next unless movable?(param)
90
+ def add_to_required(definition, value)
91
+ return if value.blank?
56
92
 
57
- name = param[:name].to_sym
58
- properties[name] = {}
59
-
60
- properties[name].tap do |x|
61
- property_keys.each do |attribute|
62
- x[attribute] = param[attribute] unless param[attribute].nil?
63
- end
64
- end
93
+ definition[:required] ||= []
94
+ definition[:required].push(*value)
95
+ end
65
96
 
66
- properties[name][:readOnly] = true unless deletable?(param)
67
- params.delete(param) if deletable?(param)
97
+ def build_properties(params)
98
+ properties = {}
99
+ required = []
68
100
 
69
- definition[:required] << name if deletable?(param) && param[:required]
70
- end
101
+ prepare_nested_types(params) if should_expose_as_array?(params)
71
102
 
72
- definition.delete(:required) if definition[:required].empty?
73
- definition[:properties] = properties
74
- end
103
+ params.each do |param|
104
+ name = param[:name].to_sym
105
+ properties[name] = {}
75
106
 
76
- def nested_definitions(name, params, properties)
77
- loop do
78
- nested_name = params.bsearch { |x| x[:name].include?('[') }
79
- return if nested_name.nil?
107
+ if should_expose_as_array?([param])
108
+ prepare_nested_types([param])
80
109
 
81
- nested_name = nested_name[:name].split('[').first
110
+ properties[name][:type] = 'array'
111
+ properties[name][:items] = {}
112
+ properties[name][:items].tap do |x|
113
+ property_keys.each do |attribute|
114
+ x[attribute] = param[attribute] unless param[attribute].nil?
115
+ end
116
+ end
117
+ else
82
118
 
83
- nested, = params.partition { |x| x[:name].start_with?("#{nested_name}[") }
84
- nested.each { |x| params.delete(x) }
85
- nested_def_name = GrapeSwagger::DocMethods::OperationId.manipulate(nested_name)
86
- def_name = "#{name}#{nested_def_name}"
87
- properties[nested_name] = { '$ref' => "#/definitions/#{def_name}" }
119
+ properties[name].tap do |x|
120
+ property_keys.each do |attribute|
121
+ x[attribute] = param[attribute] unless param[attribute].nil?
122
+ end
123
+ end
124
+ end
88
125
 
89
- prepare_nested_names(nested)
90
- build_definition(def_name)
91
- @definitions[def_name][:description] = "#{name} - #{nested_name}"
92
- move_params_to_new(def_name, nested)
126
+ required << name if deletable?(param) && param[:required]
93
127
  end
94
- end
95
128
 
96
- private
129
+ [properties, required]
130
+ end
97
131
 
98
- def build_body_parameter(response, name = false)
99
- entity = response[:schema] ? parse_model(response[:schema]['$ref']) : name
100
- body_param = {}
101
- body_param.tap do |x|
102
- x[:name] = entity
132
+ def build_body_parameter(reference, name)
133
+ {}.tap do |x|
134
+ x[:name] = name
103
135
  x[:in] = 'body'
104
136
  x[:required] = true
105
- x[:schema] = { '$ref' => response[:schema]['$ref'] } unless name
106
- x[:schema] = { '$ref' => "#/definitions/#{name}" } if name
137
+ x[:schema] = { '$ref' => "#/definitions/#{reference}" }
107
138
  end
108
139
  end
109
140
 
110
- def build_definition(name, verb = nil)
111
- name = "#{verb}Request#{name}" if verb
112
- @definitions[name] = { type: 'object', properties: {}, required: [] }
141
+ def build_definition(name, params, verb = nil)
142
+ name = "#{verb}#{name}" if verb
143
+ @definitions[name] = should_expose_as_array?(params) ? array_type : object_type
113
144
 
114
145
  name
115
146
  end
116
147
 
117
- def prepare_nested_names(params)
118
- params.each do |param|
119
- param.tap do |x|
120
- name = x[:name].partition('[').last.sub(']', '')
121
- name = name.partition('[').last.sub(']', '') if name.start_with?('[')
122
- x[:name] = name
123
- end
124
- end
148
+ def array_type
149
+ { type: 'array', items: { type: 'object', properties: {} } }
125
150
  end
126
151
 
127
- def unify!(params)
152
+ def object_type
153
+ { type: 'object', properties: {} }
154
+ end
155
+
156
+ def prepare_nested_types(params)
128
157
  params.each do |param|
129
- param[:in] = param.delete(:param_type) if param.key?(:param_type)
130
- param[:in] = 'body' if param[:in] == 'formData'
158
+ next unless param[:items]
159
+ param[:type] = param[:items][:type] == 'array' ? 'string' : param[:items][:type]
160
+ param[:format] = param[:items][:format] if param[:items][:format]
161
+ param.delete(:items)
131
162
  end
132
163
  end
133
164
 
134
- def parse_model(ref)
135
- ref.split('/').last
165
+ def prepare_nested_names(property, params)
166
+ params.each { |x| x[:name] = x[:name].sub(property, '').sub('[', '').sub(']', '') }
136
167
  end
137
168
 
138
- def move_methods
139
- [:post, :put, :patch]
169
+ def unify!(params)
170
+ params.each { |x| x[:in] = x.delete(:param_type) if x[:param_type] }
171
+ params.each { |x| x[:in] = 'body' if x[:in] == 'formData' } if includes_body_param?(params)
172
+ end
173
+
174
+ def parse_model(ref)
175
+ parts = ref.split('/')
176
+ parts.last.include?('{') ? parts[0..-2].join('/') : parts[0..-1].join('/')
140
177
  end
141
178
 
142
179
  def property_keys
143
180
  [:type, :format, :description, :minimum, :maximum, :items]
144
181
  end
145
182
 
146
- def movable?(param)
147
- return true if param[:in] == 'body' || param[:in] == 'path'
148
- false
183
+ def deletable?(param)
184
+ param[:in] == 'body'
149
185
  end
150
186
 
151
- def deletable?(param)
152
- return true if movable?(param) && param[:in] == 'body'
187
+ def move_methods
188
+ [:post, :put, :patch, 'POST', 'PUT', 'PATCH']
189
+ end
190
+
191
+ def includes_body_param?(params)
192
+ params.map { |x| return true if x[:in] == 'body' || x[:param_type] == 'body' }
153
193
  false
154
194
  end
155
195
 
156
- def should_move?(params)
157
- !params.select { |x| x[:in] == 'body' || x[:param_type] == 'body' }.empty?
196
+ def should_expose_as_array?(params)
197
+ should_exposed_as(params) == 'array'
198
+ end
199
+
200
+ def should_exposed_as(params)
201
+ params.map { |x| return 'object' if x[:type] && x[:type] != 'array' }
202
+ 'array'
158
203
  end
159
204
  end
160
205
  end