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
@@ -48,20 +48,23 @@ describe 'additional parameter settings' do
48
48
  [
49
49
  { 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true },
50
50
  { 'in' => 'formData', 'name' => 'name', 'type' => 'string', 'required' => false }
51
- ])
51
+ ]
52
+ )
52
53
  end
53
54
 
54
55
  specify do
55
56
  expect(subject['paths']['/bookings/{id}']['get']['parameters']).to eql(
56
57
  [
57
58
  { 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }
58
- ])
59
+ ]
60
+ )
59
61
  end
60
62
 
61
63
  specify do
62
64
  expect(subject['paths']['/bookings/{id}']['delete']['parameters']).to eql(
63
65
  [
64
66
  { 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }
65
- ])
67
+ ]
68
+ )
66
69
  end
67
70
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'response' do
4
- include_context 'the api entities'
4
+ include_context "#{MODEL_PARSER} swagger example"
5
5
 
6
6
  before :all do
7
7
  module TheApi
@@ -45,6 +45,7 @@ describe 'response' do
45
45
  end
46
46
  specify do
47
47
  expect(subject['paths']['/nested_type']['get']).to eql(
48
+ 'summary' => 'This returns something',
48
49
  'description' => 'This returns something',
49
50
  'produces' => ['application/json'],
50
51
  'responses' => {
@@ -52,22 +53,9 @@ describe 'response' do
52
53
  '400' => { 'description' => 'NotFound', 'schema' => { '$ref' => '#/definitions/ApiError' } }
53
54
  },
54
55
  'tags' => ['nested_type'],
55
- 'operationId' => 'getNestedType')
56
- expect(subject['definitions']).to eql(
57
- 'ResponseItem' => {
58
- 'type' => 'object',
59
- 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } }
60
- },
61
- 'UseItemResponseAsType' => {
62
- 'type' => 'object',
63
- 'properties' => { 'description' => { 'type' => 'string' }, 'responses' => { '$ref' => '#/definitions/ResponseItem' } },
64
- 'description' => 'This returns something'
65
- },
66
- 'ApiError' => {
67
- 'type' => 'object',
68
- 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'type' => 'string' } },
69
- 'description' => 'This returns something'
70
- })
56
+ 'operationId' => 'getNestedType'
57
+ )
58
+ expect(subject['definitions']).to eql(swagger_nested_type)
71
59
  end
72
60
  end
73
61
 
@@ -79,6 +67,7 @@ describe 'response' do
79
67
 
80
68
  specify do
81
69
  expect(subject['paths']['/entity_response']['get']).to eql(
70
+ 'summary' => 'This returns something',
82
71
  'description' => 'This returns something',
83
72
  'produces' => ['application/json'],
84
73
  'responses' => {
@@ -86,22 +75,9 @@ describe 'response' do
86
75
  '400' => { 'description' => 'NotFound', 'schema' => { '$ref' => '#/definitions/ApiError' } }
87
76
  },
88
77
  'tags' => ['entity_response'],
89
- 'operationId' => 'getEntityResponse')
90
- expect(subject['definitions']).to eql(
91
- 'ResponseItem' => {
92
- 'type' => 'object',
93
- 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } }
94
- },
95
- 'UseResponse' => {
96
- 'type' => 'object',
97
- 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } },
98
- 'description' => 'This returns something'
99
- },
100
- 'ApiError' => {
101
- 'type' => 'object',
102
- 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'type' => 'string' } },
103
- 'description' => 'This returns something'
104
- })
78
+ 'operationId' => 'getEntityResponse'
79
+ )
80
+ expect(subject['definitions']).to eql(swagger_entity_as_response_object)
105
81
  end
106
82
  end
107
83
 
@@ -113,6 +89,7 @@ describe 'response' do
113
89
 
114
90
  specify do
115
91
  expect(subject['paths']['/params_response']['post']).to eql(
92
+ 'summary' => 'This returns something',
116
93
  'description' => 'This returns something',
117
94
  'produces' => ['application/json'],
118
95
  'consumes' => ['application/json'],
@@ -125,13 +102,9 @@ describe 'response' do
125
102
  '400' => { 'description' => 'NotFound', 'schema' => { '$ref' => '#/definitions/ApiError' } }
126
103
  },
