grape-swagger 0.23.0 → 0.24.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c9531be292e7a5a8d3ee086291e248f42f13457
4
- data.tar.gz: 8f7cea40f42d1bb5a474f3468000c837eb540699
3
+ metadata.gz: 99ac4d378547c6998e6c712a83eb82d8277786fb
4
+ data.tar.gz: 9d18548301ad46bd474671a770c5e528ee7a4f3c
5
5
  SHA512:
6
- metadata.gz: 133c28324c1637b9117e06e33e4585df7b3c072dba7432784550b1c309b24b1635abd18f97a06ce43b9c9650176a7df7bccb0a4d7ddb4cc80d61ac7ddbd067a1
7
- data.tar.gz: 238698df89fbb172922f2224652eddff2fc26d89c593fb90f787bab4293def4d32bcdfd929200c7ef1770b6c33a486feb7435763bae9eb8708e83992c927cd2f
6
+ metadata.gz: b609366f8b1e964dbffbc0a65593bca55d050ac46cac23896627fd0b2b49f9a2b4fc368022e20d3e2866d458322d083a13f1bc44d7cc36c4bff323d7082497ed
7
+ data.tar.gz: 6c6e93549bf4b7eebc659155dcb21ce5d999ef811ea1f8dbceb7c6104b35c3765d0ea6ef16fb706303f9dcd86c99811862fd939af471cbb1a5ff730c9c07aa50
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: 226
31
+ Max: 240
32
32
 
33
33
  # Offense count: 10
34
34
  Metrics/CyclomaticComplexity:
data/CHANGELOG.md CHANGED
@@ -8,12 +8,30 @@
8
8
 
9
9
  * Your contribution here.
10
10
 
