grape-swagger 1.2.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +14 -0
  3. data/.github/workflows/rubocop.yml +26 -0
  4. data/.github/workflows/ruby.yml +32 -0
  5. data/.rubocop.yml +15 -4
  6. data/.rubocop_todo.yml +1 -1
  7. data/CHANGELOG.md +37 -0
  8. data/Gemfile +2 -2
  9. data/README.md +84 -3
  10. data/UPGRADING.md +8 -0
  11. data/grape-swagger.gemspec +3 -3
  12. data/lib/grape-swagger/doc_methods/format_data.rb +2 -2
  13. data/lib/grape-swagger/doc_methods/move_params.rb +1 -1
  14. data/lib/grape-swagger/doc_methods/parse_params.rb +6 -0
  15. data/lib/grape-swagger/doc_methods.rb +65 -62
  16. data/lib/grape-swagger/endpoint.rb +55 -15
  17. data/lib/grape-swagger/errors.rb +2 -0
  18. data/lib/grape-swagger/model_parsers.rb +2 -2
  19. data/lib/grape-swagger/rake/oapi_tasks.rb +2 -0
  20. data/lib/grape-swagger/version.rb +1 -1
  21. data/lib/grape-swagger.rb +5 -2
  22. data/spec/issues/537_enum_values_spec.rb +1 -0
  23. data/spec/issues/776_multiple_presents_spec.rb +59 -0
  24. data/spec/issues/809_utf8_routes_spec.rb +55 -0
  25. data/spec/lib/format_data_spec.rb +24 -0
  26. data/spec/lib/move_params_spec.rb +2 -2
  27. data/spec/spec_helper.rb +1 -1
  28. data/spec/support/empty_model_parser.rb +2 -0
  29. data/spec/support/model_parsers/entity_parser.rb +8 -8
  30. data/spec/support/model_parsers/mock_parser.rb +24 -8
  31. data/spec/support/model_parsers/representable_parser.rb +8 -8
  32. data/spec/support/namespace_tags.rb +3 -0
  33. data/spec/support/the_paths_definitions.rb +4 -4
  34. data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +1 -0
  35. data/spec/swagger_v2/api_swagger_v2_response_with_headers_spec.rb +4 -2
  36. data/spec/swagger_v2/api_swagger_v2_response_with_models_spec.rb +53 -0
  37. data/spec/swagger_v2/api_swagger_v2_spec.rb +1 -0
  38. data/spec/swagger_v2/boolean_params_spec.rb +1 -0
  39. data/spec/swagger_v2/float_api_spec.rb +1 -0
  40. data/spec/swagger_v2/inheritance_and_discriminator_spec.rb +1 -0
  41. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +1 -0
  42. data/spec/swagger_v2/param_multi_type_spec.rb +2 -0
  43. data/spec/swagger_v2/param_type_spec.rb +3 -0
  44. data/spec/swagger_v2/param_values_spec.rb +6 -0
  45. data/spec/swagger_v2/{params_array_collection_fromat_spec.rb → params_array_collection_format_spec.rb} +0 -0
  46. data/spec/swagger_v2/params_example_spec.rb +40 -0
  47. data/spec/swagger_v2/reference_entity_spec.rb +2 -2
  48. data/spec/swagger_v2/simple_mounted_api_spec.rb +3 -0
  49. metadata +19 -7
  50. data/.travis.yml +0 -36
@@ -40,21 +40,37 @@ RSpec.shared_context 'mock swagger example' do
40
40
  end
41
41
 
42
42
  class UseNestedWithAddress < OpenStruct; end
43
+
43
44
  class TypedDefinition < OpenStruct; end
45
+
44
46
  class UseItemResponseAsType < OpenStruct; end
47
+
45
48
  class OtherItem < OpenStruct; end
49
+
46
50
  class EnumValues < OpenStruct; end
51
+
47
52
  class AliasedThing < OpenStruct; end
53
+
48
54
  class FourthLevel < OpenStruct; end
55
+
49
56
  class ThirdLevel < OpenStruct; end
57
+
50
58
  class SecondLevel < OpenStruct; end
59
+
51
60
  class FirstLevel < OpenStruct; end
61
+
52
62
  class QueryInputElement < OpenStruct; end
63
+
53
64
  class QueryInput < OpenStruct; end
65
+
54
66
  class ApiError < OpenStruct; end
67
+
55
68
  class SecondApiError < OpenStruct; end