127
104
  'tags' => ['params_response'],
128
- 'operationId' => 'postParamsResponse')
129
- expect(subject['definitions']).to eql(
130
- 'ApiError' => {
131
- 'type' => 'object',
132
- 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'type' => 'string' } },
133
- 'description' => 'This returns something'
134
- })
105
+ 'operationId' => 'postParamsResponse'
106
+ )
107
+ expect(subject['definitions']).to eql(swagger_params_as_response_object)
135
108
  end
136
109
  end
137
110
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'swagger spec v2.0' do
4
- include_context 'swagger example'
4
+ include_context "#{MODEL_PARSER} swagger example"
5
5
 
6
6
  def app
7
7
  Class.new(Grape::API) do
@@ -201,6 +201,8 @@ describe 'swagger spec v2.0' do
201
201
  end
202
202
 
203
203
  describe 'swagger file' do
204
- it { expect(json).to eql swagger_json }
204
+ it do
205
+ expect(json).to eql swagger_json
206
+ end
205
207
  end
206
208
  end
@@ -20,30 +20,13 @@ require 'spec_helper'
20
20
  # Rack::Multipart::UploadedFile -> file
21
21
 
22
22
  describe 'type format settings' do
23
+ include_context "#{MODEL_PARSER} swagger example"
24
+
23
25
  before :all do
24
26
  module TheApi
25
- module Entities
26
- class TypedDefinition < Grape::Entity
27
- expose :prop_integer, documentation: { type: Integer, desc: 'prop_integer description' }
28
- expose :prop_long, documentation: { type: Numeric, desc: 'prop_long description' }
29
- expose :prop_float, documentation: { type: Float, desc: 'prop_float description' }
30
- expose :prop_double, documentation: { type: BigDecimal, desc: 'prop_double description' }
31
- expose :prop_string, documentation: { type: String, desc: 'prop_string description' }
32
- expose :prop_symbol, documentation: { type: Symbol, desc: 'prop_symbol description' }
33
- expose :prop_date, documentation: { type: Date, desc: 'prop_date description' }
34
- expose :prop_date_time, documentation: { type: DateTime, desc: 'prop_date_time description' }
35
- expose :prop_time, documentation: { type: Time, desc: 'prop_time description' }
36
- expose :prop_password, documentation: { type: 'password', desc: 'prop_password description' }
37
- expose :prop_email, documentation: { type: 'email', desc: 'prop_email description' }
38
- expose :prop_boolean, documentation: { type: Virtus::Attribute::Boolean, desc: 'prop_boolean description' }
39
- expose :prop_file, documentation: { type: File, desc: 'prop_file description' }
40
- expose :prop_json, documentation: { type: JSON, desc: 'prop_json description' }
41
- end
42
- end
43
-
44
27
  class TypeFormatApi < Grape::API
45
28
  desc 'full set of request data types',
46
- success: TheApi::Entities::TypedDefinition
29
+ success: Entities::TypedDefinition
47
30
 
48
31
  params do
49
32
  # grape supported data types
@@ -94,24 +77,11 @@ describe 'type format settings' do
94
77
  { 'in' => 'formData', 'name' => 'param_boolean', 'required' => false, 'type' => 'boolean' },
95
78
  { 'in' => 'formData', 'name' => 'param_file', 'required' => false, 'type' => 'file' },
96
79
  { 'in' => 'formData', 'name' => 'param_json', 'required' => false, 'type' => 'json' }
97
- ])
80
+ ]
81
+ )
98
82
  end
99
83
 
100
84
  specify do
