grape-swagger 0.20.3 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +10 -10
  3. data/.travis.yml +8 -3
  4. data/CHANGELOG.md +27 -0
  5. data/Gemfile +2 -0
  6. data/README.md +111 -12
  7. data/UPGRADING.md +9 -0
  8. data/grape-swagger.gemspec +1 -3
  9. data/lib/grape-swagger.rb +8 -1
  10. data/lib/grape-swagger/doc_methods/optional_object.rb +14 -2
  11. data/lib/grape-swagger/doc_methods/parse_params.rb +3 -4
  12. data/lib/grape-swagger/doc_methods/path_string.rb +4 -3
  13. data/lib/grape-swagger/endpoint.rb +25 -55
  14. data/lib/grape-swagger/errors.rb +3 -0
  15. data/lib/grape-swagger/grape/route.rb +2 -1
  16. data/lib/grape-swagger/model_parsers.rb +33 -0
  17. data/lib/grape-swagger/version.rb +1 -1
  18. data/spec/issues/403_versions_spec.rb +20 -4
  19. data/spec/lib/model_parsers_spec.rb +102 -0
  20. data/spec/lib/optional_object_spec.rb +15 -11
  21. data/spec/lib/path_string_spec.rb +72 -18
  22. data/spec/lib/produces_consumes_spec.rb +10 -5
  23. data/spec/spec_helper.rb +4 -2
  24. data/spec/support/empty_model_parser.rb +20 -0
  25. data/spec/support/mock_parser.rb +22 -0
  26. data/spec/support/model_parsers/entity_parser.rb +325 -0
  27. data/spec/support/{api_swagger_v2_result.rb → model_parsers/mock_parser.rb} +186 -60
  28. data/spec/support/model_parsers/representable_parser.rb +394 -0
  29. data/spec/support/the_paths_definitions.rb +7 -3
  30. data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +5 -4
  31. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +3 -3
  32. data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +1 -1
  33. data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +1 -1
  34. data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +5 -3
  35. data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +1 -1
  36. data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +1 -1
  37. data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +25 -14
  38. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +22 -12
  39. data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +30 -18
  40. data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +6 -3
  41. data/spec/swagger_v2/api_swagger_v2_response_spec.rb +13 -40
  42. data/spec/swagger_v2/api_swagger_v2_spec.rb +4 -2
  43. data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +6 -36
  44. data/spec/swagger_v2/default_api_spec.rb +10 -2
  45. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +30 -0
  46. data/spec/swagger_v2/errors_spec.rb +75 -0
  47. data/spec/swagger_v2/hide_api_spec.rb +22 -4
  48. data/spec/swagger_v2/mounted_target_class_spec.rb +6 -2
  49. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +6 -3
  50. data/spec/swagger_v2/namespace_tags_spec.rb +6 -3
  51. data/spec/swagger_v2/params_array_spec.rb +4 -2
  52. data/spec/swagger_v2/params_hash_spec.rb +4 -2
  53. data/spec/swagger_v2/params_nested_spec.rb +4 -2
  54. data/spec/swagger_v2/simple_mounted_api_spec.rb +66 -24
  55. metadata +23 -40
  56. data/spec/support/the_api_entities.rb +0 -50
  57. data/spec/swagger_v2/response_model_spec.rb +0 -208
@@ -28,7 +28,8 @@ describe GrapeSwagger::DocMethods::ProducesConsumes do
28
28
  'application/json',
29
29
  'application/octet-stream',
30
30
  'text/plain'
31
- ])
31
+ ]
32
+ )
32
33
  end
33
34
  end
34
35
  end
@@ -59,7 +60,8 @@ describe GrapeSwagger::DocMethods::ProducesConsumes do
59
60
  'application/json',
60
61
  'application/octet-stream',
61
62
  'text/plain'
62
- ])
63
+ ]
64
+ )
63
65
  end
64
66
  end
65
67
  end
@@ -82,7 +84,8 @@ describe GrapeSwagger::DocMethods::ProducesConsumes do
82
84
  'application/json',
83
85
  'application/octet-stream',
84
86
  'text/plain'
85
- ])
87
+ ]
88
+ )
86
89
  end
87
90
 
88
91
  subject do
@@ -93,7 +96,8 @@ describe GrapeSwagger::DocMethods::ProducesConsumes do
93
96
  'application/json',
94
97
  'application/octet-stream',
95
98
  :txt
96
- ])
99
+ ]
100
+ )
97
101
  end
98
102
 
99
103
  specify do
@@ -103,7 +107,8 @@ describe GrapeSwagger::DocMethods::ProducesConsumes do
103
107
  'application/json',
