apipierails3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -0
  5. data/APACHE-LICENSE-2.0 +202 -0
  6. data/CHANGELOG.md +469 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.rails32 +6 -0
  9. data/Gemfile.rails41 +6 -0
  10. data/Gemfile.rails42 +11 -0
  11. data/Gemfile.rails50 +6 -0
  12. data/Gemfile.rails51 +7 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.rst +1874 -0
  17. data/Rakefile +13 -0
  18. data/apipierails3.gemspec +27 -0
  19. data/app/controllers/apipie/apipies_controller.rb +199 -0
  20. data/app/helpers/apipie_helper.rb +10 -0
  21. data/app/public/apipie/javascripts/apipie.js +6 -0
  22. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
  25. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  26. data/app/public/apipie/stylesheets/application.css +7 -0
  27. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  31. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  32. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  33. data/app/views/apipie/apipies/_languages.erb +6 -0
  34. data/app/views/apipie/apipies/_metadata.erb +1 -0
  35. data/app/views/apipie/apipies/_method_detail.erb +61 -0
  36. data/app/views/apipie/apipies/_params.html.erb +42 -0
  37. data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
  38. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  39. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  40. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  41. data/app/views/apipie/apipies/index.html.erb +56 -0
  42. data/app/views/apipie/apipies/method.html.erb +41 -0
  43. data/app/views/apipie/apipies/plain.html.erb +77 -0
  44. data/app/views/apipie/apipies/resource.html.erb +80 -0
  45. data/app/views/apipie/apipies/static.html.erb +103 -0
  46. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  47. data/config/locales/de.yml +28 -0
  48. data/config/locales/en.yml +32 -0
  49. data/config/locales/es.yml +28 -0
  50. data/config/locales/fr.yml +31 -0
  51. data/config/locales/it.yml +31 -0
  52. data/config/locales/ja.yml +31 -0
  53. data/config/locales/pl.yml +28 -0
  54. data/config/locales/pt-BR.yml +28 -0
  55. data/config/locales/ru.yml +28 -0
  56. data/config/locales/tr.yml +28 -0
  57. data/config/locales/zh-CN.yml +28 -0
  58. data/config/locales/zh-TW.yml +28 -0
  59. data/images/screenshot-1.png +0 -0
  60. data/images/screenshot-2.png +0 -0
  61. data/lib/apipie/apipie_module.rb +83 -0
  62. data/lib/apipie/application.rb +462 -0
  63. data/lib/apipie/configuration.rb +186 -0
  64. data/lib/apipie/dsl_definition.rb +607 -0
  65. data/lib/apipie/error_description.rb +44 -0
  66. data/lib/apipie/errors.rb +86 -0
  67. data/lib/apipie/extractor.rb +177 -0
  68. data/lib/apipie/extractor/collector.rb +117 -0
  69. data/lib/apipie/extractor/recorder.rb +166 -0
  70. data/lib/apipie/extractor/writer.rb +454 -0
  71. data/lib/apipie/helpers.rb +73 -0
  72. data/lib/apipie/markup.rb +48 -0
  73. data/lib/apipie/method_description.rb +273 -0
  74. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  75. data/lib/apipie/param_description.rb +280 -0
  76. data/lib/apipie/railtie.rb +9 -0
  77. data/lib/apipie/resource_description.rb +124 -0
  78. data/lib/apipie/response_description.rb +131 -0
  79. data/lib/apipie/response_description_adapter.rb +200 -0
  80. data/lib/apipie/routes_formatter.rb +33 -0
  81. data/lib/apipie/routing.rb +16 -0
  82. data/lib/apipie/rspec/response_validation_helper.rb +192 -0
  83. data/lib/apipie/see_description.rb +39 -0
  84. data/lib/apipie/static_dispatcher.rb +69 -0
  85. data/lib/apipie/swagger_generator.rb +707 -0
  86. data/lib/apipie/tag_list_description.rb +11 -0
  87. data/lib/apipie/validator.rb +526 -0
  88. data/lib/apipie/version.rb +3 -0
  89. data/lib/apipierails3.rb +25 -0
  90. data/lib/generators/apipie/install/README +6 -0
  91. data/lib/generators/apipie/install/install_generator.rb +25 -0
  92. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  93. data/lib/generators/apipie/views_generator.rb +11 -0
  94. data/lib/tasks/apipie.rake +345 -0
  95. data/rel-eng/packages/.readme +3 -0
  96. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  97. data/rel-eng/tito.props +5 -0
  98. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  99. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  100. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
  101. data/spec/controllers/apipies_controller_spec.rb +273 -0
  102. data/spec/controllers/concerns_controller_spec.rb +42 -0
  103. data/spec/controllers/extended_controller_spec.rb +11 -0
  104. data/spec/controllers/users_controller_spec.rb +740 -0
  105. data/spec/dummy/Rakefile +7 -0
  106. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  107. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  108. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  109. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
  110. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  111. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  112. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  114. data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
  115. data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
  116. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  117. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  118. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  119. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  120. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  121. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  122. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  123. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  124. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  125. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  126. data/spec/dummy/app/controllers/users_controller.rb +297 -0
  127. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  128. data/spec/dummy/config.ru +4 -0
  129. data/spec/dummy/config/application.rb +49 -0
  130. data/spec/dummy/config/boot.rb +10 -0
  131. data/spec/dummy/config/database.yml +21 -0
  132. data/spec/dummy/config/environment.rb +8 -0
  133. data/spec/dummy/config/environments/development.rb +28 -0
  134. data/spec/dummy/config/environments/production.rb +52 -0
  135. data/spec/dummy/config/environments/test.rb +38 -0
  136. data/spec/dummy/config/initializers/apipie.rb +110 -0
  137. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/spec/dummy/config/initializers/inflections.rb +10 -0
  139. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  140. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  141. data/spec/dummy/config/initializers/session_store.rb +8 -0
  142. data/spec/dummy/config/locales/en.yml +5 -0
  143. data/spec/dummy/config/routes.rb +51 -0
  144. data/spec/dummy/db/.gitkeep +0 -0
  145. data/spec/dummy/doc/apipie_examples.json +1 -0
  146. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  147. data/spec/dummy/public/404.html +26 -0
  148. data/spec/dummy/public/422.html +26 -0
  149. data/spec/dummy/public/500.html +26 -0
  150. data/spec/dummy/public/favicon.ico +0 -0
  151. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  152. data/spec/dummy/script/rails +6 -0
  153. data/spec/lib/application_spec.rb +49 -0
  154. data/spec/lib/extractor/extractor_spec.rb +9 -0
  155. data/spec/lib/extractor/middleware_spec.rb +44 -0
  156. data/spec/lib/extractor/writer_spec.rb +110 -0
  157. data/spec/lib/file_handler_spec.rb +18 -0
  158. data/spec/lib/method_description_spec.rb +98 -0
  159. data/spec/lib/param_description_spec.rb +345 -0
  160. data/spec/lib/param_group_spec.rb +60 -0
  161. data/spec/lib/rake_spec.rb +71 -0
  162. data/spec/lib/resource_description_spec.rb +48 -0
  163. data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
  164. data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
  165. data/spec/lib/swagger/response_validation_spec.rb +104 -0
  166. data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
  167. data/spec/lib/validator_spec.rb +113 -0
  168. data/spec/lib/validators/array_validator_spec.rb +85 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/rake.rb +21 -0
  171. metadata +415 -0