69
+
56
70
  class RecursiveModel < OpenStruct; end
71
+
57
72
  class DocumentedHashAndArrayModel < OpenStruct; end
73
+
58
74
  module NestedModule
59
75
  class ApiResponse < OpenStruct; end
60
76
  end
@@ -112,7 +128,7 @@ RSpec.shared_context 'mock swagger example' do
112
128
  'description' => "it's a mock"
113
129
  }
114
130
  },
115
- 'description' => 'This returns something'
131
+ 'description' => 'ApiError model'
116
132
  },
117
133
  'UseItemResponseAsType' => {
118
134
  'type' => 'object',
@@ -122,7 +138,7 @@ RSpec.shared_context 'mock swagger example' do
122
138
  'description' => "it's a mock"
123
139
  }
124
140
  },
125
- 'description' => 'This returns something'
141
+ 'description' => 'UseItemResponseAsType model'
126
142
  }
127
143
  }
128
144
  end
@@ -137,7 +153,7 @@ RSpec.shared_context 'mock swagger example' do
137
153
  'description' => "it's a mock"
138
154
  }
139
155
  },
140
- 'description' => 'This returns something'
156
+ 'description' => 'UseResponse model'
141
157
  },
142
158
  'ApiError' => {
143
159
  'type' => 'object',
@@ -147,7 +163,7 @@ RSpec.shared_context 'mock swagger example' do
147
163
  'description' => "it's a mock"
148
164
  }
149
165
  },
150
- 'description' => 'This returns something'
166
+ 'description' => 'ApiError model'
151
167
  }
152
168
  }
153
169
  end
@@ -162,7 +178,7 @@ RSpec.shared_context 'mock swagger example' do
162
178
  'description' => "it's a mock"
163
179
  }
164
180
  },
165
- 'description' => 'This returns something'
181
+ 'description' => 'ApiError model'
166
182
  }
167
183
  }
168
184
  end
@@ -296,7 +312,7 @@ RSpec.shared_context 'mock swagger example' do
296
312
  'description' => "it's a mock"
297
313
  }
298
314
  },
299
- 'description' => 'nested route inside namespace'
315
+ 'description' => 'QueryInput model'
300
316
  },
301
317
  'ApiError' => {
302
318
  'type' => 'object',
@@ -306,7 +322,7 @@ RSpec.shared_context 'mock swagger example' do
306
322
  'description' => "it's a mock"
307
323
  }
308
324
  },
309
- 'description' => 'This gets Things.'
325
+ 'description' => 'ApiError model'
310
326
  },
311
327
  'Something' => {
312
328
  'type' => 'object',
@@ -316,7 +332,7 @@ RSpec.shared_context 'mock swagger example' do
316
332
  'description' => "it's a mock"
317
333
  }
318
334
  },
319
- 'description' => 'This gets Things.'
335
+ 'description' => 'Something model'
320
336
  }
321
337
  }
322
338
  }
@@ -218,22 +218,22 @@ RSpec.shared_context 'representable swagger example' do
218
218
 
219
219
  let(:swagger_nested_type) do
220
220
  {
221
- 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'This returns something' },
222
- 'UseItemResponseAsType' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, 'responses' => { 'description' => '', 'type' => 'ResponseItem' } }, 'description' => 'This returns something' }
221
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' },
222
+ 'UseItemResponseAsType' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, 'responses' => { 'description' => '', 'type' => 'ResponseItem' } }, 'description' => 'UseItemResponseAsType model' }
223
223
  }
224
224
  end
225
225
 
226
226
  let(:swagger_entity_as_response_object) do
227
227
  {
228
- 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'This returns something' },
228
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' },
229
229
  'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'description' => '', 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'description' => '', 'type' => 'string' } } },
230
- 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' }, 'description' => '' } }, 'description' => 'This returns something' }
230
+ 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' }, 'description' => '' } }, 'description' => 'UseResponse model' }
231
231
  }
232
232
  end
233
233
 
234
234
  let(:swagger_params_as_response_object) do
235
235
  {
236
- 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'This returns something' }
236
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' }
237
237
  }
238
238
  end
239
239
 
@@ -372,7 +372,7 @@ RSpec.shared_context 'representable swagger example' do
372
372
  'type' => 'object',
373
373
  'required' => ['elements'],