104
108
  'application/octet-stream',
105
109
  'text/plain'
106
- ])
110
+ ]
111
+ )
107
112
  end
108
113
  end
109
114
  end
@@ -1,10 +1,12 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
2
 
3
- Dir[File.join(Dir.getwd, 'spec/support/**/*.rb')].each { |f| require f }
3
+ MODEL_PARSER = ENV.key?('MODEL_PARSER') ? ENV['MODEL_PARSER'].to_s.downcase.sub('grape-swagger-', '') : 'mock'
4
4
 
5
5
  require 'grape'
6
6
  require 'grape-swagger'
7
- require 'grape-entity'
7
+ Dir[File.join(Dir.getwd, 'spec/support/*.rb')].each { |f| require f }
8
+ require "grape-swagger/#{MODEL_PARSER}" if MODEL_PARSER != 'mock'
9
+ require File.join(Dir.getwd, "spec/support/model_parsers/#{MODEL_PARSER}_parser.rb")
8
10
 
9
11
  Bundler.setup :default, :test
10
12
 
@@ -0,0 +1,20 @@
1
+ class EmptyClass
2
+ end
3
+
4
+ module GrapeSwagger
5
+ class EmptyModelParser
6
+ attr_reader :model
7
+ attr_reader :endpoint
8
+
9
+ def initialize(model, endpoint)
10
+ @model = model
11
+ @endpoint = endpoint
12
+ end
13
+
14
+ def call
15
+ {}
16
+ end
17
+ end
18
+ end
19
+
20
+ GrapeSwagger.model_parsers.register(GrapeSwagger::EmptyModelParser, EmptyClass)
@@ -0,0 +1,22 @@
1
+ module GrapeSwagger
2
+ class MockParser
3
+ attr_reader :model
4
+ attr_reader :endpoint
5
+
6
+ def initialize(model, endpoint)
7
+ @model = model
8
+ @endpoint = endpoint
9
+ end
10
+
11
+ def call
12
+ {
13
+ mock_data: {
14
+ type: :string,
15
+ description: "it's a mock"
16
+ }
17
+ }
18
+ end
19
+ end
20
+ end
21
+
22
+ GrapeSwagger.model_parsers.register(GrapeSwagger::MockParser, OpenStruct)
@@ -0,0 +1,325 @@
1
+ RSpec.shared_context 'entity swagger example' do
2
+ before :all do
3
+ module Entities
4
+ class Something < Grape::Entity
5
+ expose :id, documentation: { type: Integer, desc: 'Identity of Something' }
6
+ expose :text, documentation: { type: String, desc: 'Content of something.' }
7
+ expose :links, documentation: { type: 'link', is_array: true }
8
+ expose :others, documentation: { type: 'text', is_array: false }
9
+ end
10
+
11
+ class EnumValues < Grape::Entity
12
+ expose :gender, documentation: { type: 'string', desc: 'Content of something.', values: %w(Male Female) }
13
+ expose :number, documentation: { type: 'integer', desc: 'Content of something.', values: [1, 2] }
14
+ end
15
+
16
+ class AliasedThing < Grape::Entity
17
+ expose :something, as: :post, using: Entities::Something, documentation: { type: 'Something', desc: 'Reference to something.' }
18
+ end
19
+
20
+ class FourthLevel < Grape::Entity
21
+ expose :text, documentation: { type: 'string' }
22
+ end
23
+
24
+ class ThirdLevel < Grape::Entity
25
+ expose :parts, using: Entities::FourthLevel, documentation: { type: 'FourthLevel' }
26
+ end
27
+
28
+ class SecondLevel < Grape::Entity
29
+ expose :parts, using: Entities::ThirdLevel, documentation: { type: 'ThirdLevel' }
30
+ end
31
+
32
+ class FirstLevel < Grape::Entity
33
+ expose :parts, using: Entities::SecondLevel, documentation: { type: 'SecondLevel' }
34
+ end
35
+
36
+ class QueryInputElement < Grape::Entity
37
+ expose :key, documentation: {
38
+ type: String, desc: 'Name of parameter', required: true
39
+ }
40
+ expose :value, documentation: {
41
+ type: String, desc: 'Value of parameter', required: true
42
+ }
43
+ end
44
+
45
+ class QueryInput < Grape::Entity
46
+ expose :elements, using: Entities::QueryInputElement, documentation: {
47
+ type: 'QueryInputElement',
48
+ desc: 'Set of configuration',
49
+ param_type: 'body',
50
+ is_array: true,
51
+ required: true
52
+ }
53
+ end
54
+
55
+ class ApiError < Grape::Entity
56
+ expose :code, documentation: { type: Integer, desc: 'status code' }
57
+ expose :message, documentation: { type: String, desc: 'error message' }
58
+ end
59
+
60
+ class SecondApiError < Grape::Entity
61
+ expose :code, documentation: { type: Integer }
62
+ expose :severity, documentation: { type: String }
63
+ expose :message, documentation: { type: String }
64
+ end
65
+
66
+ class ResponseItem < Grape::Entity
67
+ expose :id, documentation: { type: Integer }
68
+ expose :name, documentation: { type: String }
69
+ end
70
+
71
+ class OtherItem < Grape::Entity
72
+ expose :key, documentation: { type: Integer }
73
+ expose :symbol, documentation: { type: String }
74
+ end
75
+
76
+ class UseResponse < Grape::Entity
77
+ expose :description, documentation: { type: String }
78
+ expose :items, as: '$responses', using: Entities::ResponseItem, documentation: { is_array: true }
79
+ end
80
+
81
+ class UseItemResponseAsType < Grape::Entity
82
+ expose :description, documentation: { type: String }
83
+ expose :responses, documentation: { type: Entities::ResponseItem, is_array: false }
84
+ end
85
+
86
+ class UseAddress < Grape::Entity
87
+ expose :street, documentation: { type: String, desc: 'street' }
88
+ expose :postcode, documentation: { type: String, desc: 'postcode' }
89
+ expose :city, documentation: { type: String, desc: 'city' }
90
+ expose :country, documentation: { type: String, desc: 'country' }
91
+ end
92
+
93
+ class UseNestedWithAddress < Grape::Entity
94
+ expose :name, documentation: { type: String }
95
+ expose :address, using: Entities::UseAddress
96
+ end
97
+
98
+ class TypedDefinition < Grape::Entity
99
+ expose :prop_integer, documentation: { type: Integer, desc: 'prop_integer description' }
100
+ expose :prop_long, documentation: { type: Numeric, desc: 'prop_long description' }
101
+ expose :prop_float, documentation: { type: Float, desc: 'prop_float description' }
102
+ expose :prop_double, documentation: { type: BigDecimal, desc: 'prop_double description' }
103
+ expose :prop_string, documentation: { type: String, desc: 'prop_string description' }
104
+ expose :prop_symbol, documentation: { type: Symbol, desc: 'prop_symbol description' }
105
+ expose :prop_date, documentation: { type: Date, desc: 'prop_date description' }
106
+ expose :prop_date_time, documentation: { type: DateTime, desc: 'prop_date_time description' }
107
+ expose :prop_time, documentation: { type: Time, desc: 'prop_time description' }
108
+ expose :prop_password, documentation: { type: 'password', desc: 'prop_password description' }
109
+ expose :prop_email, documentation: { type: 'email', desc: 'prop_email description' }
110
+ expose :prop_boolean, documentation: { type: Virtus::Attribute::Boolean, desc: 'prop_boolean description' }
111
+ expose :prop_file, documentation: { type: File, desc: 'prop_file description' }
112
+ expose :prop_json, documentation: { type: JSON, desc: 'prop_json description' }
113
+ end
114
+
115
+ class RecursiveModel < Grape::Entity
116
+ expose :name, documentation: { type: String, desc: 'The name.' }
117
+ expose :children, using: self, documentation: { type: 'RecursiveModel', is_array: true, desc: 'The child nodes.' }
118
+ end
119
+ end
120
+ end
121
+
122
+ let(:swagger_definitions_models) do
123
+ {
124
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } } },
125
+ 'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } } },
126
+ 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } } },
127
+ 'RecursiveModel' => { 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'The name.' }, 'children' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/RecursiveModel' }, 'description' => 'The child nodes.' } } }
128
+ }
129
+ end
130
+
131
+ let(:swagger_nested_type) do
132
+ {
133
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'description' => 'This returns something' },
134
+ 'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } } },
135
+ 'UseItemResponseAsType' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, 'responses' => { '$ref' => '#/definitions/ResponseItem' } }, 'description' => 'This returns something' }
136
+ }
137
+ end
138
+
139
+ let(:swagger_entity_as_response_object) do
140
+ {
141
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'description' => 'This returns something' },
142
+ 'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } } },
143
+ 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } }, 'description' => 'This returns something' }
144
+ }
145
+ end
146
+
147
+ let(:swagger_params_as_response_object) do
148
+ {
149
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'This returns something' }
150
+ }
151
+ end
152
+
153
+ let(:swagger_typed_defintion) do
154
+ {
155
+ 'prop_boolean' => { 'description' => 'prop_boolean description', 'type' => 'boolean' },
156
+ 'prop_date' => { 'description' => 'prop_date description', 'type' => 'string', 'format' => 'date' },
157
+ 'prop_date_time' => { 'description' => 'prop_date_time description', 'type' => 'string', 'format' => 'date-time' },
158
+ 'prop_double' => { 'description' => 'prop_double description', 'type' => 'number', 'format' => 'double' },
159
+ 'prop_email' => { 'description' => 'prop_email description', 'type' => 'string', 'format' => 'email' },
160
+ 'prop_file' => { 'description' => 'prop_file description', 'type' => 'file' },
161
+ 'prop_float' => { 'description' => 'prop_float description', 'type' => 'number', 'format' => 'float' },
162
+ 'prop_integer' => { 'description' => 'prop_integer description', 'type' => 'integer', 'format' => 'int32' },
163
+ 'prop_json' => { 'description' => 'prop_json description', 'type' => 'json' },
164
+ 'prop_long' => { 'description' => 'prop_long description', 'type' => 'integer', 'format' => 'int64' },
165
+ 'prop_password' => { 'description' => 'prop_password description', 'type' => 'string', 'format' => 'password' },
166
+ 'prop_string' => { 'description' => 'prop_string description', 'type' => 'string' },
167
+ 'prop_symbol' => { 'description' => 'prop_symbol description', 'type' => 'string' },
168
+ 'prop_time' => { 'description' => 'prop_time description', 'type' => 'string', 'format' => 'date-time' }
169
+ }
170
+ end
171
+
172
+ let(:swagger_json) do
173
+ {
174
+ 'info' => {
175
+ 'title' => 'The API title to be displayed on the API homepage.',
176
+ 'description' => 'A description of the API.',
177
+ 'termsOfServiceUrl' => 'www.The-URL-of-the-terms-and-service.com',
178
+ 'contact' => { 'name' => 'Contact name', 'email' => 'Contact@email.com', 'url' => 'Contact URL' },
179
+ 'license' => { 'name' => 'The name of the license.', 'url' => 'www.The-URL-of-the-license.org' },
180
+ 'version' => '0.0.1'
181
+ },
182
+ 'swagger' => '2.0',
183
+ 'produces' => ['application/json'],
184
+ 'host' => 'example.org',
185
+ 'basePath' => '/api',
186
+ 'tags' => [
187
+ { 'name' => 'other_thing', 'description' => 'Operations about other_things' },
188
+ { 'name' => 'thing', 'description' => 'Operations about things' },
189
+ { 'name' => 'thing2', 'description' => 'Operations about thing2s' },
190
+ { 'name' => 'dummy', 'description' => 'Operations about dummies' }
191
+ ],
192
+ 'paths' => {
193
+ '/v3/other_thing/{elements}' => {
194
+ 'get' => {
195
+ 'summary' => 'nested route inside namespace',
196
+ 'description' => 'nested route inside namespace',
197
+ 'produces' => ['application/json'],
198
+ 'parameters' => [{ 'in' => 'body', 'name' => 'elements', 'description' => 'Set of configuration', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => true }],
199
+ 'responses' => { '200' => { 'description' => 'nested route inside namespace', 'schema' => { '$ref' => '#/definitions/QueryInput' } } },
200
+ 'tags' => ['other_thing'],
201
+ 'operationId' => 'getV3OtherThingElements',
202
+ 'x-amazon-apigateway-auth' => { 'type' => 'none' },
203
+ 'x-amazon-apigateway-integration' => { 'type' => 'aws', 'uri' => 'foo_bar_uri', 'httpMethod' => 'get' }
204
+ }
205
+ },
206
+ '/thing' => {
207
+ 'get' => {
208
+ 'summary' => 'This gets Things.',
209
+ 'description' => 'This gets Things.',
210
+ 'produces' => ['application/json'],
211
+ 'parameters' => [
212
+ { 'in' => 'query', 'name' => 'id', 'description' => 'Identity of Something', 'type' => 'integer', 'format' => 'int32', 'required' => false },
213
+ { 'in' => 'query', 'name' => 'text', 'description' => 'Content of something.', 'type' => 'string', 'required' => false },
214
+ { 'in' => 'formData', 'name' => 'links', 'type' => 'array', 'items' => { 'type' => 'link' }, 'required' => false },
215
+ { 'in' => 'query', 'name' => 'others', 'type' => 'text', 'required' => false }
216
+ ],
217
+ 'responses' => { '200' => { 'description' => 'This gets Things.' }, '401' => { 'description' => 'Unauthorized', 'schema' => { '$ref' => '#/definitions/ApiError' } } },
218
+ 'tags' => ['thing'],
219
+ 'operationId' => 'getThing'
220
+ },
221
+ 'post' => {
222
+ 'summary' => 'This creates Thing.',
223
+ 'description' => 'This creates Thing.',
224
+ 'produces' => ['application/json'],
225
+ 'consumes' => ['application/json'],
226
+ 'parameters' => [
227
+ { 'in' => 'formData', 'name' => 'text', 'description' => 'Content of something.', 'type' => 'string', 'required' => true },
228
+ { 'in' => 'formData', 'name' => 'links', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => true }
229
+ ],
230
+ 'responses' => { '201' => { 'description' => 'This creates Thing.', 'schema' => { '$ref' => '#/definitions/Something' } }, '422' => { 'description' => 'Unprocessible Entity' } },
231
+ 'tags' => ['thing'],
232
+ 'operationId' => 'postThing'
233
+ }
234
+ },
235
+ '/thing/{id}' => {
236
+ 'get' => {
237
+ 'summary' => 'This gets Thing.',
238
+ 'description' => 'This gets Thing.',
239
+ 'produces' => ['application/json'],
240
+ 'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
241
+ 'responses' => { '200' => { 'description' => 'getting a single thing' }, '401' => { 'description' => 'Unauthorized' } },
242
+ 'tags' => ['thing'],
243
+ 'operationId' => 'getThingId'
244
+ },
245
+ 'put' => {
246
+ 'summary' => 'This updates Thing.',
247
+ 'description' => 'This updates Thing.',
248
+ 'produces' => ['application/json'],
249
+ 'consumes' => ['application/json'],
250
+ 'parameters' => [
251
+ { 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true },
252
+ { 'in' => 'formData', 'name' => 'text', 'description' => 'Content of something.', 'type' => 'string', 'required' => false },
253
+ { 'in' => 'formData', 'name' => 'links', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => false }
254
+ ],
255
+ 'responses' => { '200' => { 'description' => 'This updates Thing.', 'schema' => { '$ref' => '#/definitions/Something' } } },
256
+ 'tags' => ['thing'],
257
+ 'operationId' => 'putThingId'
258
+ },
259
+ 'delete' => {
260
+ 'summary' => 'This deletes Thing.',
261
+ 'description' => 'This deletes Thing.',
262
+ 'produces' => ['application/json'],
263
+ 'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
264
+ 'responses' => { '200' => { 'description' => 'This deletes Thing.', 'schema' => { '$ref' => '#/definitions/Something' } } },
265
+ 'tags' => ['thing'],
266
+ 'operationId' => 'deleteThingId'
267
+ }
268
+ },
269
+ '/thing2' => {
270
+ 'get' => {
271
+ 'summary' => 'This gets Things.',
272
+ 'description' => 'This gets Things.',
273
+ 'produces' => ['application/json'],
274
+ 'responses' => { '200' => { 'description' => 'get Horses', 'schema' => { '$ref' => '#/definitions/Something' } }, '401' => { 'description' => 'HorsesOutError', 'schema' => { '$ref' => '#/definitions/ApiError' } } },
275
+ 'tags' => ['thing2'],
276
+ 'operationId' => 'getThing2'
277
+ }
278
+ },
279
+ '/dummy/{id}' => {
280
+ 'delete' => {
281
+ 'summary' => 'dummy route.',
282
+ 'description' => 'dummy route.',
283
+ 'produces' => ['application/json'],
284
+ 'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
285
+ 'responses' => { '204' => { 'description' => 'dummy route.' }, '401' => { 'description' => 'Unauthorized' } },
286
+ 'tags' => ['dummy'],
287
+ 'operationId' => 'deleteDummyId'
288
+ }
289
+ }
290
+ },
291
+ 'definitions' => {
292
+ 'QueryInput' => {
293
+ 'type' => 'object',
294
+ 'properties' => { 'elements' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/QueryInputElement' }, 'description' => 'Set of configuration' } },
295
+ 'description' => 'nested route inside namespace'
296
+ },
297
+ 'QueryInputElement' => {
298
+ 'type' => 'object',
299
+ 'properties' => { 'key' => { 'type' => 'string', 'description' => 'Name of parameter' }, 'value' => { 'type' => 'string', 'description' => 'Value of parameter' } }
300
+ },
301
+ 'ApiError' => {
302
+ 'type' => 'object',
303
+ 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } },
304
+ 'description' => 'This gets Things.'
305
+ },
306
+ 'Something' => {
307
+ 'type' => 'object',
308
+ 'properties' => {
309
+ 'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'Identity of Something' },
310
+ 'text' => { 'type' => 'string', 'description' => 'Content of something.' },
311
+ 'links' => { 'type' => 'link' },
312
+ 'others' => { 'type' => 'text' }
313
+ },
314
+ 'description' => 'This gets Things.'
315
+ }
316
+ }
317
+ }
318
+ end
319
+
320
+ let(:http_verbs) { %w(get post put delete) }
321
+ end
322
+
323
+ def mounted_paths
324
+ %w( /thing /other_thing /dummy )
325
+ end
@@ -1,60 +1,164 @@
1
- RSpec.shared_context 'swagger example' do
1
+ RSpec.shared_context 'mock swagger example' do
2
2
  before :all do
