grape-swagger 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +4 -0
  4. data/.rubocop_todo.yml +29 -32
  5. data/.travis.yml +2 -0
  6. data/CHANGELOG.md +15 -0
  7. data/Gemfile +1 -1
  8. data/LICENSE.txt +1 -1
  9. data/README.md +253 -223
  10. data/UPGRADING.md +4 -0
  11. data/grape-swagger.gemspec +2 -2
  12. data/lib/grape-swagger.rb +10 -4
  13. data/lib/grape-swagger/doc_methods.rb +10 -5
  14. data/lib/grape-swagger/doc_methods/build_model_definition.rb +38 -0
  15. data/lib/grape-swagger/doc_methods/data_type.rb +6 -5
  16. data/lib/grape-swagger/doc_methods/move_params.rb +9 -1
  17. data/lib/grape-swagger/doc_methods/optional_object.rb +1 -1
  18. data/lib/grape-swagger/doc_methods/parse_params.rb +20 -19
  19. data/lib/grape-swagger/doc_methods/tag_name_description.rb +9 -17
  20. data/lib/grape-swagger/endpoint.rb +26 -29
  21. data/lib/grape-swagger/model_parsers.rb +7 -0
  22. data/lib/grape-swagger/version.rb +1 -1
  23. data/spec/issues/427_entity_as_string_spec.rb +39 -0
  24. data/spec/issues/430_entity_definitions_spec.rb +48 -4
  25. data/spec/support/model_parsers/entity_parser.rb +10 -1
  26. data/spec/support/model_parsers/mock_parser.rb +4 -1
  27. data/spec/support/model_parsers/representable_parser.rb +13 -2
  28. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +0 -2
  29. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +56 -0
  30. data/spec/swagger_v2/api_swagger_v2_spec.rb +87 -61
  31. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +24 -3
  32. data/spec/swagger_v2/guarded_endpoint_spec.rb +8 -3
  33. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +2 -8
  34. data/spec/swagger_v2/namespace_tags_spec.rb +2 -8
  35. data/spec/swagger_v2/params_array_spec.rb +36 -0
  36. data/spec/swagger_v2/simple_mounted_api_spec.rb +2 -14
  37. metadata +12 -9
@@ -10,19 +10,53 @@ describe 'definition names' do
10
10
  module AnotherGroupingModule
11
11
  class Class1
12
12
  class Entity < Grape::Entity
13
- expose :one_thing
13
+ expose :first_thing
14
14
  end
15
15
  end
16
16
 
17
17
  class Class2
18
+ class Entities < Grape::Entity
19
+ expose :second_thing
20
+ end
21
+ end
22
+
23
+ class Class3
18
24
  class Entity < Grape::Entity
19
- expose :another_thing
25
+ expose :third_thing
20
26
 
21
27
  def self.entity_name
22
28
  'FooKlass'
23
29
  end
24
30
  end
25
31
  end
32
+
33
+ class Class4
34
+ class FourthEntity < Grape::Entity
35
+ expose :fourth_thing
36
+ end
37
+ end
38
+
39
+ class Class5
40
+ class FithEntity < Class4::FourthEntity
41
+ expose :fith_thing
42
+ end
43
+ end
44
+
45
+ class Class6
46
+ class SixthEntity < Grape::Entity
47
+ expose :sixth_thing
48
+
49
+ def self.entity_name
50
+ 'BarKlass'
51
+ end
52
+ end
53
+ end
54
+
55
+ class Class7
56
+ class SeventhEntity < Class6::SixthEntity
57
+ expose :seventh_thing
58
+ end
59
+ end
26
60
  end
27
61
  end
28
62
  end
@@ -30,7 +64,12 @@ describe 'definition names' do
30
64
  class NameApi < Grape::API
31
65
  add_swagger_documentation models: [
32
66
  DummyEntities::WithVeryLongName::AnotherGroupingModule::Class1::Entity,
33
- DummyEntities::WithVeryLongName::AnotherGroupingModule::Class2::Entity
67
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class2::Entities,
68
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class3::Entity,
69
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class4::FourthEntity,
70
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class5::FithEntity,
71
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class6::SixthEntity,
72
+ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class7::SeventhEntity
34
73
  ]