374
374
  'properties' => { 'elements' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/QueryInputElement' }, 'description' => 'Set of configuration' } },
375
- 'description' => 'nested route inside namespace'
375
+ 'description' => 'QueryInput model'
376
376
  },
377
377
  'QueryInputElement' => {
378
378
  'type' => 'object',
@@ -382,7 +382,7 @@ RSpec.shared_context 'representable swagger example' do
382
382
  'ApiError' => {
383
383
  'type' => 'object',
384
384
  'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } },
385
- 'description' => 'This gets Things.'
385
+ 'description' => 'ApiError model'
386
386
  },
387
387
  'Something' => {
388
388
  'type' => 'object',
@@ -392,7 +392,7 @@ RSpec.shared_context 'representable swagger example' do
392
392
  'links' => { 'type' => 'array', 'items' => { 'description' => '', 'type' => 'link' } },
393
393
  'others' => { 'description' => '', 'type' => 'text' }
394
394
  },
395
- 'description' => 'This gets Things.'
395
+ 'description' => 'Something model'
396
396
  }
397
397
  }
398
398
  }
@@ -3,12 +3,15 @@
3
3
  RSpec.shared_context 'namespace example' do
4
4
  before :all do
5
5
  module TheApi
6
+ # rubocop:disable Lint/EmptyClass
6
7
  class CustomType; end
8
+ # rubocop:enable Lint/EmptyClass
7
9
 
8
10
  class NamespaceApi < Grape::API
9
11
  namespace :hudson do
10
12
  desc 'Document root'
11
13
  get '/' do
14
+ { message: 'hi' }
12
15
  end
13
16
  end
14
17
 
@@ -8,7 +8,7 @@ RSpec.shared_context 'the api paths/defs' do
8
8
  produces: ['application/json'],
9
9
  consumes: ['application/json'],
10
10
  parameters: [
11
- { in: 'body', name: 'in_body_1', description: 'in_body_1', type: 'integer', format: 'int32', required: true },
11
+ { in: 'body', name: 'in_body_1', description: 'in_body_1', type: 'integer', format: 'int32', required: true, example: 23 },
12
12
  { in: 'body', name: 'in_body_2', description: 'in_body_2', type: 'string', required: false },
13
13
  { in: 'body', name: 'in_body_3', description: 'in_body_3', type: 'string', required: false }
14
14
  ],
@@ -31,7 +31,7 @@ RSpec.shared_context 'the api paths/defs' do
31
31
  { in: 'path', name: 'key', description: nil, type: 'integer', format: 'int32', required: true },
32
32
  { in: 'body', name: 'in_body_1', description: 'in_body_1', type: 'integer', format: 'int32', required: true },
33
33
  { in: 'body', name: 'in_body_2', description: 'in_body_2', type: 'string', required: false },
34
- { in: 'body', name: 'in_body_3', description: 'in_body_3', type: 'string', required: false }
34
+ { in: 'body', name: 'in_body_3', description: 'in_body_3', type: 'string', required: false, example: 'my example string' }
35
35
  ],
36
36
  responses: { 200 => { description: 'put in body /wo entity', schema: { '$ref' => '#/definitions/InBody' } } },
37
37
  tags: ['in_body'],
@@ -85,7 +85,7 @@ RSpec.shared_context 'the api paths/defs' do
85
85
  {
86
86
  type: 'object',
87
87
  properties: {
88
- in_body_1: { type: 'integer', format: 'int32', description: 'in_body_1' },
88
+ in_body_1: { type: 'integer', format: 'int32', description: 'in_body_1', example: 23 },
89
89
  in_body_2: { type: 'string', description: 'in_body_2' },
90
90
  in_body_3: { type: 'string', description: 'in_body_3' }
91
91
  },
@@ -99,7 +99,7 @@ RSpec.shared_context 'the api paths/defs' do
99
99
  properties: {
100
100
  in_body_1: { type: 'integer', format: 'int32', description: 'in_body_1' },
101
101
  in_body_2: { type: 'string', description: 'in_body_2' },
102
- in_body_3: { type: 'string', description: 'in_body_3' }
102
+ in_body_3: { type: 'string', description: 'in_body_3', example: 'my example string' }
103
103
  },
104
104
  required: [:in_body_1]
105
105
  }
@@ -81,6 +81,7 @@ describe 'swagger spec v2.0' do
81
81
  requires :id, type: Integer
82
82
  end
83
83
  delete '/dummy/:id' do
84
+ {}
84
85
  end
85
86
 