3
3
  module Entities
4
- class Something < Grape::Entity
5
- expose :id, documentation: { type: Integer, desc: 'Identity of Something' }
6
- expose :text, documentation: { type: String, desc: 'Content of something.' }
7
- expose :links, documentation: { type: 'link', is_array: true }
8
- expose :others, documentation: { type: 'text', is_array: false }
4
+ class Something < OpenStruct
5
+ class << self
6
+ # Representable doesn't have documentation method, mock this
7
+ def documentation
8
+ {
9
+ id: { type: Integer, desc: 'Identity of Something' },
10
+ text: { type: String, desc: 'Content of something.' },
11
+ links: { type: 'link', is_array: true },
12
+ others: { type: 'text', is_array: false }
13
+ }
14
+ end
15
+ end
9
16
  end
10
17
 
11
- class EnumValues < Grape::Entity
12
- expose :gender, documentation: { type: 'string', desc: 'Content of something.', values: %w(Male Female) }
13
- expose :number, documentation: { type: 'integer', desc: 'Content of something.', values: [1, 2] }
18
+ class UseResponse < OpenStruct
19
+ class << self
20
+ def documentation
21
+ {
22
+ :description => { type: String },
23
+ '$responses' => { is_array: true }
24
+ }
25
+ end
26
+ end
14
27
  end