11
+ ### 0.24.0 (September 23, 2016)
12
+
13
+ #### Features
14
+
15
+ * [#504](https://github.com/ruby-grape/grape-swagger/pull/504): Added support for set the 'collectionFormat' of arrays - [@rczjns](https://github.com/rczjns).
16
+ * [#502](https://github.com/ruby-grape/grape-swagger/pull/502): Adds specs for rake tasks - [@LeFnord](https://github.com/LeFnord).
17
+ * [#501](https://github.com/ruby-grape/grape-swagger/pull/501): Adds getting of a specified resource for Rake Tasks - [@LeFnord](https://github.com/LeFnord).
18
+ * [#500](https://github.com/ruby-grape/grape-swagger/pull/500): Adds Rake tasks to get and validate OAPI/Swagger documentation - [@LeFnord](https://github.com/LeFnord).
19
+ * [#493](https://github.com/ruby-grape/grape-swagger/pull/493): Swagger UI endpoint authorization. - [@texpert](https://github.com/texpert).
20
+ * [#492](https://github.com/ruby-grape/grape/pull/492): Define security requirements on endpoint methods - [@tomregelink](https://github.com/tomregelink).
21
+ * [#497](https://github.com/ruby-grape/grape-swagger/pull/497): Use ruby-grape-danger in Dangerfile - [@dblock](https://github.com/dblock).
22
+
23
+ #### Fixes
24
+
25
+ * [#503](https://github.com/ruby-grape/grape-swagger/pull/503): Corrects exposing of inline definitions - [@LeFnord](https://github.com/LeFnord).
26
+ * [#494](https://github.com/ruby-grape/grape-swagger/pull/494): Header parametes are now included in documentation when body parameters have been defined - [@anakinj](https://github.com/anakinj).
27
+ * [#505](https://github.com/ruby-grape/grape-swagger/pull/505): Combines namespaces with their mounted paths to allow APIs with specified mount_paths - [@KevinLiddle](https://github.com/KevinLiddle).
28
+
11
29
  ### 0.23.0 (August 5, 2016)
12
30
 
13
31
  #### Features
14
32
 
15
- * [#491](https://github.com/ruby-grape/grape/pull/491): Add `ignore_defaults` option - [@pezholio](https://github.com/pezholio).
16
- * [#486](https://github.com/ruby-grape/grape/pull/486): Use an automated PR linter, [danger.systems](http://danger.systems) - [@dblock](https://github.com/dblock).
33
+ * [#491](https://github.com/ruby-grape/grape-swagger/pull/491): Add `ignore_defaults` option - [@pezholio](https://github.com/pezholio).
34
+ * [#486](https://github.com/ruby-grape/grape-swagger/pull/486): Use an automated PR linter, [danger.systems](http://danger.systems) - [@dblock](https://github.com/dblock).
17
35
 
18
36
  #### Fixes
19
37
 
data/Dangerfile CHANGED
@@ -1 +1 @@
1
- # inherits from https://github.com/ruby-grape/danger
1
+ danger.import_dangerfile(gem: 'ruby-grape-danger')
data/Gemfile CHANGED
@@ -16,4 +16,8 @@ if RUBY_VERSION < '2.2.2'
16
16
  gem 'activesupport', '<5.0.0'
17
17
  end
18
18
 
19
- gem 'danger', '~> 2.1', require: false
19
+ group :test do
20
+ gem 'ruby-grape-danger', '~> 0.1.0', require: false
21
+ gem 'grape-entity'
22
+ gem 'grape-swagger-entity'
23
+ end
data/README.md CHANGED
@@ -14,10 +14,12 @@
14
14
  * [Model Parsers](#model_parsers)
15
15
  * [Configure](#configure)
16
16
  * [Routes Configuration](#routes)
17
+ * [Securing the Swagger UI](#oauth)
17
18
  * [Markdown](#md_usage)
18
19
  * [Response documentation](#response)
19
20
  * [Extensions](#extensions)
20
21
  * [Example](#example)
22
+ * [Rake Tasks](#rake)
21
23
 
22
24
  <a name="what" />
23
25
  ## What is grape-swagger?
@@ -50,7 +52,7 @@ grape-swagger | swagger spec | grape | grape-entity | represen
50
52
  0.20.1 | 2.0 | >= 0.12.0 ... <= 0.14.0 | <= 0.5.1 | n/a |
51
53
  0.20.3 | 2.0 | >= 0.12.0 ... ~> 0.16.2 | ~> 0.5.1 | n/a |
52
54
  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 |
55
+ 0.23.0 | 2.0 | >= 0.12.0 ... <= 0.17.0 | <= 0.5.1 | >= 2.4.1 |
54
56
 
55
57
  <a name="swagger-spec" />
56
58
  ## Swagger-Spec
@@ -192,6 +194,9 @@ end
192
194
  * [add_version](#add_version)
193
195
  * [doc_version](#doc_version)
194
196
  * [markdown](#markdown)
197
+ * [endpoint_auth_wrapper](#endpoint_auth_wrapper)
198
+ * [swagger_endpoint_guard](#swagger_endpoint_guard)
199
+ * [oauth_token](#oauth_token)
195
200
  * [security_definitions](#security_definitions)
196
201
  * [models](#models)
197
202
  * [hide_documentation_path](#hide_documentation_path)
@@ -273,6 +278,33 @@ add_swagger_documentation \
273
278
  markdown: GrapeSwagger::Markdown::RedcarpetAdapter.new
274
279
  ```
275
280
 
281
+ <a name="endpoint_auth_wrapper" />
282
+ #### endpoint_auth_wrapper:
283
+ Specify the middleware to use for securing endpoints.
284
+
285
+ ```ruby
286
+ add_swagger_documentation \
287
+ endpoint_auth_wrapper: WineBouncer::OAuth2
288
+ ```
289
+
290
+ <a name="swagger_endpoint_guard" />
291
+ #### swagger_endpoint_guard:
292
+ Specify the method and auth scopes, used by the middleware for securing endpoints.
293
+
294
+ ```ruby
295
+ add_swagger_documentation \
296
+ swagger_endpoint_guard: 'oauth2 false'
297
+ ```
298
+
299
+ <a name="oauth_token" />
300
+ #### oauth_token:
301
+ Specify the method to get the oauth_token, provided by the middleware.
302
+
303
+ ```ruby
304
+ add_swagger_documentation \
305
+ oauth_token: 'doorkeeper_access_token'
306
+ ```
307
+
276
308
  <a name="security_definitions" />
277
309
  #### security_definitions:
278
310
  Specify the [Security Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object)
@@ -518,6 +550,39 @@ end
518
550
  }
519
551
  ```
520
552
 
553
+ #### Collection format of arrays
554
+
555
+ You can set the collection format of an array, using the documentation hash.
556
+
557
+ Collection format determines the format of the array if type array is used. Possible values are:
558
+ * csv - comma separated values foo,bar.
559
+ * ssv - space separated values foo bar.
560
+ * tsv - tab separated values foo\tbar.
561
+ * pipes - pipe separated values foo|bar.
562
+ * multi - corresponds to multiple parameter instances instead of multiple values for a single instance foo=bar&foo=baz. This is valid only for parameters in "query" or "formData".
563
+
564
+ ```ruby
565
+ params do
566
+ requires :statuses, type: Array[String], documentation: { collectionFormat: 'multi' }
567
+ end
568
+ post :act do
569
+ ...
570
+ end
571
+ ```
572
+
573
+ ```json
574
+ {
575
+ "in": "formData",
576
+ "name": "statuses",
577
+ "type": "array",
578
+ "items": {
579
+ "type": "string"
580
+ },
581
+ "collectionFormat": "multi",
582
+ "required": true
583
+ }
584
+ ```
585
+
521
586
  #### Multi types
522
587
 
523
588
  By default when you set multi types, the first type is selected as swagger type
@@ -765,9 +830,78 @@ module API
765
830
  end
766
831
  ```
767
832
 
833
+ <a name="oauth" />
834
+ ## Securing the Swagger UI
835
+
836
+
837
+ The Swagger UI on Grape could be secured from unauthorized access using any middleware, which provides certain methods:
838
+
839
+ - a *before* method to be run in the Grape controller for authorization purpose;
840
+ - some guard method, which could receive as argument a string or an array of authorization scopes;
841
+ - a method which processes and returns the access token received in the HTTP request headers (usually in the 'HTTP_AUTHORIZATION' header).
842
+
843
+ Below are some examples of securing the Swagger UI on Grape installed along with Ruby on Rails:
844
+
845
+ - The WineBouncer and Doorkeeper gems are used in the examples;
846
+ - 'rails' and 'wine_bouncer' gems should be required prior to 'grape-swagger' in boot.rb;
847
+ - This works with a fresh PR to WineBouncer which is yet unmerged - [WineBouncer PR](https://github.com/antek-drzewiecki/wine_bouncer/pull/64).
848
+
849
+ This is how to configure the grape_swagger documentation:
850
+
851
+ ```ruby
852
+ add_swagger_documentation base_path: '/',
853
+ title: 'My API',
854
+ doc_version: '0.0.1',
855
+ hide_documentation_path: true,
856
+ hide_format: true,
857
+ endpoint_auth_wrapper: WineBouncer::OAuth2, # This is the middleware for securing the Swagger UI
858
+ swagger_endpoint_guard: 'oauth2 false', # this is the guard method and scope
859
+ oauth_token: 'doorkeeper_access_token' # This is the method returning the access_token
860
+ ```
861
+
862
+ The guard method should inject the Security Requirement Object into the endpoint's route settings (see Grape::DSL::Settings.route_setting method).
863
+
864
+ The 'oauth2 false' added to swagger_documentation is making the main Swagger endpoint protected with OAuth, i.e. it
865
+ is retreiving the access_token from the HTTP request, but the 'false' scope is for skipping authorization and showing
866
+ the UI for everyone. If the scope would be set to something else, like 'oauth2 admin', for example, than the UI
867
+ wouldn't be displayed at all to unauthorized users.
868
+
869
+ Further on, the guard could be used, where necessary, for endpoint access protection. Put it prior to the endpoint's method:
870
+
871
+ ```ruby
872
+ resource :users do
873
+ oauth2 'read, write'
874
+ get do
875
+ render_users
876
+ end
877
+
878
+ oauth2 'admin'
879
+ post do
880
+ User.create!...
881
+ end
882
+ end
883
+ ```
884
+
885
+ And, finally, if you want to not only restrict the access, but to completely hide the endpoint from unauthorized
886
+ users, you could pass a lambda to the :hidden key of a endpoint's description:
887
+
888
+ ```ruby
889
+ not_admins = lambda { |token=nil| token.nil? || !User.find(token.resource_owner_id).admin? }
890
+
891
+ resource :users do
892
+ desc 'Create user', hidden: not_admins
893
+ oauth2 'admin'
894
+ post do
895
+ User.create!...
896
+ end
897
+ end
898
+ ```
899
+
900
+ The lambda is checking whether the user is authenticated (if not, the token is nil by default), and has the admin
901
+ role - only admins can see this endpoint.
768
902
 
769
903
  <a name="md_usage" />
770
- ### Markdown in Detail
904
+ ## Markdown in Detail
771
905
 
772
906
  The grape-swagger gem allows you to add an explanation in markdown in the detail field. Which would result in proper formatted markdown in Swagger UI.
773
907
  Grape-swagger uses adapters for several markdown formatters. It includes adapters for [kramdown](http://kramdown.rubyforge.org) (kramdown [syntax](http://kramdown.rubyforge.org/syntax.html)) and [redcarpet](https://github.com/vmg/redcarpet).
@@ -961,19 +1095,20 @@ route_setting :x_def, [{ for: 422, other: 'stuff' }, { for: 200, some: 'stuff' }
961
1095
  ```
962
1096
 
963
1097
  <a="example" />
964
- # Example
1098
+ ## Example
965
1099
 
966
1100
  Go into example directory and run it: `$ bundle exec rackup`
967
1101
  go to: `http://localhost:9292/swagger_doc` to get it
968
1102
 
969
1103
  For request examples load the [postman file]()
970
- ## Grouping the API list using Namespace
1104
+
1105
+ #### Grouping the API list using Namespace
971
1106
 
972
1107
  Use namespace for grouping APIs
973
1108
 
974
1109
  ![grape-swagger-v2-new-corrected](https://cloud.githubusercontent.com/assets/1027590/13516020/979cfefa-e1f9-11e5-9624-f4a6b17a3c8a.png)
975
1110
 
976
- # Example
1111
+ #### Example Code
977
1112
 
978
1113
  ```ruby
979
1114
  class NamespaceApi < Grape::API
@@ -1005,6 +1140,38 @@ end
1005
1140
 
1006
1141
  ```
1007
1142
 
1143
+
1144
+ <a name="rake" />
1145
+ ## Rake Tasks
1146
+
1147
+ Add these lines to your Rakefile, and initialize the Task class with your Api class – be sure your Api class is available.
1148
+
1149
+ ```ruby
1150
+ require 'grape-swagger/rake/oapi_tasks'
1151
+ GrapeSwagger::Rake::OapiTasks.new(::Api::Base)
1152
+ ```
1153
+
1154
+ #### OpenApi/Swagger Documentation
1155
+
1156
+ ```
1157
+ rake oapi:fetch
1158
+ params:
1159
+ - store={ true | file_name } – save as JSON (optional)
1160
+ - resource=resource_name – get only for this one (optional)
1161
+ ```
1162
+
1163
+ #### OpenApi/Swagger Validation
1164
+
1165
+ **requires**: `npm` and `swagger-cli` to be installed
1166
+
1167
+
1168
+ ```
1169
+ rake oapi:validate
1170
+ params:
1171
+ - resource=resource_name – get only for this one (optional)
1172
+ ```
1173
+
1174
+
1008
1175
  ## Contributing to grape-swagger
1009
1176
 
1010
1177
  See [CONTRIBUTING](CONTRIBUTING.md).
data/lib/grape-swagger.rb CHANGED
@@ -18,6 +18,7 @@ module GrapeSwagger
18
18
  @model_parsers ||= GrapeSwagger::ModelParsers.new
19
19
  end
20
20
  end
21
+ autoload :Rake, 'grape-swagger/rake/oapi_tasks'
21
22
  end
22
23
 
23
24
  module Grape
@@ -31,6 +32,11 @@ module Grape
31
32
  version_for(options)
32
33
  options = { target_class: self }.merge(options)
33
34
  @target_class = options[:target_class]
35
+ auth_wrapper = options[:endpoint_auth_wrapper]
36
+
37
+ if auth_wrapper && auth_wrapper.method_defined?(:before) && !middleware.flatten.include?(auth_wrapper)
38
+ use auth_wrapper
39
+ end
34
40
 
35
41
  documentation_class.setup(options)
36
42
  mount(documentation_class)
@@ -80,7 +86,9 @@ module Grape
80
86
  end
81
87
  # use the full namespace here (not the latest level only)
82
88
  # and strip leading slash
83
- @target_class.combined_namespaces[endpoint.namespace.sub(/^\//, '')] = ns if ns
89
+ mount_path = (endpoint.namespace_stackable(:mount_path) || []).join('/')
90
+ full_namespace = (mount_path + endpoint.namespace).sub(/\/{2,}/, '/').sub(/^\//, '')
91
+ @target_class.combined_namespaces[full_namespace] = ns if ns
84
92
 
85
93
  combine_namespaces(endpoint.options[:app]) if endpoint.options[:app]
86
94
  end
@@ -31,37 +31,38 @@ module GrapeSwagger
31
31
  # options could be set on #add_swagger_documentation call,
32
32
  # for available options see #defaults
33
33
  target_class = options[:target_class]
34
- api_doc = options[:api_documentation].dup
35
- specific_api_doc = options[:specific_api_documentation].dup
34
+ guard = options[:swagger_endpoint_guard]
35
+ formatter = options[:format]
36
36
 
37
37
  class_variables_from(options)
38
38
 
39
39
  [:format, :default_format, :default_error_formatter].each do |method|
40
- send(method, options[:format])
41
- end if options[:format]
42
- # getting of the whole swagger2.0 spec file
43
- desc api_doc.delete(:desc), api_doc
44
- get mount_path do
45
- header['Access-Control-Allow-Origin'] = '*'
46
- header['Access-Control-Request-Method'] = '*'
40
+ send(method, formatter)
41
+ end if formatter
47
42
 
48
- output = swagger_object(
43
+ send(guard.split.first.to_sym, *guard.split(/[\s,]+/).drop(1)) unless guard.nil?
44
+
45
+ output_path_definitions = proc do |combi_routes, endpoint|
46
+ output = endpoint.swagger_object(
49
47
  target_class,
50
- request,
48
+ endpoint.request,
51
49
  options
52
50
  )
53
51
 
54
- target_routes = target_class.combined_namespace_routes
55
- paths, definitions = path_and_definition_objects(target_routes, options)
52
+ paths, definitions = endpoint.path_and_definition_objects(combi_routes, options)
56
53
  output[:paths] = paths unless paths.blank?
57
54
  output[:definitions] = definitions unless definitions.blank?
58
55
 
59
56
  output
60
57
  end
61
58
 
62
- # getting of a specific/named route of the swagger2.0 spec file
63
- desc specific_api_doc.delete(:desc), { params:
64
- specific_api_doc.delete(:params) || {} }.merge(specific_api_doc)
59
+ get mount_path do
60
+ header['Access-Control-Allow-Origin'] = '*'
61
+ header['Access-Control-Request-Method'] = '*'
62
+
63
+ output_path_definitions.call(target_class.combined_namespace_routes, self)
64
+ end
65
+
65
66
  params do
66
67
  requires :name, type: String, desc: 'Resource name of mounted API'
67
68
  optional :locale, type: Symbol, desc: 'Locale of API documentation'
@@ -72,18 +73,7 @@ module GrapeSwagger
72
73
  combined_routes = target_class.combined_namespace_routes[params[:name]]
73
74
  error!({ error: 'named resource not exist' }, 400) if combined_routes.nil?
74
75
 
75
- output = swagger_object(
76
- target_class,
77
- request,
78
- options
79
- )
80
-
81
- target_routes = { params[:name] => combined_routes }
82
- paths, definitions = path_and_definition_objects(target_routes, options)
83
- output[:paths] = paths unless paths.blank?
84
- output[:definitions] = definitions unless definitions.blank?
85
-
86
- output
76
+ output_path_definitions.call({ params[:name] => combined_routes }, self)
87
77
  end
88
78
  end
89
79
 
@@ -104,7 +94,10 @@ module GrapeSwagger
104
94
  authorizations: nil,
105
95
  security_definitions: nil,
106
96
  api_documentation: { desc: 'Swagger compatible API description' },
107
- specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
97
+ specific_api_documentation: { desc: 'Swagger compatible API description for specific API' },
98
+ endpoint_auth_wrapper: nil,
99
+ swagger_endpoint_guard: nil,
100
+ oauth_token: nil
108
101
  }
109
102
  end
110
103
 
@@ -66,6 +66,7 @@ module GrapeSwagger
66
66
  param_type = value_type[:documentation][:param_type]
67
67
  doc_type = value_type[:documentation][:type]
68
68
  type = GrapeSwagger::DocMethods::DataType.mapping(doc_type) if doc_type && !DataType.request_primitive?(doc_type)
69
+ collection_format = value_type[:documentation][:collectionFormat]
69
70
  end
70
71
 
71
72
  array_items = {
@@ -76,6 +77,7 @@ module GrapeSwagger
76
77
  @parsed_param[:in] = param_type || 'formData'
77
78
  @parsed_param[:items] = array_items
78
79
  @parsed_param[:type] = 'array'
80
+ @parsed_param[:collectionFormat] = collection_format if %w(csv ssv tsv pipes multi).include?(collection_format)
79
81
  end
80
82
  end
81
83
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support'
2
4
  require 'active_support/core_ext/string/inflections.rb'
3
5
 
@@ -84,7 +86,7 @@ module Grape
84
86
  # path object
85
87
  def path_item(routes, options)
86
88
  routes.each do |route|
87
- next if hidden?(route)
89
+ next if hidden?(route, options)
88
90
 
89
91
  @item, path = GrapeSwagger::DocMethods::PathString.build(route, options)
90
92
  @entity = route.entity || route.options[:success]
@@ -108,6 +110,7 @@ module Grape
108
110
  method[:produces] = produces_object(route, options[:produces] || options[:format])
109
111
  method[:consumes] = consumes_object(route, options[:format])
110
112
  method[:parameters] = params_object(route)
113
+ method[:security] = security_object(route)
111
114
  method[:responses] = response_object(route, options[:markdown])
112
115
  method[:tags] = tag_object(route)
113
116
  method[:operationId] = GrapeSwagger::DocMethods::OperationId.build(route, path)
@@ -116,6 +119,10 @@ module Grape
116
119
  [route.request_method.downcase.to_sym, method]
117
120
  end
118
121
 
122
+ def security_object(route)
123
+ route.options[:security] if route.options.key?(:security)
124
+ end
125
+
119
126
  def summary_object(route)
120
127
  summary = route.options[:desc] if route.options.key?(:desc)
121
128
  summary = route.description if route.description.present?
@@ -215,15 +222,17 @@ module Grape
215
222
  declared_params = route.settings[:declared_params] if route.settings[:declared_params].present?
216
223
  required, exposed = route.params.partition { |x| x.first.is_a? String }
217
224
  required = GrapeSwagger::DocMethods::Headers.parse(route) + required unless route.headers.nil?
225
+
218
226
  default_type(required)
219
227
  default_type(exposed)
220
228
 
221
- unless declared_params.nil? && route.headers.nil?
222
- request_params = parse_request_params(required)
223
- end
229
+ request_params = unless declared_params.nil? && route.headers.nil?
230
+ parse_request_params(required)
231
+ end || {}
232
+
233
+ request_params = route.params.merge(request_params) if route.params.present? && !route.settings[:declared_params].present?
224
234
 
225
- return route.params if route.params.present? && !route.settings[:declared_params].present?
226
- request_params || {}
235
+ request_params
227
236
  end
228
237
 
229
238
  def default_type(params)
@@ -279,13 +288,23 @@ module Grape
279
288
  end
280
289
 
281
290
  def model_name(name)
282
- name.respond_to?(:name) ? name.name.demodulize.camelize : name.split('::').last
291
+ if name.respond_to?(:entity_name)
292
+ name.entity_name
293
+ elsif name.to_s.end_with?('Entity', 'Entities')
294
+ length = 0
295
+ name.to_s.split('::')[0..-2].reverse.take_while do |x|
296
+ length += x.length
297
+ length < 42
298
+ end.reverse.join
299
+ else
300
+ name.name.demodulize.camelize
301
+ end
283
302
  end
284
303
 
285
- def hidden?(route)
304
+ def hidden?(route, options)
286
305
  route_hidden = route.options[:hidden]
287
- route_hidden = route_hidden.call if route_hidden.is_a?(Proc)
288
- route_hidden
306
+ return route_hidden unless route_hidden.is_a?(Proc)
307
+ options[:oauth_token] ? route_hidden.call(send(options[:oauth_token].to_sym)) : route_hidden.call
289
308
  end
290
309
 
291
310
  def public_parameter?(param)
@@ -0,0 +1,99 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'rack/test'
4
+
5
+ module GrapeSwagger
6
+ module Rake
7
+ class OapiTasks < ::Rake::TaskLib
8
+ include Rack::Test::Methods
9
+
10
+ attr_reader :oapi
11
+ attr_reader :api_class
12
+
13
+ def initialize(api_class)
14
+ super()
15
+
16
+ @api_class = api_class
17
+ define_tasks
18
+ end
19
+
20
+ private
21
+
22
+ def define_tasks
23
+ namespace :oapi do
24
+ fetch
25
+ validate
26
+ end
27
+ end
28
+
29
+ # tasks
30
+ #
31
+ # get swagger/OpenAPI documentation
32
+ def fetch
33
+ desc 'generates OpenApi documentation …
34
+ params (usage: key=value):
35
+ store – save as JSON file, default: false (optional)
36
+ resource - if given only for that it would be generated (optional)'
37
+ task fetch: :environment do
38
+ make_request
39
+
40
+ save_to_file? ? File.write(file, @oapi) : $stdout.print(@oapi)
41
+ end
42
+ end
43
+
44
+ # validates swagger/OpenAPI documentation
45
+ def validate
46
+ desc 'validates the generated OpenApi file …
47
+ params (usage: key=value):
48
+ resource - if given only for that it would be generated (optional)'
49
+ task validate: :environment do
50
+ ENV['store'] = 'true'
51
+ ::Rake::Task['oapi:fetch'].invoke
52
+ exit if error?
53
+
54
+ output = system "swagger validate #{file}"
55
+
56
+ $stdout.puts 'install swagger-cli with `npm install swagger-cli -g`' if output.nil?
57
+ FileUtils.rm(file)
58
+ end
59
+ end
60
+
61
+ # helper methods
62
+ #
63
+ def make_request
64
+ get url_for
65
+ last_response
66
+ @oapi = JSON.pretty_generate(
67
+ JSON.parse(
68
+ last_response.body, symolize_names: true
69
+ )
70
+ ) + "\n"
71
+ end
72
+
73
+ def url_for
74
+ oapi_route = api_class.routes[-2]
75
+ path = oapi_route.path.sub(/\(\.\w+\)$/, '').sub(/\(\.:\w+\)$/, '')
76
+ path.sub!(':version', oapi_route.version.to_s)
77
+
78
+ [path, ENV['resource']].join('/').chomp('/')
79
+ end
80
+
81
+ def save_to_file?
82
+ ENV['store'].present? && !error?
83
+ end
84
+
85
+ def error?
86
+ JSON.parse(@oapi).keys.first == 'error'
87
+ end
88
+
89
+ def file
90
+ name = ENV['store'] == 'true' || ENV['store'].blank? ? 'swagger_doc.json' : ENV['store']
91
+ File.join(Dir.getwd, name)
92
+ end
93
+
94
+ def app
95
+ api_class.new
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,3 @@
1
1
  module GrapeSwagger
2
- VERSION = '0.23.0'.freeze
2
+ VERSION = '0.24.0'.freeze
3
3
  end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'grape-entity'
3
+ require 'grape-swagger-entity'
4
+
5
+ describe 'definition names' do
6
+ before :all do
7
+ module TestDefinition
8
+ module DummyEntities
9
+ module WithVeryLongName
10
+ module AnotherGroupingModule
11
+ class Class1
12
+ class Entity < Grape::Entity
13
+ expose :one_thing
14
+ end
15
+ end
16
+
17
+ class Class2
18
+ class Entity < Grape::Entity
19
+ expose :another_thing
20
+
21
+ def self.entity_name
22
+ 'FooKlass'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ class NameApi < Grape::API
31
+ add_swagger_documentation models: [
32
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class1::Entity,
33
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class2::Entity
34
+ ]
35
+ end
36
+ end
37
+ end
38
+
39
+ let(:app) { TestDefinition::NameApi }
40
+
41
+ subject do
42
+ get '/swagger_doc'
43
+ JSON.parse(last_response.body)['definitions']
44
+ end
45
+
46
+ specify { expect(subject).to include 'AnotherGroupingModuleClass1' }
47
+ specify { expect(subject).to include 'FooKlass' }
48
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GrapeSwagger::Rake::OapiTasks do
4
+ let(:api) do
5
+ item = Class.new(Grape::API) do
6
+ version 'v1', using: :path
7
+
8
+ resource :item do
9
+ get '/'
10
+ end
11
+
12
+ resource :otherItem do
13
+ get '/'
14
+ end
15
+ end
16
+
17
+ Class.new(Grape::API) do
18
+ prefix :api
19
+ mount item
20
+ add_swagger_documentation add_version: true
21
+ end
22
+ end
23
+
24
+ subject { described_class.new(api) }
25
+
26
+ describe '#make_request' do
27
+ describe 'complete documentation' do
28
+ before do
29
+ subject.send(:make_request)
30
+ end
31
+
32
+ describe 'not storing' do
33
+ it 'has no error' do
34
+ expect(subject.send(:error?)).to be false
35
+ end
36
+
37
+ it 'does not allow to save' do
38
+ expect(subject.send(:save_to_file?)).to be false
39
+ end
40
+
41
+ it 'requests doc url' do
42
+ expect(subject.send(:url_for)).to eql '/api/swagger_doc'
43
+ end
44
+ end
45
+
46
+ describe 'store it' do
47
+ before { ENV['store'] = 'true' }
48
+ after { ENV.delete('store') }
49
+
50
+ it 'allows to save' do
51
+ expect(subject.send(:save_to_file?)).to be true
52
+ end
53
+ end
54
+ end
55
+
56
+ describe 'documentation for resource' do
57
+ before do
58
+ ENV['resource'] = resource
59
+ subject.send(:make_request)
60
+ end
61
+
62
+ let(:response) { JSON.parse(subject.send(:make_request)) }
63
+
64
+ after { ENV.delete('resource') }
65
+
66
+ describe 'valid name' do
67
+ let(:resource) { 'otherItem' }
68
+
69
+ it 'has no error' do
70
+ expect(subject.send(:error?)).to be false
71
+ end
72
+
73
+ it 'requests doc url' do
74
+ expect(subject.send(:url_for)).to eql "/api/swagger_doc/#{resource}"
75
+ end
76
+
77
+ it 'has only one resource path' do
78
+ expect(response['paths'].length).to eql 1
79
+ expect(response['paths'].keys.first).to end_with resource
80
+ end
81
+ end
82
+
83
+ describe 'wrong name' do
84
+ let(:resource) { 'foo' }
85
+
86
+ it 'has error' do
87
+ expect(subject.send(:error?)).to be true
88
+ end
89
+ end
90
+
91
+ describe 'empty name' do
92
+ let(:resource) { nil }
93
+
94
+ it 'has no error' do
95
+ expect(subject.send(:error?)).to be false
96
+ end
97
+
98
+ it 'returns complete doc' do
99
+ expect(response['paths'].length).to eql 2
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ describe '#file' do
106
+ describe 'no store given' do
107
+ it 'returns swagger_doc.json' do
108
+ expect(subject.send(:file)).to end_with 'swagger_doc.json'
109
+ end
110
+ end
111
+
112
+ describe 'store given' do
113
+ after { ENV.delete('store') }
114
+
115
+ describe 'boolean true' do
116
+ before { ENV['store'] = 'true' }
117
+
118
+ it 'returns swagger_doc.json' do
119
+ expect(subject.send(:file)).to end_with 'swagger_doc.json'
120
+ end
121
+ end
122
+
123
+ describe 'name given' do
124
+ let(:name) { 'oapi_doc.json' }
125
+ before { ENV['store'] = name }
126
+
127
+ it 'returns swagger_doc.json' do
128
+ expect(subject.send(:file)).to end_with name
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -39,6 +39,8 @@ describe 'body parameter definitions' do
39
39
  'body_param' => { 'type' => 'string', 'description' => 'param' },
40
40
  'body_type_as_const_param' => { 'type' => 'string', 'description' => 'string_param' }
41
41
  )
42
+
43
+ expect(subject['paths']['/endpoint']['post']['parameters'].any? { |p| p['name'] == 'XAuthToken' && p['in'] == 'header' }).to eql(true)
42
44
  end
43
45
  end
44
46
  end
@@ -2,8 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe 'details' do
4
4
  describe 'details, pass markdown with redcarpet even with nil description and detail', unless: RUBY_PLATFORM.eql?('java') do
5
- include_context 'the api entities'
6
-
7
5
  before :all do
8
6
  module TheApi
9
7
  class GfmRcDetailApi < Grape::API
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ class SampleAuth < Grape::Middleware::Base
6
+ module AuthMethods
7
+ attr_accessor :access_token
8
+
9
+ def protected_endpoint=(protected)
10
+ @protected_endpoint = protected
11
+ end
12
+
13
+ def protected_endpoint?
14
+ @protected_endpoint || false
15
+ end
16
+
17
+ def access_token
18
+ @_access_token
19
+ end
20
+
21
+ def access_token=(token)
22
+ @_access_token = token
23
+ end
24
+ end
25
+
26
+ def context
27
+ env['api.endpoint']
28
+ end
29
+
30
+ def before
31
+ context.extend(SampleAuth::AuthMethods)
32
+ context.protected_endpoint = context.options[:route_options][:auth].present?
33
+
34
+ return unless context.protected_endpoint?
35
+ scopes = context.options[:route_options][:auth][:scopes].map(&:to_sym)
36
+ authorize!(*scopes) unless scopes.include? :false
37
+ context.access_token = env['HTTP_AUTHORIZATION']
38
+ end
39
+ end
40
+
41
+ module Extension
42
+ def sample_auth(*scopes)
43
+ description = route_setting(:description) || route_setting(:description, {})
44
+ description[:auth] = { scopes: scopes }
45
+ end
46
+
47
+ Grape::API.extend self
48
+ end
49
+
50
+ describe 'a guarded api endpoint' do
51
+ before :all do
52
+ class GuardedMountedApi < Grape::API
53
+ access_token_valid = proc { |token = nil| token.nil? || token != '12345' }
54
+
55
+ desc 'Show endpoint if authenticated', hidden: access_token_valid
56
+ get '/auth' do
57
+ { foo: 'bar' }
58
+ end
59
+ end
60
+
61
+ class GuardedApi < Grape::API
62
+ mount GuardedMountedApi
63
+ add_swagger_documentation endpoint_auth_wrapper: SampleAuth,
64
+ swagger_endpoint_guard: 'sample_auth false',
65
+ oauth_token: 'access_token'
66
+ end
67
+ end
68
+
69
+ def app
70
+ GuardedApi
71
+ end
72
+
73
+ context 'when a correct token is passed with the request' do
74
+ subject do
75
+ get '/swagger_doc.json', {}, 'HTTP_AUTHORIZATION' => '12345'
76
+ JSON.parse(last_response.body)
77
+ end
78
+
79
+ it 'retrieves swagger-documentation for the endpoint' do
80
+ expect(subject).to eq(
81
+ 'info' => { 'title' => 'API title', 'version' => '0.0.1' },
82
+ 'swagger' => '2.0',
83
+ 'produces' => ['application/xml', 'application/json', 'application/octet-stream', 'text/plain'],
84
+ 'host' => 'example.org',
85
+ 'paths' => {
86
+ '/auth' => {
87
+ 'get' => {
88
+ 'summary' => 'Show endpoint if authenticated',
89
+ 'description' => 'Show endpoint if authenticated',
90
+ 'produces' => ['application/json'],
91
+ 'tags' => ['auth'],
92
+ 'operationId' => 'getAuth',
93
+ 'responses' => { '200' => { 'description' => 'Show endpoint if authenticated' } }
94
+ }
95
+ }
96
+ }
97
+ )
98
+ end
99
+ end
100
+
101
+ context 'when a bad token is passed with the request' do
102
+ subject do
103
+ get '/swagger_doc.json', {}, 'HTTP_AUTHORIZATION' => '123456'
104
+ JSON.parse(last_response.body)
105
+ end
106
+
107
+ it 'does not retrieve swagger-documentation for the endpoint - only the info_object' do
108
+ expect(subject).to eq(
109
+ 'info' => { 'title' => 'API title', 'version' => '0.0.1' },
110
+ 'swagger' => '2.0',
111
+ 'produces' => ['application/xml', 'application/json', 'application/octet-stream', 'text/plain'],
112
+ 'host' => 'example.org'
113
+ )
114
+ end
115
+ end
116
+ end
@@ -1,8 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'host in the swagger_doc' do
4
- include_context 'the api entities'
5
-
6
4
  before :all do
7
5
  module TheApi
8
6
  class EmptyApi < Grape::API
@@ -64,4 +64,56 @@ describe 'namespace' do
64
64
  expect(subject['description']).to eql('Description for aspace')
65
65
  end
66
66
  end
67
+
68
+ context 'mounted under a route' do
69
+ def app
70
+ namespaced_api = Class.new(Grape::API) do
71
+ namespace :bspace do
72
+ get '/', desc: 'Description for aspace'
73
+ end
74
+ end
75
+
76
+ Class.new(Grape::API) do
77
+ mount namespaced_api => '/mounted'
78
+ add_swagger_documentation format: :json
79
+ end
80
+ end
81
+
82
+ subject do
83
+ get '/swagger_doc'
84
+ JSON.parse(last_response.body)['paths']['/mounted/bspace']['get']
85
+ end
86
+
87
+ it 'shows the namespace description in the json spec' do
88
+ expect(subject['description']).to eql('Description for aspace')
89
+ end
90
+ end
91
+
92
+ context 'arbitrary mounting' do
93
+ def app
94
+ inner_namespaced_api = Class.new(Grape::API) do
95
+ namespace :bspace do
96
+ get '/', desc: 'Description for aspace'
97
+ end
98
+ end
99
+
100
+ outer_namespaced_api = Class.new(Grape::API) do
101
+ mount inner_namespaced_api => '/mounted'
102
+ end
103
+
104
+ Class.new(Grape::API) do
105
+ mount outer_namespaced_api => '/'
106
+ add_swagger_documentation format: :json
107
+ end
108
+ end
109
+
110
+ subject do
111
+ get '/swagger_doc'
112
+ JSON.parse(last_response.body)['paths']['/mounted/bspace']['get']
113
+ end
114
+
115
+ it 'shows the namespace description in the json spec' do
116
+ expect(subject['description']).to eql('Description for aspace')
117
+ end
118
+ end
67
119
  end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Group Array Params, using collection format' do
4
+ def app
5
+ Class.new(Grape::API) do
6
+ format :json
7
+
8
+ params do
9
+ optional :array_of_strings, type: Array[String], desc: 'array in csv collection format'
10
+ end
11
+
12
+ get '/array_of_strings_without_collection_format' do
13
+ { 'declared_params' => declared(params) }
14
+ end
15
+
16
+ params do
17
+ optional :array_of_strings, type: Array[String], desc: 'array in multi collection format', documentation: { collectionFormat: 'multi' }
18
+ end
19
+
20
+ get '/array_of_strings_multi_collection_format' do
21
+ { 'declared_params' => declared(params) }
22
+ end
23
+
24
+ params do
25
+ optional :array_of_strings, type: Array[String], documentation: { collectionFormat: 'foo' }
26
+ end
27
+
28
+ get '/array_of_strings_invalid_collection_format' do
29
+ { 'declared_params' => declared(params) }
30
+ end
31
+
32
+ add_swagger_documentation
33
+ end
34
+ end
35
+
36
+ describe 'documentation for array parameter in default csv collectionFormat' do
37
+ subject do
38
+ get '/swagger_doc/array_of_strings_without_collection_format'
39
+ JSON.parse(last_response.body)
40
+ end
41
+
42
+ specify do
43
+ expect(subject['paths']['/array_of_strings_without_collection_format']['get']['parameters']).to eql(
44
+ [
45
+ { 'in' => 'formData', 'name' => 'array_of_strings', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => false, 'description' => 'array in csv collection format' }
46
+ ]
47
+ )
48
+ end
49
+ end
50
+
51
+ describe 'documentation for array parameters in multi collectionFormat set from documentation' do
52
+ subject do
53
+ get '/swagger_doc/array_of_strings_multi_collection_format'
54
+ JSON.parse(last_response.body)
55
+ end
56
+
57
+ specify do
58
+ expect(subject['paths']['/array_of_strings_multi_collection_format']['get']['parameters']).to eql(
59
+ [
60
+ { 'in' => 'formData', 'name' => 'array_of_strings', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => false, 'collectionFormat' => 'multi', 'description' => 'array in multi collection format' }
61
+ ]
62
+ )
63
+ end
64
+ end
65
+
66
+ describe 'documentation for array parameters with collectionFormat set to invalid option' do
67
+ subject do
68
+ get '/swagger_doc/array_of_strings_invalid_collection_format'
69
+ JSON.parse(last_response.body)
70
+ end
71
+
72
+ specify do
73
+ expect(subject['paths']['/array_of_strings_invalid_collection_format']['get']['parameters']).to eql(
74
+ [
75
+ { 'in' => 'formData', 'name' => 'array_of_strings', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => false }
76
+ ]
77
+ )
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'security requirement on endpoint method' do
4
+ def app
5
+ Class.new(Grape::API) do
6
+ desc 'Endpoint with security requirement', security: [oauth_pets: ['read:pets', 'write:pets']]
7
+ get '/with_security' do
8
+ { foo: 'bar' }
9
+ end
10
+
11
+ add_swagger_documentation
12
+ end
13
+ end
14
+
15
+ subject do
16
+ get '/swagger_doc.json'
17
+ JSON.parse(last_response.body)
18
+ end
19
+
20
+ it 'defines the security requirement on the endpoint method' do
21
+ expect(subject['paths']['/with_security']['get']['security']).to eql ['oauth_pets' => ['read:pets', 'write:pets']]
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-swagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.0
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Vandecasteele
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-05 00:00:00.000000000 Z
11
+ date: 2016-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grape
@@ -255,13 +255,16 @@ files:
255
255
  - lib/grape-swagger/markdown/kramdown_adapter.rb
256
256
  - lib/grape-swagger/markdown/redcarpet_adapter.rb
257
257
  - lib/grape-swagger/model_parsers.rb
258
+ - lib/grape-swagger/rake/oapi_tasks.rb
258
259
  - lib/grape-swagger/version.rb
259
260
  - spec/issues/403_versions_spec.rb
261
+ - spec/issues/430_entity_definitions_spec.rb
260
262
  - spec/lib/data_type_spec.rb
261
263
  - spec/lib/endpoint_spec.rb
262
264
  - spec/lib/extensions_spec.rb
263
265
  - spec/lib/model_parsers_spec.rb
264
266
  - spec/lib/move_params_spec.rb
267
+ - spec/lib/oapi_tasks_spec.rb
265
268
  - spec/lib/operation_id_spec.rb
266
269
  - spec/lib/optional_object_spec.rb
267
270
  - spec/lib/path_string_spec.rb
@@ -304,6 +307,7 @@ files:
304
307
  - spec/swagger_v2/float_api_spec.rb
305
308
  - spec/swagger_v2/form_params_spec.rb
306
309
  - spec/swagger_v2/grape-swagger_spec.rb
310
+ - spec/swagger_v2/guarded_endpoint_spec.rb
307
311
  - spec/swagger_v2/hide_api_spec.rb
308
312
  - spec/swagger_v2/host.rb
309
313
  - spec/swagger_v2/mounted_target_class_spec.rb
@@ -314,10 +318,12 @@ files:
314
318
  - spec/swagger_v2/param_multi_type_spec.rb
315
319
  - spec/swagger_v2/param_type_spec.rb
316
320
  - spec/swagger_v2/param_values_spec.rb
321
+ - spec/swagger_v2/params_array_collection_fromat_spec.rb
317
322
  - spec/swagger_v2/params_array_spec.rb
318
323
  - spec/swagger_v2/params_hash_spec.rb
319
324
  - spec/swagger_v2/params_nested_spec.rb
320
325
  - spec/swagger_v2/reference_entity.rb
326
+ - spec/swagger_v2/security_requirement_spec.rb
321
327
  - spec/swagger_v2/simple_mounted_api_spec.rb
322
328
  - spec/version_spec.rb
323
329
  homepage: https://github.com/ruby-grape/grape-swagger
@@ -340,18 +346,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
340
346
  version: '0'
341
347
  requirements: []
342
348
  rubyforge_project:
343
- rubygems_version: 2.6.4
349
+ rubygems_version: 2.6.6
344
350
  signing_key:
345
351
  specification_version: 4
346
352
  summary: A simple way to add auto generated documentation to your Grape API that can
347
353
  be displayed with Swagger.
348
354
  test_files:
349
355
  - spec/issues/403_versions_spec.rb
356
+ - spec/issues/430_entity_definitions_spec.rb
350
357
  - spec/lib/data_type_spec.rb
351
358
  - spec/lib/endpoint_spec.rb
352
359
  - spec/lib/extensions_spec.rb
353
360
  - spec/lib/model_parsers_spec.rb
354
361
  - spec/lib/move_params_spec.rb
362
+ - spec/lib/oapi_tasks_spec.rb
355
363
  - spec/lib/operation_id_spec.rb
356
364
  - spec/lib/optional_object_spec.rb
357
365
  - spec/lib/path_string_spec.rb
@@ -394,6 +402,7 @@ test_files:
394
402
  - spec/swagger_v2/float_api_spec.rb
395
403
  - spec/swagger_v2/form_params_spec.rb
396
404
  - spec/swagger_v2/grape-swagger_spec.rb
405
+ - spec/swagger_v2/guarded_endpoint_spec.rb
397
406
  - spec/swagger_v2/hide_api_spec.rb
398
407
  - spec/swagger_v2/host.rb
399
408
  - spec/swagger_v2/mounted_target_class_spec.rb
@@ -404,9 +413,11 @@ test_files:
404
413
  - spec/swagger_v2/param_multi_type_spec.rb
405
414
  - spec/swagger_v2/param_type_spec.rb
406
415
  - spec/swagger_v2/param_values_spec.rb
416
+ - spec/swagger_v2/params_array_collection_fromat_spec.rb
407
417
  - spec/swagger_v2/params_array_spec.rb
408
418
  - spec/swagger_v2/params_hash_spec.rb
409
419
  - spec/swagger_v2/params_nested_spec.rb
410
420
  - spec/swagger_v2/reference_entity.rb
421
+ - spec/swagger_v2/security_requirement_spec.rb
411
422
  - spec/swagger_v2/simple_mounted_api_spec.rb
412
423
  - spec/version_spec.rb