86
87
  namespace :other_thing do
@@ -19,7 +19,8 @@ describe 'response with headers' do
19
19
  end
20
20
 
21
21
  desc 'A 204 can have headers too' do
22
- success Hash[status: 204, message: 'No content', headers: { 'Location' => { description: 'Location of resource', type: 'string' } }]
22
+ foo = { status: 204, message: 'No content', headers: { 'Location' => { description: 'Location of resource', type: 'string' } } }
23
+ success foo
23
24
  failure [[400, 'Bad Request', Entities::ApiError, { 'application/json' => { code: 400, message: 'Bad request' } }, { 'Date' => { description: 'Date of failure', type: 'string' } }]]
24
25
  end
25
26
  delete '/no_content_response_headers' do
@@ -27,7 +28,8 @@ describe 'response with headers' do
27
28
  end
28
29
 
29
30
  desc 'A file can have headers too' do
30
- success Hash[status: 200, model: 'File', headers: { 'Cache-Control' => { description: 'Directive for caching', type: 'string' } }]
31
+ foo = { status: 200, model: 'File', headers: { 'Cache-Control': { description: 'Directive for caching', type: 'string' } } }
32
+ success foo
31
33
  failure [[404, 'NotFound', Entities::ApiError, { 'application/json' => { code: 404, message: 'Not found' } }, { 'Date' => { description: 'Date of failure', type: 'string' } }]]
32
34
  end
33
35
  get '/file_response_headers' do
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'response' do
6
+ include_context "#{MODEL_PARSER} swagger example"
7
+
8
+ before :all do
9
+ module TheApi
10
+ class ResponseApiModels < Grape::API
11
+ format :json
12
+
13
+ desc 'This returns something',
14
+ success: [{ code: 200 }],
15
+ failure: [
16
+ { code: 400, message: 'NotFound', model: '' },
17
+ { code: 404, message: 'BadRequest', model: Entities::ApiError }
18
+ ]
19
+ get '/use-response' do
20
+ { 'declared_params' => declared(params) }
21
+ end
22
+
23
+ add_swagger_documentation(models: [Entities::UseResponse])
24
+ end
25
+ end
26
+ end
27
+
28
+ def app
29
+ TheApi::ResponseApiModels
30
+ end
31
+
32
+ describe 'uses entity as response object implicitly with route name' do
33
+ subject do
34
+ get '/swagger_doc/use-response'
35
+ JSON.parse(last_response.body)
36
+ end
37
+
38
+ specify do
39
+ expect(subject['paths']['/use-response']['get']).to eql(
40
+ 'description' => 'This returns something',
41
+ 'produces' => ['application/json'],
42
+ 'responses' => {
43
+ '200' => { 'description' => 'This returns something', 'schema' => { '$ref' => '#/definitions/UseResponse' } },
44
+ '400' => { 'description' => 'NotFound' },
45
+ '404' => { 'description' => 'BadRequest', 'schema' => { '$ref' => '#/definitions/ApiError' } }
46
+ },
47
+ 'tags' => ['use-response'],
48
+ 'operationId' => 'getUseResponse'
49
+ )
50
+ expect(subject['definitions']).to eql(swagger_entity_as_response_object)
51
+ end
52
+ end
53
+ end
@@ -80,6 +80,7 @@ describe 'swagger spec v2.0' do
80
80
  requires :id, type: Integer
81
81
  end
82
82
  delete '/dummy/:id' do
83
+ {}
83
84
  end
84
85
 
85
86
  namespace :other_thing do
@@ -11,6 +11,7 @@ describe 'Boolean Params' do
11
11
  requires :a_boolean, type: Grape::API::Boolean
12
12
  end
13
13
  post :splines do
14
+ { message: 'hi' }
14
15
  end
15
16
 
16
17
  add_swagger_documentation
@@ -11,6 +11,7 @@ describe 'Float Params' do
11
11
  requires :a_float, type: Float
12
12
  end
13
13
  post :splines do
14
+ { message: 'hi' }
14
15
  end
15
16
 
16
17
  add_swagger_documentation
@@ -33,6 +33,7 @@ describe 'Inheritance and Discriminator' do
33
33
  }
34
34
  end
35
35
  end
36
+
36
37
  class NameApi < Grape::API
37
38
  add_swagger_documentation models: [Entities::Pet, Entities::Cat]
38
39
  end