101
- expect(subject['definitions']['TypedDefinition']['properties']).to eql(
102
- 'prop_integer' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'prop_integer description' },
103
- 'prop_long' => { 'type' => 'integer', 'format' => 'int64', 'description' => 'prop_long description' },
104
- 'prop_float' => { 'type' => 'number', 'format' => 'float', 'description' => 'prop_float description' },
105
- 'prop_double' => { 'type' => 'number', 'format' => 'double', 'description' => 'prop_double description' },
106
- 'prop_string' => { 'type' => 'string', 'description' => 'prop_string description' },
107
- 'prop_symbol' => { 'type' => 'string', 'description' => 'prop_symbol description' },
108
- 'prop_date' => { 'type' => 'string', 'format' => 'date', 'description' => 'prop_date description' },
109
- 'prop_date_time' => { 'type' => 'string', 'format' => 'date-time', 'description' => 'prop_date_time description' },
110
- 'prop_time' => { 'type' => 'string', 'format' => 'date-time', 'description' => 'prop_time description' },
111
- 'prop_password' => { 'type' => 'string', 'format' => 'password', 'description' => 'prop_password description' },
112
- 'prop_email' => { 'type' => 'string', 'format' => 'email', 'description' => 'prop_email description' },
113
- 'prop_boolean' => { 'type' => 'boolean', 'description' => 'prop_boolean description' },
114
- 'prop_file' => { 'type' => 'file', 'description' => 'prop_file description' },
115
- 'prop_json' => { 'type' => 'json', 'description' => 'prop_json description' })
85
+ expect(subject['definitions']['TypedDefinition']['properties']).to eql(swagger_typed_defintion)
116
86
  end
117
87
  end
@@ -29,11 +29,15 @@ describe 'Default API' do
29
29
  'paths' => {
30
30
  '/something' => {
31
31
  'get' => {
32
+ 'summary' => 'This gets something.',
32
33
  'description' => 'This gets something.',
33
34
  'produces' => ['application/json'],
34
35
  'tags' => ['something'],
35
36
  'operationId' => 'getSomething',
36
- 'responses' => { '200' => { 'description' => 'This gets something.' } } } } }
37
+ 'responses' => { '200' => { 'description' => 'This gets something.' } }
38
+ }
39
+ }
40
+ }
37
41
  )
38
42
  end
39
43
 
@@ -72,11 +76,15 @@ describe 'Default API' do
72
76
  'paths' => {
73
77
  '/something' => {
74
78
  'get' => {
79
+ 'summary' => 'This gets something.',
75
80
  'description' => 'This gets something.',
76
81
  'produces' => ['application/json'],
77
82
  'tags' => ['something'],
78
83
  'operationId' => 'getSomething',
79
- 'responses' => { '200' => { 'description' => 'This gets something.' } } } } })
84
+ 'responses' => { '200' => { 'description' => 'This gets something.' } }
85
+ }
86
+ }
87
+ })
80
88
  end
81
89
  end