15
28
 
16
- class AliasedThing < Grape::Entity
17
- expose :something, as: :post, using: Entities::Something, documentation: { type: 'Something', desc: 'Reference to something.' }
29
+ class ResponseItem < OpenStruct
30
+ class << self
31
+ def documentation
32
+ {
33
+ id: { type: Integer },
34
+ name: { type: String }
35
+ }
36
+ end
37
+ end
18
38
  end
19
39
 
20
- class FourthLevel < Grape::Entity
21
- expose :text, documentation: { type: 'string' }
22
- end
23
-
24
- class ThirdLevel < Grape::Entity
25
- expose :parts, using: Entities::FourthLevel, documentation: { type: 'FourthLevel' }
26
- end
40
+ class UseNestedWithAddress < OpenStruct; end
41
+ class TypedDefinition < OpenStruct; end
42
+ class UseItemResponseAsType < OpenStruct; end
43
+ class OtherItem < OpenStruct; end
44
+ class EnumValues < OpenStruct; end
45
+ class AliasedThing < OpenStruct; end
46
+ class FourthLevel < OpenStruct; end
47
+ class ThirdLevel < OpenStruct; end
48
+ class SecondLevel < OpenStruct; end
49
+ class FirstLevel < OpenStruct; end
50
+ class QueryInputElement < OpenStruct; end
51
+ class QueryInput < OpenStruct; end
52
+ class ApiError < OpenStruct; end
53
+ class SecondApiError < OpenStruct; end
54
+ class RecursiveModel < OpenStruct; end
55
+ end
56
+ end
27
57
 