@@ -17,6 +17,7 @@ describe 'namespace tags check while using prefix and version' do
17
17
  namespace :hudson do
18
18
  desc 'Document root'
19
19
  get '/' do
20
+ { message: 'hi' }
20
21
  end
21
22
  end
22
23
 
@@ -16,6 +16,7 @@ describe 'Params Multi Types' do
16
16
  requires :another_input, type: [String, Integer]
17
17
  end
18
18
  post :action do
19
+ { message: 'hi' }
19
20
  end
20
21
 
21
22
  add_swagger_documentation
@@ -61,6 +62,7 @@ describe 'Params Multi Types' do
61
62
  requires :another_input, type: [String, Integer]
62
63
  end
63
64
  post :action do
65
+ { message: 'hi' }
64
66
  end
65
67
 
66
68
  add_swagger_documentation
@@ -11,12 +11,14 @@ describe 'Params Types' do
11
11
  requires :input, type: String
12
12
  end
13
13
  post :action do
14
+ { message: 'hi' }
14
15
  end
15
16
 
16
17
  params do
17
18
  requires :input, type: String, default: '14', documentation: { type: 'email', default: '42' }
18
19
  end
19
20
  post :action_with_doc do
21
+ { message: 'hi' }
20
22
  end
21
23
 
22
24
  add_swagger_documentation
@@ -49,6 +51,7 @@ describe 'Params Types' do
49
51
  requires :input, type: String
50
52
  end
51
53
  post :action do
54
+ { message: 'hi' }
52
55
  end
53
56
 
54
57
  add_swagger_documentation
@@ -12,24 +12,28 @@ describe 'Convert values to enum or Range' do
12
12
  requires :letter, type: String, values: %w[a b c]
13
13
  end
14
14
  post :plain_array do
15
+ { message: 'hi' }
15
16
  end
16
17
 
17
18
  params do
18
19
  requires :letter, type: String, values: proc { %w[d e f] }
19
20
  end
20
21
  post :array_in_proc do
22
+ { message: 'hi' }
21
23
  end
22
24
 
23
25
  params do
24
26
  requires :letter, type: String, values: 'a'..'z'
25
27
  end
26
28
  post :range_letter do
29
+ { message: 'hi' }
27
30
  end
28
31
 
29
32
  params do
30
33
  requires :integer, type: Integer, values: -5..5
31
34
  end
32
35
  post :range_integer do
36
+ { message: 'hi' }
33
37
  end
34
38
 
35
39
  add_swagger_documentation
@@ -107,12 +111,14 @@ describe 'Convert values to enum for float range and not arrays inside a proc',
107
111
  requires :letter, type: String, values: proc { 'string' }
108
112
  end
109
113
  post :non_array_in_proc do
114
+ { message: 'hi' }
110
115
  end
111
116
 
112
117
  params do
113
118
  requires :float, type: Float, values: -5.0..5.0
114
119
  end
115
120
  post :range_float do
121
+ { message: 'hi' }
116
122
  end
117
123
 
118
124
  add_swagger_documentation
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Param example' do
6
+ def app
7
+ Class.new(Grape::API) do
8
+ format :json
9
+
10
+ params do
11
+ requires :id, type: Integer, documentation: { example: 123 }
12
+ optional :name, type: String, documentation: { example: 'Person' }
13
+ optional :obj, type: 'Object', documentation: { example: { 'foo' => 'bar' } }
14
+ end
15
+
16
+ get '/endpoint_with_examples' do
17
+ { 'declared_params' => declared(params) }
18
+ end
19
+
20
+ add_swagger_documentation
21
+ end
22
+ end
23
+
24
+ describe 'documentation with parameter examples' do
25
+ subject do
26
+ get '/swagger_doc/endpoint_with_examples'
27
+ JSON.parse(last_response.body)
28
+ end
29
+
30
+ specify do
31
+ expect(subject['paths']['/endpoint_with_examples']['get']['parameters']).to eql(
32
+ [
33
+ { 'in' => 'query', 'name' => 'id', 'type' => 'integer', 'example' => 123, 'format' => 'int32', 'required' => true },
34
+ { 'in' => 'query', 'name' => 'name', 'type' => 'string', 'example' => 'Person', 'required' => false },
35
+ { 'in' => 'query', 'name' => 'obj', 'type' => 'Object', 'example' => { 'foo' => 'bar' }, 'required' => false }
36
+ ]
37
+ )
38
+ end
39
+ end
40
+ end