82
90
 
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Grape::Endpoint#path_and_definitions' do
4
+ before do
5
+ module API
6
+ module V1
7
+ class Item < Grape::API
8
+ version 'v1', using: :path
9
+
10
+ resource :item do
11
+ get '/'
12
+ end
13
+ end
14
+ end
15
+
16
+ class Root < Grape::API
17
+ mount API::V1::Item
18
+ add_swagger_documentation add_version: true
19
+ end
20
+ end
21
+
22
+ @options = { add_version: true }
23
+ @target_routes = API::Root.combined_namespace_routes
24
+ end
25
+
26
+ it 'is returning a versioned path' do
27
+ expect(API::V1::Item.endpoints[0]
28
+ .path_and_definition_objects(@target_routes, @options)[0].keys[0]).to eql '/v1/item'
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Errors' do
4
+ describe 'Empty model error' do
5
+ let!(:app) do
6
+ Class.new(Grape::API) do
7
+ format :json
8
+
9
+ desc 'Empty model get.' do
10
+ http_codes [
11
+ { code: 200, message: 'get Empty model', model: EmptyClass }
12
+ ]
13
+ end
14
+ get '/empty_model' do
15
+ something = OpenStruct.new text: 'something'
16
+ present something, with: EmptyClass
17
+ end
18
+
19
+ version 'v3', using: :path
20
+ add_swagger_documentation api_version: 'v1',
21
+ base_path: '/api',
22
+ info: {
23
+ title: 'The API title to be displayed on the API homepage.',
24
+ description: 'A description of the API.',
25
+ contact_name: 'Contact name',
26
+ contact_email: 'Contact@email.com',
27
+ contact_url: 'Contact URL',
28
+ license: 'The name of the license.',
29
+ license_url: 'www.The-URL-of-the-license.org',
30
+ terms_of_service_url: 'www.The-URL-of-the-terms-and-service.com'
31
+ }
32
+ end
33
+ end
34
+
35
+ it 'should raise SwaggerSpec exception' do
36
+ expect { get '/v3/swagger_doc' }.to raise_error(GrapeSwagger::Errors::SwaggerSpec, "Empty model EmptyClass, swagger 2.0 doesn't support empty definitions.")
37
+ end
38
+ end
39
+
40
+ describe 'Parser not found error' do
41
+ let!(:app) do
42
+ Class.new(Grape::API) do
43
+ format :json
44
+
45
+ desc 'Wrong model get.' do
46
+ http_codes [
47
+ { code: 200, message: 'get Wrong model', model: Hash }
48
+ ]
49
+ end
50
+ get '/wrong_model' do
51
+ something = OpenStruct.new text: 'something'
52
+ present something, with: Hash
53
+ end
54
+
55
+ version 'v3', using: :path
56
+ add_swagger_documentation api_version: 'v1',
57
+ base_path: '/api',
58
+ info: {
59
+ title: 'The API title to be displayed on the API homepage.',
60
+ description: 'A description of the API.',
61
+ contact_name: 'Contact name',
62
+ contact_email: 'Contact@email.com',
63
+ contact_url: 'Contact URL',
64
+ license: 'The name of the license.',
65
+ license_url: 'www.The-URL-of-the-license.org',
66
+ terms_of_service_url: 'www.The-URL-of-the-terms-and-service.com'
67
+ }
68
+ end
69
+ end
70
+
71
+ it 'should raise UnregisteredParser exception' do
72
+ expect { get '/v3/swagger_doc' }.to raise_error(GrapeSwagger::Errors::UnregisteredParser, 'No parser registered for Hash.')
73
+ end
74
+ end
75
+ end
@@ -44,18 +44,26 @@ describe 'a hide mounted api' do
44
44
  'paths' => {
45
45
  '/simple' => {
46
46
  'get' => {
47
+ 'summary' => 'Show this endpoint',
47
48
  'description' => 'Show this endpoint',
48
49
  'produces' => ['application/json'],
49
50
  'tags' => ['simple'],
50
51
  'operationId' => 'getSimple',
51
- 'responses' => { '200' => { 'description' => 'Show this endpoint' } } } },
52
+ 'responses' => { '200' => { 'description' => 'Show this endpoint' } }
53
+ }
54
+ },
52
55
  '/lazy' => {
53
56
  'get' => {
57
+ 'summary' => 'Lazily show endpoint',
54
58
  'description' => 'Lazily show endpoint',
55
59
  'produces' => ['application/json'],
56
60
  'tags' => ['lazy'],
57
61
  'operationId' => 'getLazy',
58
- 'responses' => { '200' => { 'description' => 'Lazily show endpoint' } } } } })
62
+ 'responses' => { '200' => { 'description' => 'Lazily show endpoint' } }
63
+ }
64
+ }
65
+ }
66
+ )
59
67
  end
60
68
  end
61
69
 
@@ -99,10 +107,15 @@ describe 'a hide mounted api with same namespace' do
99
107
  'paths' => {
100
108
  '/simple/show' => {
101
109
  'get' => {
110
+ 'summary' => 'Show this endpoint',
102
111
  'description' => 'Show this endpoint',
103
112
  'produces' => ['application/json'],
104
113
  'operationId' => 'getSimpleShow',
105
- 'tags' => ['simple'], 'responses' => { '200' => { 'description' => 'Show this endpoint' } } } } })
114
+ 'tags' => ['simple'], 'responses' => { '200' => { 'description' => 'Show this endpoint' } }
115
+ }
116
+ }
117
+ }
118
+ )
106
119
  end