28
- class SecondLevel < Grape::Entity
29
- expose :parts, using: Entities::ThirdLevel, documentation: { type: 'ThirdLevel' }
30
- end
58
+ let(:swagger_definitions_models) do
59
+ {
60
+ 'ApiError' => {
61
+ 'type' => 'object',
62
+ 'properties' => {
63
+ 'mock_data' => {
64
+ 'type' => 'string',
65
+ 'description' => "it's a mock"
66
+ }
67
+ }
68
+ },
69
+ 'RecursiveModel' => {
70
+ 'type' => 'object',
71
+ 'properties' => {
72
+ 'mock_data' => {
73
+ 'type' => 'string',
74
+ 'description' => "it's a mock"
75
+ }
76
+ }
77
+ },
78
+ 'UseResponse' => {
79
+ 'type' => 'object',
80
+ 'properties' => {
81
+ 'mock_data' => {
82
+ 'type' => 'string',
83
+ 'description' => "it's a mock"
84
+ }
85
+ }
86
+ }
87
+ }
88
+ end
31
89
 
32
- class FirstLevel < Grape::Entity
33
- expose :parts, using: Entities::SecondLevel, documentation: { type: 'SecondLevel' }
34
- end
90
+ let(:swagger_nested_type) do
91
+ {
92
+ 'ApiError' => {
93
+ 'type' => 'object',
94
+ 'properties' => {
95
+ 'mock_data' => {
96
+ 'type' => 'string',
97
+ 'description' => "it's a mock"
98
+ }
99
+ },
100
+ 'description' => 'This returns something'
101
+ },
102
+ 'UseItemResponseAsType' => {
103
+ 'type' => 'object',
104
+ 'properties' => {
105
+ 'mock_data' => {
106
+ 'type' => 'string',
107
+ 'description' => "it's a mock"
108
+ }
109
+ },
110
+ 'description' => 'This returns something'
111
+ }
112
+ }
113
+ end
35
114
 