35
74
  end
36
75
  end
@@ -43,6 +82,11 @@ describe 'definition names' do
43
82
  JSON.parse(last_response.body)['definitions']
44
83
  end
45
84
 
46
- specify { expect(subject).to include 'AnotherGroupingModuleClass1' }
85
+ specify { expect(subject).to include 'Class1' }
86
+ specify { expect(subject).to include 'Class2' }
47
87
  specify { expect(subject).to include 'FooKlass' }
88
+ specify { expect(subject).to include 'FourthEntity' }
89
+ specify { expect(subject).to include 'FithEntity' }
90
+ specify { expect(subject).to include 'BarKlass' }
91
+ specify { expect(subject).to include 'SeventhEntity' }
48
92
  end
@@ -57,6 +57,13 @@ RSpec.shared_context 'entity swagger example' do
57
57
  expose :message, documentation: { type: String, desc: 'error message' }
58
58
  end
59
59
 
60
+ module NestedModule
61
+ class ApiResponse < Grape::Entity
62
+ expose :status, documentation: { type: String }
63
+ expose :error, documentation: { type: ::Entities::ApiError }
64
+ end
65
+ end
66
+
60
67
  class SecondApiError < Grape::Entity
61
68
  expose :code, documentation: { type: Integer }
62
69
  expose :severity, documentation: { type: String }