107
120
 
108
121
  it "retrieves the documentation for mounted-api that doesn't include hidden endpoints" do
@@ -116,10 +129,15 @@ describe 'a hide mounted api with same namespace' do
116
129
  'paths' => {
117
130
  '/simple/show' => {
118
131
  'get' => {
132
+ 'summary' => 'Show this endpoint',
119
133
  'description' => 'Show this endpoint',
120
134
  'produces' => ['application/json'],
121
135
  'tags' => ['simple'],
122
136
  'operationId' => 'getSimpleShow',
123
- 'responses' => { '200' => { 'description' => 'Show this endpoint' } } } } })
137
+ 'responses' => { '200' => { 'description' => 'Show this endpoint' } }
138
+ }
139
+ }
140
+ }
141
+ )
124
142
  end
125
143
  end
@@ -37,6 +37,7 @@ describe 'docs mounted separately from api' do
37
37
  'paths' => {
38
38
  '/simple' => {
39
39
  'get' => {
40
+ 'summary' => 'This gets something.',
40
41
  'description' => 'This gets something.',
41
42
  'produces' => ['application/json'],
42
43
  'responses' => { '200' => { 'description' => 'This gets something.' } },
@@ -44,7 +45,8 @@ describe 'docs mounted separately from api' do
44
45
  'operationId' => 'getSimple'
45
46
  }
46
47
  }
47
- })
48
+ }
49
+ )
48
50
  end
49
51
 
50
52
  it 'retrieves docs for endpoint in actual api class' do
@@ -58,6 +60,7 @@ describe 'docs mounted separately from api' do
58
60
  'paths' => {
59
61
  '/simple' => {
60
62
  'get' => {
63
+ 'summary' => 'This gets something.',
61
64
  'description' => 'This gets something.',
62
65
  'produces' => ['application/json'],
63
66
  'responses' => {
@@ -67,6 +70,7 @@ describe 'docs mounted separately from api' do
67
70
  'operationId' => 'getSimple'
68
71
  }
69
72
  }
70
- })
73
+ }
74
+ )
71
75
  end
72
76
  end
@@ -34,7 +34,8 @@ describe 'namespace tags check while using prefix and version' do
34
34
  { 'name' => 'colorado', 'description' => 'Operations about colorados' },
35
35
  { 'name' => 'thames', 'description' => 'Operations about thames' },
36
36
  { 'name' => 'niles', 'description' => 'Operations about niles' }
37
- ])
37
+ ]
38
+ )
38
39
 
39
40
  expect(subject['paths']['/api/v1/hudson']['get']['tags']).to eql(['hudson'])
40
41
  expect(subject['paths']['/api/v1/colorado/simple']['get']['tags']).to eql(['colorado'])
@@ -58,7 +59,8 @@ describe 'namespace tags check while using prefix and version' do
58
59
  { 'name' => 'colorado', 'description' => 'Operations about colorados' },
59
60
  { 'name' => 'thames', 'description' => 'Operations about thames' },
60
61
  { 'name' => 'niles', 'description' => 'Operations about niles' }
61
- ])
62
+ ]
63
+ )
62
64
 
63
65
  expect(subject['paths']['/api/v1/colorado/simple']['get']['tags']).to eql(['colorado'])
64
66
  expect(subject['paths']['/api/v1/colorado/simple-test']['get']['tags']).to eql(['colorado'])
@@ -77,7 +79,8 @@ describe 'namespace tags check while using prefix and version' do
77
79
  { 'name' => 'colorado', 'description' => 'Operations about colorados' },
78
80
  { 'name' => 'thames', 'description' => 'Operations about thames' },
79
81
  { 'name' => 'niles', 'description' => 'Operations about niles' }
80
- ])
82
+ ]
83
+ )
81
84
 
82
85
  expect(subject['paths']['/api/v1/thames/simple_with_headers']['get']['tags']).to eql(['thames'])
83
86
  end