36
- class QueryInputElement < Grape::Entity
37
- expose :key, documentation: {
38
- type: String, desc: 'Name of parameter', required: true }
39
- expose :value, documentation: {
40
- type: String, desc: 'Value of parameter', required: true }
41
- end
115
+ let(:swagger_entity_as_response_object) do
116
+ {
117
+ 'UseResponse' => {
118
+ 'type' => 'object',
119
+ 'properties' => {
120
+ 'mock_data' => {
121
+ 'type' => 'string',
122
+ 'description' => "it's a mock"
123
+ }
124
+ },
125
+ 'description' => 'This returns something'
126
+ },
127
+ 'ApiError' => {
128
+ 'type' => 'object',
129
+ 'properties' => {
130
+ 'mock_data' => {
131
+ 'type' => 'string',
132
+ 'description' => "it's a mock"
133
+ }
134
+ },
135
+ 'description' => 'This returns something'
136
+ }
137
+ }
138
+ end
42
139
 
43
- class QueryInput < Grape::Entity
44
- expose :elements, using: Entities::QueryInputElement, documentation: {
45
- type: 'QueryInputElement',
46
- desc: 'Set of configuration',
47
- param_type: 'body',
48
- is_array: true,
49
- required: true
50
- }
51
- end
140
+ let(:swagger_params_as_response_object) do
141
+ {
142
+ 'ApiError' => {
143
+ 'type' => 'object',
144
+ 'properties' => {
145
+ 'mock_data' => {
146
+ 'type' => 'string',
147
+ 'description' => "it's a mock"
148
+ }
149
+ },
150
+ 'description' => 'This returns something'
151
+ }
152
+ }
153
+ end
52
154
 