@@ -297,11 +304,13 @@ RSpec.shared_context 'entity swagger example' do
297
304
  'definitions' => {
298
305
  'QueryInput' => {
299
306
  'type' => 'object',
307
+ 'required' => ['elements'],
300
308
  'properties' => { 'elements' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/QueryInputElement' }, 'description' => 'Set of configuration' } },
301
309
  'description' => 'nested route inside namespace'
302
310
  },
303
311
  'QueryInputElement' => {
304
312
  'type' => 'object',
313
+ 'required' => %w(key value),
305
314
  'properties' => { 'key' => { 'type' => 'string', 'description' => 'Name of parameter' }, 'value' => { 'type' => 'string', 'description' => 'Value of parameter' } }
306
315
  },
307
316
  'ApiError' => {
@@ -327,5 +336,5 @@ RSpec.shared_context 'entity swagger example' do
327
336
  end
328
337
 
329
338
  def mounted_paths
330
- %w( /thing /other_thing /dummy )
339
+ %w(/thing /other_thing /dummy)
331
340
  end
@@ -53,6 +53,9 @@ RSpec.shared_context 'mock swagger example' do
53
53
  class SecondApiError < OpenStruct; end
54
54
  class RecursiveModel < OpenStruct; end
55
55
  class DocumentedHashAndArrayModel < OpenStruct; end
56
+ module NestedModule
57
+ class ApiResponse < OpenStruct; end
58
+ end
56
59
  end
57
60
  end
58
61
 
@@ -329,5 +332,5 @@ RSpec.shared_context 'mock swagger example' do
329
332
  end
330
333
 
331
334
  def mounted_paths
332
- %w( /thing /other_thing /dummy )
335
+ %w(/thing /other_thing /dummy)
333
336
  end
@@ -91,6 +91,15 @@ RSpec.shared_context 'representable swagger example' do
91
91
  property :message, documentation: { type: String, desc: 'error message' }
92
92
  end
93
93
 
94
+ module NestedModule
95
+ class ApiResponse < Representable::Decorator
96
+ include Representable::JSON
97
+
98
+ property :status, documentation: { type: String }
99
+ property :error, documentation: { type: ::Entities::ApiError }
100
+ end
101
+ end
102
+
94
103
  class SecondApiError < Representable::Decorator
95
104
  include Representable::JSON
96
105
 
@@ -236,7 +245,7 @@ RSpec.shared_context 'representable swagger example' do
236
245
  'prop_file' => { 'description' => 'prop_file description', 'type' => 'file' },
237
246
  'prop_float' => { 'description' => 'prop_float description', 'type' => 'number', 'format' => 'float' },
238
247
  'prop_integer' => { 'description' => 'prop_integer description', 'type' => 'integer', 'format' => 'int32' },
239
- 'prop_json' => { 'description' => 'prop_json description', 'type' => 'Representable::JSON' },
248
+ 'prop_json' => { 'description' => 'prop_json description', 'type' => 'JSON' },
240
249
  'prop_long' => { 'description' => 'prop_long description', 'type' => 'integer', 'format' => 'int64' },
241
250
  'prop_password' => { 'description' => 'prop_password description', 'type' => 'string', 'format' => 'password' },
242
251
  'prop_string' => { 'description' => 'prop_string description', 'type' => 'string' },
@@ -367,11 +376,13 @@ RSpec.shared_context 'representable swagger example' do
367
376
  'definitions' => {
368
377
  'QueryInput' => {
369
378
  'type' => 'object',
379
+ 'required' => ['elements'],
370
380
  'properties' => { 'elements' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/QueryInputElement' }, 'description' => 'Set of configuration' } },
371
381
  'description' => 'nested route inside namespace'
372
382
  },
373
383
  'QueryInputElement' => {
374
384
  'type' => 'object',
385
+ 'required' => %w(key value),
375
386
  'properties' => { 'key' => { 'type' => 'string', 'description' => 'Name of parameter' }, 'value' => { 'type' => 'string', 'description' => 'Value of parameter' } }
376
387
  },
377
388
  'ApiError' => {
@@ -397,5 +408,5 @@ RSpec.shared_context 'representable swagger example' do
397
408
  end
398
409
 
399
410
  def mounted_paths
400
- %w( /thing /other_thing /dummy )
411
+ %w(/thing /other_thing /dummy)
401
412
  end
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  def details
@@ -54,6 +54,17 @@ describe 'setting of param type, such as `query`, `path`, `formData`, `body`, `h
54
54
  end
55
55
  end
56
56
 
57
+ namespace :with_entity_param do
58
+ desc 'put in body with entity parameter'
59
+ params do
60
+ optional :data, type: ::Entities::NestedModule::ApiResponse, documentation: { desc: 'request data' }
61
+ end
62
+
63
+ post do
64
+ { 'declared_params' => declared(params) }
65
+ end
66
+ end
67
+
57
68
  add_swagger_documentation
58
69
  end
59
70
  end
@@ -156,4 +167,49 @@ describe 'setting of param type, such as `query`, `path`, `formData`, `body`, `h
156
167
  )
157
168
  end
158
169
  end
170
+
171
+ describe 'complex entity given' do
172
+ let(:request_parameters_definition) do
173
+ [
174
+ {
175
+ 'name' => 'WithEntityParam',
176
+ 'in' => 'body',
177
+ 'required' => true,
178
+ 'schema' => {
179
+ '$ref' => '#/definitions/postWithEntityParam'
180
+ }
181
+ }
182
+ ]
183
+ end
184
+
185
+ let(:request_body_parameters_definition) do
186
+ {
187
+ 'type' => 'object',
188
+ 'properties' => {
189
+ 'data' => {
190
+ '$ref' => '#/definitions/ApiResponse',
191
+ 'description' => 'request data'
192
+ }
193
+ },
194
+ 'description' => 'put in body with entity parameter'
195
+ }
196
+ end
197
+
198
+ subject do
199
+ get '/swagger_doc/with_entity_param'
200
+ JSON.parse(last_response.body)
201
+ end
202
+
203
+ specify do
204
+ expect(subject['paths']['/with_entity_param']['post']['parameters']).to eql(request_parameters_definition)
205
+ end
206
+
207
+ specify do
208
+ expect(subject['definitions']['ApiResponse']).not_to be_nil
209
+ end
210
+
211
+ specify do
212
+ expect(subject['definitions']['postWithEntityParam']).to eql(request_body_parameters_definition)
213
+ end
214
+ end
159
215
  end
@@ -118,91 +118,117 @@ describe 'swagger spec v2.0' do
118
118
  end
119
119
  end
120
120
 
121
- before do
122
- get '/v3/swagger_doc'
123
- end
121
+ describe 'whole documentation' do
122
+ subject do
123
+ get '/v3/swagger_doc'
124
+ JSON.parse(last_response.body)
125
+ end
124
126
 
125
- let(:json) { JSON.parse(last_response.body) }
127
+ describe 'swagger object' do
128
+ describe 'required keys' do
129
+ it { expect(subject.keys).to include 'swagger' }
130
+ it { expect(subject['swagger']).to eql '2.0' }
131
+ it { expect(subject.keys).to include 'info' }
132
+ it { expect(subject['info']).to be_a Hash }
133
+ it { expect(subject.keys).to include 'paths' }
134
+ it { expect(subject['paths']).to be_a Hash }
135
+ end
126
136
 
127
- describe 'swagger object' do
128
- describe 'required keys' do
129
- it { expect(json.keys).to include 'swagger' }
130
- it { expect(json['swagger']).to eql '2.0' }
131
- it { expect(json.keys).to include 'info' }
132
- it { expect(json['info']).to be_a Hash }
133
- it { expect(json.keys).to include 'paths' }
134
- it { expect(json['paths']).to be_a Hash }
135
- end
137
+ describe 'info object required keys' do
138
+ let(:info) { subject['info'] }
139
+
140
+ it { expect(info.keys).to include 'title' }
141
+ it { expect(info['title']).to be_a String }
142
+ it { expect(info.keys).to include 'version' }
143
+ it { expect(info['version']).to be_a String }
136
144
 
137
- describe 'info object required keys' do
138
- let(:info) { json['info'] }
145
+ describe 'license object' do
146
+ let(:license) { subject['info']['license'] }
139
147
 
140
- it { expect(info.keys).to include 'title' }
141
- it { expect(info['title']).to be_a String }
142
- it { expect(info.keys).to include 'version' }
143
- it { expect(info['version']).to be_a String }
148
+ it { expect(license.keys).to include 'name' }
149
+ it { expect(license['name']).to be_a String }
150
+ it { expect(license.keys).to include 'url' }
151
+ it { expect(license['url']).to be_a String }
152
+ end
144
153
 
145
- describe 'license object' do
146
- let(:license) { json['info']['license'] }
154
+ describe 'contact object' do
155
+ let(:contact) { subject['info']['contact'] }
147
156
 
148
- it { expect(license.keys).to include 'name' }
149
- it { expect(license['name']).to be_a String }
150
- it { expect(license.keys).to include 'url' }
151
- it { expect(license['url']).to be_a String }
152
- end
157
+ it { expect(contact.keys).to include 'name' }
158
+ it { expect(contact['name']).to be_a String }
159
+ it { expect(contact.keys).to include 'email' }
160
+ it { expect(contact['email']).to be_a String }
161
+ it { expect(contact.keys).to include 'url' }
162
+ it { expect(contact['url']).to be_a String }
163
+ end
153
164
 
154
- describe 'contact object' do
155
- let(:contact) { json['info']['contact'] }
165
+ describe 'global tags' do
166
+ let(:tags) { subject['tags'] }
156
167
 
157
- it { expect(contact.keys).to include 'name' }
158
- it { expect(contact['name']).to be_a String }
159
- it { expect(contact.keys).to include 'email' }
160
- it { expect(contact['email']).to be_a String }
161
- it { expect(contact.keys).to include 'url' }
162
- it { expect(contact['url']).to be_a String }
168
+ it { expect(tags).to be_a Array }
169
+ it { expect(tags).not_to be_empty }
170
+ end
163
171
  end
164
- end
165
172
 
166
- describe 'path object' do
167
- let(:paths) { json['paths'] }
173
+ describe 'path object' do
174
+ let(:paths) { subject['paths'] }
168
175
 
169
- it 'hides documentation paths per default' do
170
- expect(paths.keys).not_to include '/swagger_doc', '/swagger_doc/{name}'
171
- end
176
+ it 'hides documentation paths per default' do
177
+ expect(paths.keys).not_to include '/swagger_doc', '/swagger_doc/{name}'
178
+ end
172
179
 
173
- specify do
174
- paths.each_pair do |path, value|
175
- expect(path).to start_with('/')
176
- expect(value).to be_a Hash
177
- expect(value).not_to be_empty
180
+ specify do
181
+ paths.each_pair do |path, value|
182
+ expect(path).to start_with('/')
183
+ expect(value).to be_a Hash
184
+ expect(value).not_to be_empty
178
185
 
179
- value.each do |method, declaration|
180
- expect(http_verbs).to include method
181
- expect(declaration).to have_key('responses')
186
+ value.each do |method, declaration|
187
+ expect(http_verbs).to include method
188
+ expect(declaration).to have_key('responses')
182
189
 
183
- declaration['responses'].each do |status_code, response|
184
- expect(status_code).to match(/\d{3}/)
185
- expect(response).to have_key('description')
190
+ declaration['responses'].each do |status_code, response|
191
+ expect(status_code).to match(/\d{3}/)
192
+ expect(response).to have_key('description')
193
+ end
186
194
  end
187
195
  end
188
196
  end
189
197
  end
190
- end
191
198
 
192
- describe 'definitions object' do
193
- let(:definitions) { json['definitions'] }
194
- specify do
195
- definitions.each do |model, properties|
196
- expect(model).to match(/\w+/)
197
- expect(properties).to have_key('properties')
199
+ describe 'definitions object' do
200
+ let(:definitions) { subject['definitions'] }
201
+
202
+ specify do
203
+ definitions.each do |model, properties|
204
+ expect(model).to match(/\w+/)
205
+ expect(properties).to have_key('properties')
206
+ end
198
207
  end
199
208
  end
200
209
  end
210
+
211
+ describe 'swagger file' do
212
+ it do
213
+ expect(subject).to eql swagger_json
214
+ end
215
+ end
201
216
  end
202
217
 
203
- describe 'swagger file' do
204
- it do
205
- expect(json).to eql swagger_json
218
+ describe 'specific resource documentation' do
219
+ subject do
220
+ get '/v3/swagger_doc/other_thing'
221
+ JSON.parse(last_response.body)
222
+ end
223
+
224
+ let(:tags) { subject['tags'] }
225
+ specify do
226
+ expect(tags).to eql [
227
+ {
228
+ 'name' => 'other_thing',
229
+ 'description' => 'Operations about other_things'
230
+ }
231
+ ]
206
232
  end
207
233
  end
208
234
  end
@@ -1,17 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Grape::Endpoint#path_and_definitions' do
4
- let(:api) do
5
- item = Class.new(Grape::API) do
4
+ let(:item) do
5
+ Class.new(Grape::API) do
6
6
  version 'v1', using: :path
7
7
 
8
8
  resource :item do
9
9
  get '/'
10
10
  end
11
11
  end
12
+ end
13
+
14
+ let(:api) do
15
+ item_api = item
12
16
 
13
17
  Class.new(Grape::API) do
14
- mount item
18
+ mount item_api
15
19
  add_swagger_documentation add_version: true
16
20
  end
17
21
  end
@@ -28,4 +32,21 @@ describe 'Grape::Endpoint#path_and_definitions' do
28
32
  it 'tags the endpoint with the resource name' do
29
33
  expect(subject.first['/v1/item'][:get][:tags]).to eq ['item']
30
34
  end
35
+
36
+ context 'when custom tags are specified' do
37
+ let(:item) do
38
+ Class.new(Grape::API) do
39
+ version 'v1', using: :path
40
+
41
+ resource :item do
42
+ desc 'Item description', tags: ['special-item']
43
+ get '/'
44
+ end
45
+ end
46
+ end
47
+
48
+ it 'tags the endpoint with the custom tags' do
49
+ expect(subject.first['/v1/item'][:get][:tags]).to eq ['special-item']
50
+ end
51
+ end
31
52
  end