grape-swagger 0.21.0 → 0.22.0

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.
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