53
- class ApiError < Grape::Entity
54
- expose :code, documentation: { type: Integer, desc: 'status code' }
55
- expose :message, documentation: { type: String, desc: 'error message' }
56
- end
57
- end
155
+ let(:swagger_typed_defintion) do
156
+ {
157
+ 'mock_data' => {
158
+ 'type' => 'string',
159
+ 'description' => "it's a mock"
160
+ }
161
+ }
58
162
  end
59
163
 
60
164
  let(:swagger_json) do
@@ -80,6 +184,7 @@ RSpec.shared_context 'swagger example' do
80
184
  'paths' => {
81
185
  '/v3/other_thing/{elements}' => {
82
186
  'get' => {
187
+ 'summary' => 'nested route inside namespace',
83
188
  'description' => 'nested route inside namespace',
84
189
  'produces' => ['application/json'],
85
190
  'parameters' => [{ 'in' => 'body', 'name' => 'elements', 'description' => 'Set of configuration', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => true }],
@@ -88,9 +193,11 @@ RSpec.shared_context 'swagger example' do
88
193
  'operationId' => 'getV3OtherThingElements',
89
194
  'x-amazon-apigateway-auth' => { 'type' => 'none' },
90
195
  'x-amazon-apigateway-integration' => { 'type' => 'aws', 'uri' => 'foo_bar_uri', 'httpMethod' => 'get' }
91
- } },
196
+ }
197
+ },
92
198
  '/thing' => {
93
199
  'get' => {
200
+ 'summary' => 'This gets Things.',
94
201
  'description' => 'This gets Things.',
95
202
  'produces' => ['application/json'],
96
203
  'parameters' => [
@@ -104,6 +211,7 @@ RSpec.shared_context 'swagger example' do
104
211
  'operationId' => 'getThing'
105
212
  },
106
213
  'post' => {
214
+ 'summary' => 'This creates Thing.',
107
215
  'description' => 'This creates Thing.',
108
216
  'produces' => ['application/json'],
109
217
  'consumes' => ['application/json'],
@@ -114,9 +222,11 @@ RSpec.shared_context 'swagger example' do
114
222
  'responses' => { '201' => { 'description' => 'This creates Thing.', 'schema' => { '$ref' => '#/definitions/Something' } }, '422' => { 'description' => 'Unprocessible Entity' } },
115
223
  'tags' => ['thing'],
116
224
  'operationId' => 'postThing'
117
- } },
225
+ }
226
+ },
118
227
  '/thing/{id}' => {
119
228
  'get' => {
229
+ 'summary' => 'This gets Thing.',
120
230
  'description' => 'This gets Thing.',
121
231
  'produces' => ['application/json'],
122
232
  'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
@@ -125,6 +235,7 @@ RSpec.shared_context 'swagger example' do
125
235
  'operationId' => 'getThingId'
126
236
  },
127
237
  'put' => {
238
+ 'summary' => 'This updates Thing.',
128
239
  'description' => 'This updates Thing.',
129
240
  'produces' => ['application/json'],
130
241
  'consumes' => ['application/json'],
@@ -138,55 +249,70 @@ RSpec.shared_context 'swagger example' do
138
249
  'operationId' => 'putThingId'
139
250
  },
140
251
  'delete' => {
252
+ 'summary' => 'This deletes Thing.',
141
253
  'description' => 'This deletes Thing.',
142
254
  'produces' => ['application/json'],
143
255
  'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
144
256
  'responses' => { '200' => { 'description' => 'This deletes Thing.', 'schema' => { '$ref' => '#/definitions/Something' } } },
145
257
  'tags' => ['thing'],
146
258
  'operationId' => 'deleteThingId'
147
- } },
259
+ }
260
+ },
148
261
  '/thing2' => {
149
262
  'get' => {
263
+ 'summary' => 'This gets Things.',
150
264
  'description' => 'This gets Things.',
151
265
  'produces' => ['application/json'],
152
266
  'responses' => { '200' => { 'description' => 'get Horses', 'schema' => { '$ref' => '#/definitions/Something' } }, '401' => { 'description' => 'HorsesOutError', 'schema' => { '$ref' => '#/definitions/ApiError' } } },
153
267
  'tags' => ['thing2'],
154
268
  'operationId' => 'getThing2'
155
- } },
269
+ }
270
+ },
156
271
  '/dummy/{id}' => {
157
272
  'delete' => {
273
+ 'summary' => 'dummy route.',
158
274
  'description' => 'dummy route.',
159
275
  'produces' => ['application/json'],
160
276
  'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
161
277
  'responses' => { '204' => { 'description' => 'dummy route.' }, '401' => { 'description' => 'Unauthorized' } },
162
278
  'tags' => ['dummy'],
163
279
  'operationId' => 'deleteDummyId'
164
- } } },
280
+ }
281
+ }
282
+ },
165
283
  'definitions' => {
166
284
  'QueryInput' => {
167
285
  'type' => 'object',
168
- 'properties' => { 'elements' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/QueryInputElement' }, 'description' => 'Set of configuration' } },
286
+ 'properties' => {
287
+ 'mock_data' => {
288
+ 'type' => 'string',
289
+ 'description' => "it's a mock"
290
+ }
291
+ },
169
292
  'description' => 'nested route inside namespace'
170
293
  },
171
- 'QueryInputElement' => {
172
- 'type' => 'object',
173
- 'properties' => { 'key' => { 'type' => 'string', 'description' => 'Name of parameter' }, 'value' => { 'type' => 'string', 'description' => 'Value of parameter' } }
174
- },
175
294
  'ApiError' => {
176
295
  'type' => 'object',
177
- 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } },
296
+ 'properties' => {
297
+ 'mock_data' => {
298
+ 'type' => 'string',
299
+ 'description' => "it's a mock"
300
+ }
301
+ },
178
302
  'description' => 'This gets Things.'
179
303
  },
180
304
  'Something' => {
181
305
  'type' => 'object',
182
306
  'properties' => {
183
- 'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'Identity of Something' },
184
- 'text' => { 'type' => 'string', 'description' => 'Content of something.' },
185
- 'links' => { 'type' => 'link' },
186
- 'others' => { 'type' => 'text' }
307
+ 'mock_data' => {
308
+ 'type' => 'string',
309
+ 'description' => "it's a mock"
310
+ }
187
311
  },
188
312
  'description' => 'This gets Things.'
189
- } } }
313
+ }
314
+ }
315
+ }
190
316
  end
191
317
 
192
318
  let(:http_verbs) { %w(get post put delete) }