@@ -0,0 +1,33 @@
1
+ module Apipie
2
+ class RoutesFormatter
3
+ API_METHODS = %w{GET POST PUT PATCH OPTIONS DELETE}
4
+
5
+ # The entry method called by Apipie to extract the array
6
+ # representing the api dsl from the routes definition.
7
+ def format_routes(rails_routes, args)
8
+ rails_routes.map { |rails_route| format_route(rails_route, args) }
9
+ end
10
+
11
+ def format_route(rails_route, args)
12
+ { :path => format_path(rails_route),
13
+ :verb => format_verb(rails_route),
14
+ :desc => args[:desc],
15
+ :options => args[:options] }
16
+ end
17
+
18
+ def format_path(rails_route)
19
+ rails_route.path.spec.to_s.gsub('(.:format)', '')
20
+ end
21
+
22
+ def format_verb(rails_route)
23
+ verb = API_METHODS.select{|defined_verb| defined_verb =~ /\A#{rails_route.verb}\z/}
24
+ if verb.count != 1
25
+ verb = API_METHODS.select{|defined_verb| defined_verb == rails_route.constraints[:method]}
26
+ if verb.blank?
27
+ raise "Unknow verb #{rails_route.path.spec.to_s}"
28
+ end
29
+ end
30
+ verb.first
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ module Apipie
2
+ module Routing
3
+ module MapperExtensions
4
+ def apipie(options = {})
5
+ namespace "apipie", :path => Apipie.configuration.doc_base_url do
6
+ get 'apipie_checksum', :to => "apipies#apipie_checksum", :format => "json"
7
+ constraints(:version => /[^\/]+/, :resource => /[^\/]+/, :method => /[^\/]+/) do
8
+ get(options.reverse_merge("(:version)/(:resource)/(:method)" => "apipies#index", :as => :apipie))
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ ActionDispatch::Routing::Mapper.send :include, Apipie::Routing::MapperExtensions
@@ -0,0 +1,192 @@
1
+ #----------------------------------------------------------------------------------------------
2
+ # response_validation_helper.rb:
3
+ #
4
+ # this is an rspec utility to allow validation of responses against the swagger schema generated
5
+ # from the Apipie 'returns' definition for the call.
6
+ #
7
+ #
8
+ # to use this file in a controller rspec you should
9
+ # require 'apipie/rspec/response_validation_helper' in the spec file
10
+ #
11
+ #
12
+ # this utility provides two mechanisms: matcher-based validation and auto-validation
13
+ #
14
+ # matcher-based: an rspec matcher allowing 'expect(response).to match_declared_responses'
15
+ # auto-validation: all responses returned from 'get', 'post', etc. are automatically tested
16
+ #
17
+ # ===================================
18
+ # Matcher-based validation - example
19
+ # ===================================
20
+ # Assume the file 'my_controller_spec.rb':
21
+ #
22
+ # require 'apipie/rspec/response_validation_helper'
23
+ #
24
+ # RSpec.describe MyController, :type => :controller, :show_in_doc => true do
25
+ #
26
+ # describe "GET stuff with response validation" do
27
+ # render_views # this makes sure the 'get' operation will actually
28
+ # # return the rendered view even though this is a Controller spec
29
+ #
30
+ # it "does something" do
31
+ # response = get :index, {format: :json}
32
+ #
33
+ # # the following expectation will fail if the returned object
34
+ # # does not match the 'returns' declaration in the Controller,
35
+ # # or if there is no 'returns' declaration for the returned
36
+ # # HTTP status code
37
+ # expect(response).to match_declared_responses
38
+ # end
39
+ # end
40
+ #
41
+ #
42
+ # ===================================
43
+ # Auto-validation
44
+ # ===================================
45
+ # To use auto-validation, at the beginning of the block in which you want to turn on validation:
46
+ # -) turn on view rendering (by stating 'render_views')
47
+ # -) turn on response validation by stating 'auto_validate_rendered_views'
48
+ #
49
+ # For example, assume the file 'my_controller_spec.rb':
50
+ #
51
+ # require 'apipie/rspec/response_validation_helper'
52
+ #
53
+ # RSpec.describe MyController, :type => :controller, :show_in_doc => true do
54
+ #
55
+ # describe "GET stuff with response validation" do
56
+ # render_views
57
+ # auto_validate_rendered_views
58
+ #
59
+ # it "does something" do
60
+ # get :index, {format: :json}
61
+ # end
62
+ # it "does something else" do
63
+ # get :another_index, {format: :json}
64
+ # end
65
+ # end
66
+ #
67
+ # describe "GET stuff without response validation" do
68
+ # it "does something" do
69
+ # get :index, {format: :json}
70
+ # end
71
+ # it "does something else" do
72
+ # get :another_index, {format: :json}
73
+ # end
74
+ # end
75
+ #
76
+ #
77
+ # Once this is done, responses from http operations ('get', 'post', 'delete', etc.)
78
+ # will fail the test if the response structure does not match the 'returns' declaration
79
+ # on the method (for the actual HTTP status code), or if there is no 'returns' declaration
80
+ # for the HTTP status code.
81
+ #----------------------------------------------------------------------------------------------
82
+
83
+
84
+ #----------------------------------------------------------------------------------------------
85
+ # Response validation: core logic (used by auto-validation and manual-validation mechanisms)
86
+ #----------------------------------------------------------------------------------------------
87
+ class ActionController::Base
88
+ module Apipie::ControllerValidationHelpers
89
+ # this method is injected into ActionController::Base in order to
90
+ # get access to the names of the current controller, current action, as well as to the response
91
+ def schema_validation_errors_for_response
92
+ unprocessed_schema = Apipie::json_schema_for_method_response(controller_name, action_name, response.code, true)
93
+
94
+ if unprocessed_schema.nil?
95
+ err = "no schema defined for #{controller_name}##{action_name}[#{response.code}]"
96
+ return [nil, [err], RuntimeError.new(err)]
97
+ end
98
+
99
+ schema = JSON.parse(JSON(unprocessed_schema))
100
+
101
+ error_list = JSON::Validator.fully_validate(schema, response.body, :strict => false, :version => :draft4, :json => true)
102
+
103
+ error_object = Apipie::ResponseDoesNotMatchSwaggerSchema.new(controller_name, action_name, response.code, error_list, schema, response.body)
104
+
105
+ [schema, error_list, error_object]
106
+ rescue Apipie::NoDocumentedMethod
107
+ [nil, [], nil]
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ module Apipie
114
+ def self.print_validation_errors(validation_errors, schema, response, error_object=nil)
115
+ Rails.logger.warn(validation_errors.to_s)
116
+ if Rails.env.test?
117
+ puts "schema validation errors:"
118
+ validation_errors.each { |e| puts "--> #{e.to_s}" }
119
+ puts "schema: #{schema.nil? ? '<none>' : JSON(schema)}"
120
+ puts "response: #{response.body}"
121
+ raise error_object if error_object
122
+ end
123
+ end
124
+ end
125
+
126
+ #---------------------------------
127
+ # Manual-validation (RSpec matcher)
128
+ #---------------------------------
129
+ RSpec::Matchers.define :match_declared_responses do
130
+ match do |actual|
131
+ (schema, validation_errors) = subject.send(:schema_validation_errors_for_response)
132
+ valid = (validation_errors == [])
133
+ Apipie::print_validation_errors(validation_errors, schema, response) unless valid
134
+
135
+ valid
136
+ end
137
+ end
138
+
139
+
140
+ #---------------------------------
141
+ # Auto-validation logic
142
+ #---------------------------------
143
+ module RSpec::Rails::ViewRendering
144
+ # Augment the RSpec DSL
145
+ module ClassMethods
146
+ def auto_validate_rendered_views
147
+ before do
148
+ @is_response_validation_on = true
149
+ end
150
+
151
+ after do
152
+ @is_response_validation_on = false
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+ ActionController::TestCase::Behavior.instance_eval do
160
+ # instrument the 'process' method in ActionController::TestCase to enable response validation
161
+ module Apipie::ResponseValidationHelpers
162
+ @is_response_validation_on = false
163
+ def process(*args)
164
+ result = super(*args)
165
+ validate_response if @is_response_validation_on
166
+
167
+ result
168
+ end
169
+
170
+ def validate_response
171
+ controller.send(:validate_response_and_abort_with_info_if_errors)
172
+ end
173
+ end
174
+
175
+ end
176
+
177
+
178
+ class ActionController::Base
179
+ module Apipie::ControllerValidationHelpers
180
+ def validate_response_and_abort_with_info_if_errors
181
+
182
+ (schema, validation_errors, error_object) = schema_validation_errors_for_response
183
+
184
+ valid = (validation_errors == [])
185
+ if !valid
186
+ Apipie::print_validation_errors(validation_errors, schema, response, error_object)
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+
@@ -0,0 +1,39 @@
1
+ module Apipie
2
+
3
+ class SeeDescription
4
+
5
+ attr_reader :link, :description
6
+
7
+ def initialize(args)
8
+ if args.first.is_a? Hash
9
+ args = args.first
10
+ elsif args.count == 2
11
+ if args.last.is_a? Hash
12
+ args = {:link => args.first}.merge(args.last)
13
+ else
14
+ args = {:link => args.first, :description => args.second}
15
+ end
16
+ elsif args.count == 1 && args.first.is_a?(String)
17
+ args = {:link => args.first, :description => args.first}
18
+ else
19
+ raise ArgumentError "ApipieError: Bad use of see method."
20
+ end
21
+ @link = args[:link] || args['link']
22
+ @description = args[:desc] || args[:description] || args['desc'] || args['description']
23
+ end
24
+
25
+ def to_json
26
+ {:link => see_url, :description => description}
27
+ end
28
+
29
+ def see_url
30
+ method_description = Apipie[@link]
31
+ if method_description.nil?
32
+ raise ArgumentError.new("Method #{@link} referenced in 'see' does not exist.")
33
+ end
34
+ method_description.doc_url
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,69 @@
1
+ module Apipie
2
+
3
+ class FileHandler
4
+ def initialize(root)
5
+ @root = root.chomp('/')
6
+ @compiled_root = /^#{Regexp.escape(root)}/
7
+ @file_server = ::Rack::File.new(@root)
8
+ end
9
+
10
+ def match?(path)
11
+ # Replace all null bytes
12
+ path = ::Rack::Utils.unescape(path || '').gsub(/\x0/, '')
13
+
14
+ full_path = path.empty? ? @root : File.join(@root, path)
15
+ paths = "#{full_path}#{ext}"
16
+
17
+ matches = Dir[paths]
18
+ match = matches.detect { |m| File.file?(m) }
19
+ if match
20
+ match.sub!(@compiled_root, '')
21
+ match
22
+ end
23
+ end
24
+
25
+ def call(env)
26
+ @file_server.call(env)
27
+ end
28
+
29
+ def ext
30
+ @ext ||= begin
31
+ ext = cache_extension
32
+ "{,#{ext},/index#{ext}}"
33
+ end
34
+ end
35
+
36
+ def cache_extension
37
+ if ::ActionController::Base.respond_to?(:default_static_extension)
38
+ ::ActionController::Base.default_static_extension
39
+ else
40
+ ::ActionController::Base.page_cache_extension
41
+ end
42
+
43
+ end
44
+ end
45
+
46
+ class StaticDispatcher
47
+ # Dispatches the static files. Similar to ActionDispatch::Static, but
48
+ # it supports different baseurl configurations
49
+ def initialize(app, path)
50
+ @app = app
51
+ @file_handler = Apipie::FileHandler.new(path)
52
+ end
53
+
54
+ def call(env)
55
+ @baseurl ||= Apipie.configuration.doc_base_url
56
+ case env['REQUEST_METHOD']
57
+ when 'GET', 'HEAD'
58
+ path = env['PATH_INFO'].sub("#{@baseurl}/","/apipie/").chomp('/')
59
+
60
+ if match = @file_handler.match?(path)
61
+ env["PATH_INFO"] = match
62
+ return @file_handler.call(env)
63
+ end
64
+ end
65
+
66
+ @app.call(env)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,707 @@
1
+ module Apipie
2
+
3
+ #--------------------------------------------------------------------------
4
+ # Configuration. Should be moved to Apipie config.
5
+ #--------------------------------------------------------------------------
6
+ class SwaggerGenerator
7
+ require 'json'
8
+ require 'ostruct'
9
+ require 'open3'
10
+ require 'zlib' if Apipie.configuration.swagger_generate_x_computed_id_field?
11
+
12
+ attr_reader :computed_interface_id
13
+
14
+ def initialize(apipie)
15
+ @apipie = apipie
16
+ @issued_warnings = []
17
+ end
18
+
19
+ def params_in_body?
20
+ Apipie.configuration.swagger_content_type_input == :json
21
+ end
22
+
23
+ def params_in_body_use_reference?
24
+ Apipie.configuration.swagger_json_input_uses_refs
25
+ end
26
+
27
+ def responses_use_reference?
28
+ Apipie.configuration.swagger_responses_use_refs?
29
+ end
30
+
31
+ def include_warning_tags?
32
+ Apipie.configuration.swagger_include_warning_tags
33
+ end
34
+
35
+
36
+ def generate_from_resources(version, resources, method_name, lang, clear_warnings=false)
37
+ init_swagger_vars(version, lang, clear_warnings)
38
+
39
+ @lang = lang
40
+ @only_method = method_name
41
+ add_resources(resources)
42
+
43
+ @swagger[:info]["x-computed-id"] = @computed_interface_id if Apipie.configuration.swagger_generate_x_computed_id_field?
44
+ return @swagger
45
+ end
46
+
47
+
48
+ #--------------------------------------------------------------------------
49
+ # Initialization
50
+ #--------------------------------------------------------------------------
51
+
52
+ def init_swagger_vars(version, lang, clear_warnings=false)
53
+
54
+ # docs = {
55
+ # :name => Apipie.configuration.app_name,
56
+ # :info => Apipie.app_info(version, lang),
57
+ # :copyright => Apipie.configuration.copyright,
58
+ # :doc_url => Apipie.full_url(url_args),
59
+ # :api_url => Apipie.api_base_url(version),
60
+ # :resources => _resources
61
+ # }
62
+
63
+
64
+ @swagger = {
65
+ swagger: '2.0',
66
+ info: {
67
+ title: "#{Apipie.configuration.app_name}",
68
+ description: "#{Apipie.app_info(version, lang)}#{Apipie.configuration.copyright}",
69
+ version: "#{version}",
70
+ "x-copyright" => Apipie.configuration.copyright,
71
+ },
72
+ basePath: Apipie.api_base_url(version),
73
+ consumes: [],
74
+ paths: {},
75
+ definitions: {},
76
+ tags: [],
77
+ }
78
+
79
+ if Apipie.configuration.swagger_api_host
80
+ @swagger[:host] = Apipie.configuration.swagger_api_host
81
+ end
82
+
83
+ if params_in_body?
84
+ @swagger[:consumes] = ['application/json']
85
+ @swagger[:info][:title] += " (params in:body)"
86
+ else
87
+ @swagger[:consumes] = ['application/x-www-form-urlencoded', 'multipart/form-data']
88
+ @swagger[:info][:title] += " (params in:formData)"
89
+ end
90
+
91
+ @paths = @swagger[:paths]
92
+ @definitions = @swagger[:definitions]
93
+ @tags = @swagger[:tags]
94
+
95
+ @issued_warnings = [] if clear_warnings || @issued_warnings.nil?
96
+ @computed_interface_id = 0
97
+
98
+ @current_lang = lang
99
+ end
100
+
101
+ #--------------------------------------------------------------------------
102
+ # Engine interface methods
103
+ #--------------------------------------------------------------------------
104
+
105
+ def add_resources(resources)
106
+ resources.each do |resource_name, resource_defs|
107
+ add_resource_description(resource_name, resource_defs)
108
+ add_resource_methods(resource_name, resource_defs)
109
+ end
110
+ end
111
+
112
+ def add_resource_methods(resource_name, resource_defs)
113
+ resource_defs._methods.each do |apipie_method_name, apipie_method_defs|
114
+ add_ruby_method(@paths, apipie_method_defs)
115
+ end
116
+ end
117
+
118
+
119
+ #--------------------------------------------------------------------------
120
+ # Logging, debugging and regression-testing utilities
121
+ #--------------------------------------------------------------------------
122
+
123
+ def ruby_name_for_method(method)
124
+ return "<no method>" if method.nil?
125
+ method.resource.controller.name + "#" + method.method
126
+ end
127
+
128
+
129
+ def warn_missing_method_summary() warn 100, "missing short description for method"; end
130
+ def warn_added_missing_slash(path) warn 101,"added missing / at beginning of path: #{path}"; end
131
+ def warn_no_return_codes_specified() warn 102,"no return codes ('errors') specified"; end
132
+ def warn_hash_without_internal_typespec(param_name) warn 103,"the parameter :#{param_name} is a generic Hash without an internal type specification"; end
133
+ def warn_optional_param_in_path(param_name) warn 104, "the parameter :#{param_name} is 'in-path'. Ignoring 'not required' in DSL"; end
134
+ def warn_optional_without_default_value(param_name) warn 105,"the parameter :#{param_name} is optional but default value is not specified (use :default_value => ...)"; end
135
+ def warn_param_ignored_in_form_data(param_name) warn 106,"ignoring param :#{param_name} -- cannot include Hash without fields in a formData specification"; end
136
+ def warn_path_parameter_not_described(name,path) warn 107,"the parameter :#{name} appears in the path #{path} but is not described"; end
137
+ def warn_inferring_boolean(name) warn 108,"the parameter [#{name}] is Enum with [true,false] values. Inferring 'boolean'"; end
138
+
139
+ def warn(warning_num, msg)
140
+ suppress = Apipie.configuration.swagger_suppress_warnings
141
+ return if suppress == true
142
+ return if suppress.is_a?(Array) && suppress.include?(warning_num)
143
+
144
+ method_id = ruby_name_for_method(@current_method)
145
+ warning_id = "#{method_id}#{warning_num}#{msg}"
146
+
147
+ if @issued_warnings.include?(warning_id)
148
+ # Already issued this warning for the current method
149
+ return
150
+ end
151
+
152
+ print "WARNING (#{warning_num}): [#{method_id}] -- #{msg}\n"
153
+ @issued_warnings.push(warning_id)
154
+ @warnings_issued = true
155
+ end
156
+
157
+ def info(msg)
158
+ print "--- INFO: [#{ruby_name_for_method(@current_method)}] -- #{msg}\n"
159
+ end
160
+
161
+
162
+ # the @computed_interface_id is a number that is uniquely derived from the list of operations
163
+ # added to the swagger definition (in an order-dependent way).
164
+ # it can be used for regression testing, allowing some differentiation between changes that
165
+ # result from changes to the input and those that result from changes to the generation
166
+ # algorithms.
167
+ # note that at the moment, this only takes operation ids into account, and ignores parameter
168
+ # definitions, so it's only partially useful.
169
+ def include_op_id_in_computed_interface_id(op_id)
170
+ @computed_interface_id = Zlib::crc32("#{@computed_interface_id} #{op_id}") if Apipie.configuration.swagger_generate_x_computed_id_field?
171
+ end
172
+
173
+ #--------------------------------------------------------------------------
174
+ # Create a tag description for a described resource
175
+ #--------------------------------------------------------------------------
176
+
177
+ def tag_name_for_resource(resource)
178
+ # resource.controller
179
+ resource._id
180
+ end
181
+
182
+ def add_resource_description(resource_name, resource)
183
+ if resource._full_description
184
+ @tags << {
185
+ name: tag_name_for_resource(resource),
186
+ description: Apipie.app.translate(resource._full_description, @current_lang)
187
+ }
188
+ end
189
+ end
190
+
191
+ #--------------------------------------------------------------------------
192
+ # Create swagger definitions for a ruby method
193
+ #--------------------------------------------------------------------------
194
+
195
+ def add_ruby_method(paths, ruby_method)
196
+
197
+ if @only_method
198
+ return unless ruby_method.method == @only_method
199
+ else
200
+ return if !ruby_method.show
201
+ end
202
+
203
+ for api in ruby_method.apis do
204
+ # controller: ruby_method.resource.controller.name,
205
+
206
+ path = swagger_path(api.path)
207
+ paths[path] ||= {}
208
+ methods = paths[path]
209
+ @current_method = ruby_method
210
+
211
+ @warnings_issued = false
212
+ responses = swagger_responses_hash_for_method(ruby_method)
213
+ if include_warning_tags?
214
+ warning_tags = @warnings_issued ? ['warnings issued'] : []
215
+ else
216
+ warning_tags = []
217
+ end
218
+
219
+ op_id = swagger_op_id_for_path(api.http_method, api.path)
220
+
221
+ include_op_id_in_computed_interface_id(op_id)
222
+
223
+ method_key = api.http_method.downcase
224
+ @current_http_method = method_key
225
+
226
+ methods[method_key] = {
227
+ tags: [tag_name_for_resource(ruby_method.resource)] + warning_tags + ruby_method.tag_list.tags,
228
+ consumes: params_in_body? ? ['application/json'] : ['application/x-www-form-urlencoded', 'multipart/form-data'],
229
+ operationId: op_id,
230
+ summary: Apipie.app.translate(api.short_description, @current_lang),
231
+ parameters: swagger_params_array_for_method(ruby_method, api.path),
232
+ responses: responses,
233
+ description: ruby_method.full_description
234
+ }
235
+
236
+ if methods[method_key][:summary].nil?
237
+ methods[method_key].delete(:summary)
238
+ warn_missing_method_summary
239
+ end
240
+ end
241
+ end
242
+
243
+ #--------------------------------------------------------------------------
244
+ # Utilities for conversion of ruby syntax to swagger syntax
245
+ #--------------------------------------------------------------------------
246
+
247
+ def swagger_path(str)
248
+ str = str.gsub(/:(\w+)/, '{\1}')
249
+ str = str.gsub(/\/$/, '')
250
+
251
+ if str[0] != '/'
252
+ warn_added_missing_slash(str)
253
+ str = '/' + str
254
+ end
255
+ str
256
+ end
257
+
258
+ def remove_colons(str)
259
+ str.gsub(":", "_")
260
+ end
261
+
262
+ def swagger_op_id_for_method(method)
263
+ remove_colons method.resource.controller.name + "::" + method.method
264
+ end
265
+
266
+ def swagger_id_for_typename(typename)
267
+ typename
268
+ end
269
+
270
+ def swagger_op_id_for_path(http_method, path)
271
+ # using lowercase http method, because the 'swagger-codegen' tool outputs
272
+ # strange method names if the http method is in uppercase
273
+ http_method.downcase + path.gsub(/\//,'_').gsub(/:(\w+)/, '\1').gsub(/_$/,'')
274
+ end
275
+
276
+ class SwaggerTypeWithFormat
277
+ attr_reader :str_format
278
+ def initialize(type, str_format)
279
+ @type = type
280
+ @str_format = str_format
281
+ end
282
+
283
+ def to_s
284
+ @type
285
+ end
286
+
287
+ def ==(other)
288
+ other.to_s == self.to_s
289
+ end
290
+ end
291
+
292
+ def lookup
293
+ @lookup ||= {
294
+ numeric: "number",
295
+ hash: "object",
296
+ array: "array",
297
+
298
+ # see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
299
+ integer: SwaggerTypeWithFormat.new("integer", "int32"),
300
+ long: SwaggerTypeWithFormat.new("integer", "int64"),
301
+ number: SwaggerTypeWithFormat.new("number", nil), # here just for completeness
302
+ float: SwaggerTypeWithFormat.new("number", "float"),
303
+ double: SwaggerTypeWithFormat.new("number", "double"),
304
+ string: SwaggerTypeWithFormat.new("string", nil), # here just for completeness
305
+ byte: SwaggerTypeWithFormat.new("string", "byte"),
306
+ binary: SwaggerTypeWithFormat.new("string", "binary"),
307
+ boolean: SwaggerTypeWithFormat.new("boolean", nil), # here just for completeness
308
+ date: SwaggerTypeWithFormat.new("string", "date"),
309
+ dateTime: SwaggerTypeWithFormat.new("string", "date-time"),
310
+ password: SwaggerTypeWithFormat.new("string", "password"),
311
+ }
312
+ end
313
+
314
+
315
+ def swagger_param_type(param_desc)
316
+ if param_desc.nil?
317
+ raise("problem")
318
+ end
319
+
320
+ v = param_desc.validator
321
+ if v.nil?
322
+ return "string"
323
+ end
324
+
325
+ if v.class == Apipie::Validator::EnumValidator || (v.respond_to?(:is_enum?) && v.is_enum?)
326
+ if v.values - [true, false] == [] && [true, false] - v.values == []
327
+ warn_inferring_boolean(param_desc.name)
328
+ return "boolean"
329
+ else
330
+ return "enum"
331
+ end
332
+ elsif v.class == Apipie::Validator::HashValidator
333
+ # pp v
334
+ end
335
+
336
+
337
+ return lookup[v.expected_type.to_sym] || v.expected_type
338
+ end
339
+
340
+
341
+ #--------------------------------------------------------------------------
342
+ # Responses
343
+ #--------------------------------------------------------------------------
344
+
345
+ def json_schema_for_method_response(method, return_code, allow_nulls)
346
+ @definitions = {}
347
+ for response in method.returns
348
+ if response.code.to_s == return_code.to_s
349
+ schema = response_schema(response, allow_nulls) if response.code.to_s == return_code.to_s
350
+ schema[:definitions] = @definitions if @definitions != {}
351
+ return schema
352
+ end
353
+ end
354
+ nil
355
+ end
356
+
357
+ def json_schema_for_self_describing_class(cls, allow_nulls)
358
+ adapter = ResponseDescriptionAdapter.from_self_describing_class(cls)
359
+ response_schema(adapter, allow_nulls)
360
+ end
361
+
362
+ def response_schema(response, allow_nulls=false)
363
+ begin
364
+ # no need to warn about "missing default value for optional param" when processing response definitions
365
+ prev_value = @disable_default_value_warning
366
+ @disable_default_value_warning = true
367
+
368
+ if responses_use_reference? && response.typename
369
+ schema = {"$ref" => gen_referenced_block_from_params_array(swagger_id_for_typename(response.typename), response.params_ordered, allow_nulls)}
370
+ else
371
+ schema = json_schema_obj_from_params_array(response.params_ordered, allow_nulls)
372
+ end
373
+
374
+ ensure
375
+ @disable_default_value_warning = prev_value
376
+ end
377
+
378
+ if response.is_array? && schema
379
+ schema = {
380
+ type: allow_nulls ? ["array","null"] : "array",
381
+ items: schema
382
+ }
383
+ end
384
+
385
+ if response.allow_additional_properties
386
+ schema[:additionalProperties] = true
387
+ end
388
+
389
+ schema
390
+ end
391
+
392
+ def swagger_responses_hash_for_method(method)
393
+ result = {}
394
+
395
+ for error in method.errors
396
+ error_block = {description: Apipie.app.translate(error.description, @current_lang)}
397
+ result[error.code] = error_block
398
+ end
399
+
400
+ for response in method.returns
401
+ swagger_response_block = {
402
+ description: response.description
403
+ }
404
+
405
+ schema = response_schema(response)
406
+ swagger_response_block[:schema] = schema if schema
407
+
408
+ result[response.code] = swagger_response_block
409
+ end
410
+
411
+ if result.length == 0
412
+ warn_no_return_codes_specified
413
+ result[200] = {description: 'ok'}
414
+ end
415
+
416
+ result
417
+ end
418
+
419
+
420
+
421
+ #--------------------------------------------------------------------------
422
+ # Auto-insertion of parameters that are implicitly defined in the path
423
+ #--------------------------------------------------------------------------
424
+
425
+ def param_names_from_path(path)
426
+ path.scan(/:(\w+)/).map do |ar|
427
+ ar[0].to_sym
428
+ end
429
+ end
430
+
431
+ def add_missing_params(method, path)
432
+ param_names_from_method = method.params.map {|name, desc| name}
433
+ missing = param_names_from_path(path) - param_names_from_method
434
+
435
+ result = method.params
436
+
437
+ missing.each do |name|
438
+ warn_path_parameter_not_described(name, path)
439
+ result[name.to_sym] = OpenStruct.new({
440
+ required: true,
441
+ _gen_added_from_path: true,
442
+ name: name,
443
+ validator: Apipie::Validator::NumberValidator.new(nil),
444
+ options: {
445
+ in: "path"
446
+ }
447
+ })
448
+ end
449
+
450
+ result
451
+ end
452
+
453
+ #--------------------------------------------------------------------------
454
+ # The core routine for creating a swagger parameter definition block.
455
+ # The output is slightly different when the parameter is inside a schema block.
456
+ #--------------------------------------------------------------------------
457
+ def swagger_atomic_param(param_desc, in_schema, name, allow_nulls)
458
+ def save_field(entry, openapi_key, v, apipie_key=openapi_key, translate=false)
459
+ if v.key?(apipie_key)
460
+ if translate
461
+ entry[openapi_key] = Apipie.app.translate(v[apipie_key], @current_lang)
462
+ else
463
+ entry[openapi_key] = v[apipie_key]
464
+ end
465
+ end
466
+ end
467
+
468
+ swagger_def = {}
469
+ swagger_def[:name] = name if !name.nil?
470
+
471
+ swg_param_type = swagger_param_type(param_desc)
472
+ swagger_def[:type] = swg_param_type.to_s
473
+ if (swg_param_type.is_a? SwaggerTypeWithFormat) && !swg_param_type.str_format.nil?
474
+ swagger_def[:format] = swg_param_type.str_format
475
+ end
476
+
477
+ if swagger_def[:type] == "array"
478
+ swagger_def[:items] = {type: "string"}
479
+ end
480
+
481
+ if swagger_def[:type] == "enum"
482
+ swagger_def[:type] = "string"
483
+ swagger_def[:enum] = param_desc.validator.values
484
+ end
485
+
486
+ if swagger_def[:type] == "object" # we only get here if there is no specification of properties for this object
487
+ swagger_def[:additionalProperties] = true
488
+ warn_hash_without_internal_typespec(param_desc.name)
489
+ end
490
+
491
+ if param_desc.is_array?
492
+ new_swagger_def = {
493
+ items: swagger_def,
494
+ type: 'array'
495
+ }
496
+ swagger_def = new_swagger_def
497
+ if allow_nulls
498
+ swagger_def[:type] = [swagger_def[:type], "null"]
499
+ end
500
+ end
501
+
502
+ if allow_nulls
503
+ swagger_def[:type] = [swagger_def[:type], "null"]
504
+ end
505
+
506
+ if !in_schema
507
+ swagger_def[:in] = param_desc.options.fetch(:in, @default_value_for_param_in)
508
+ swagger_def[:required] = param_desc.required if param_desc.required
509
+ end
510
+
511
+ save_field(swagger_def, :description, param_desc.options, :desc, true) unless param_desc.options[:desc].nil?
512
+ save_field(swagger_def, :default, param_desc.options, :default_value)
513
+
514
+ if param_desc.respond_to?(:_gen_added_from_path) && !param_desc.required
515
+ warn_optional_param_in_path(param_desc.name)
516
+ swagger_def[:required] = true
517
+ end
518
+
519
+ if !swagger_def[:required] && !swagger_def.key?(:default)
520
+ warn_optional_without_default_value(param_desc.name) unless @disable_default_value_warning
521
+ end
522
+
523
+ swagger_def
524
+ end
525
+
526
+
527
+ #--------------------------------------------------------------------------
528
+ # JSON schema and referenced-object generation
529
+ #--------------------------------------------------------------------------
530
+
531
+ def ref_to(name)
532
+ "#/definitions/#{name}"
533
+ end
534
+
535
+
536
+ def json_schema_obj_from_params_array(params_array, allow_nulls = false)
537
+ (param_defs, required_params) = json_schema_param_defs_from_params_array(params_array, allow_nulls)
538
+
539
+ result = {type: "object"}
540
+ result[:properties] = param_defs
541
+ result[:additionalProperties] = false unless Apipie.configuration.swagger_allow_additional_properties_in_response
542
+ result[:required] = required_params if required_params.length > 0
543
+
544
+ param_defs.length > 0 ? result : nil
545
+ end
546
+
547
+ def gen_referenced_block_from_params_array(name, params_array, allow_nulls=false)
548
+ return ref_to(:name) if @definitions.key(:name)
549
+
550
+ schema_obj = json_schema_obj_from_params_array(params_array, allow_nulls)
551
+ return nil if schema_obj.nil?
552
+
553
+ @definitions[name.to_sym] = schema_obj
554
+ ref_to(name.to_sym)
555
+ end
556
+
557
+ def json_schema_param_defs_from_params_array(params_array, allow_nulls = false)
558
+ param_defs = {}
559
+ required_params = []
560
+
561
+ params_array ||= []
562
+
563
+
564
+ for param_desc in params_array
565
+ if !param_desc.respond_to?(:required)
566
+ # pp param_desc
567
+ raise ("unexpected param_desc format")
568
+ end
569
+
570
+ required_params.push(param_desc.name.to_sym) if param_desc.required
571
+
572
+ param_type = swagger_param_type(param_desc)
573
+
574
+ if param_type == "object" && param_desc.validator.params_ordered
575
+ schema = json_schema_obj_from_params_array(param_desc.validator.params_ordered, allow_nulls)
576
+ if param_desc.additional_properties
577
+ schema[:additionalProperties] = true
578
+ end
579
+
580
+ if param_desc.is_array?
581
+ new_schema = {
582
+ type: 'array',
583
+ items: schema
584
+ }
585
+ schema = new_schema
586
+ end
587
+
588
+ if allow_nulls
589
+ # ideally we would write schema[:type] = ["object", "null"]
590
+ # but due to a bug in the json-schema gem, we need to use anyOf
591
+ # see https://github.com/ruby-json-schema/json-schema/issues/404
592
+ new_schema = {
593
+ anyOf: [schema, {type: "null"}]
594
+ }
595
+ schema = new_schema
596
+ end
597
+ param_defs[param_desc.name.to_sym] = schema if !schema.nil?
598
+ else
599
+ param_defs[param_desc.name.to_sym] = swagger_atomic_param(param_desc, true, nil, allow_nulls)
600
+ end
601
+ end
602
+
603
+ [param_defs, required_params]
604
+ end
605
+
606
+
607
+
608
+ #--------------------------------------------------------------------------
609
+ # swagger "Params" block generation
610
+ #--------------------------------------------------------------------------
611
+
612
+ def body_allowed_for_current_method
613
+ !(['get', 'head'].include?(@current_http_method))
614
+ end
615
+
616
+ def swagger_params_array_for_method(method, path)
617
+
618
+ swagger_result = []
619
+ all_params_hash = add_missing_params(method, path)
620
+
621
+ body_param_defs_array = all_params_hash.map {|k, v| v if !param_names_from_path(path).include?(k)}.select{|v| !v.nil?}
622
+ body_param_defs_hash = all_params_hash.select {|k, v| v if !param_names_from_path(path).include?(k)}
623
+ path_param_defs_hash = all_params_hash.select {|k, v| v if param_names_from_path(path).include?(k)}
624
+
625
+ path_param_defs_hash.each{|name,desc| desc.required = true}
626
+ add_params_from_hash(swagger_result, path_param_defs_hash, nil, "path")
627
+
628
+ if params_in_body? && body_allowed_for_current_method
629
+ if params_in_body_use_reference?
630
+ swagger_schema_for_body = {"$ref" => gen_referenced_block_from_params_array("#{swagger_op_id_for_method(method)}_input", body_param_defs_array)}
631
+ else
632
+ swagger_schema_for_body = json_schema_obj_from_params_array(body_param_defs_array)
633
+ end
634
+
635
+ swagger_body_param = {
636
+ name: 'body',
637
+ in: 'body',
638
+ schema: swagger_schema_for_body
639
+ }
640
+ swagger_result.push(swagger_body_param) if !swagger_schema_for_body.nil?
641
+
642
+ else
643
+ add_params_from_hash(swagger_result, body_param_defs_hash)
644
+ end
645
+
646
+ add_headers_from_hash(swagger_result, method.headers) if method.headers.present?
647
+
648
+ swagger_result
649
+ end
650
+
651
+
652
+ def add_headers_from_hash(swagger_params_array, headers)
653
+ swagger_headers = headers.map do |header|
654
+ {
655
+ name: header[:name],
656
+ in: 'header',
657
+ required: header[:options][:required],
658
+ description: header[:description],
659
+ type: header[:options][:type] || 'string'
660
+ }
661
+
662
+ end
663
+ swagger_params_array.push(*swagger_headers)
664
+ end
665
+
666
+
667
+ def add_params_from_hash(swagger_params_array, param_defs, prefix=nil, default_value_for_in=nil)
668
+
669
+ if default_value_for_in
670
+ @default_value_for_param_in = default_value_for_in
671
+ else
672
+ if body_allowed_for_current_method
673
+ @default_value_for_param_in = "formData"
674
+ else
675
+ @default_value_for_param_in = "query"
676
+ end
677
+ end
678
+
679
+
680
+ param_defs.each do |name, desc|
681
+
682
+ if !prefix.nil?
683
+ name = "#{prefix}[#{name}]"
684
+ end
685
+
686
+ if swagger_param_type(desc) == "object"
687
+ if desc.validator.params_ordered
688
+ params_hash = Hash[desc.validator.params_ordered.map {|desc| [desc.name, desc]}]
689
+ add_params_from_hash(swagger_params_array, params_hash, name)
690
+ else
691
+ warn_param_ignored_in_form_data(desc.name)
692
+ end
693
+ else
694
+ param_entry = swagger_atomic_param(desc, false, name, false)
695
+ if param_entry[:required]
696
+ swagger_params_array.unshift(param_entry)
697
+ else
698
+ swagger_params_array.push(param_entry)
699
+ end
700
+
701
+ end
702
+ end
703
+ end
704
+
705
+ end
706
+
707
+ end