Skip to content

Commit 294740b

Browse files
bikolyaLeFnord
authored andcommitted
Add an option to add root element to responses (#761)
1 parent 5c24f1b commit 294740b

File tree

5 files changed

+220
-6
lines changed

5 files changed

+220
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
#### Features
44

55
* Your contribution here.
6+
* [#761](https://github.com/ruby-grape/grape-swagger/pull/761): Add an option to configure root element for responses - [@bikolya](https://github.com/bikolya).
67

78
#### Fixes
89

10+
* Your contribution here.
911
* [#758](https://github.com/ruby-grape/grape-swagger/pull/758): Handle cases where a route's prefix is a nested URL - [@SimonKaluza](https://github.com/simonkaluza).
1012
* [#757](https://github.com/ruby-grape/grape-swagger/pull/757): Fix `array_use_braces` for nested body params - [@bikolya](https://github.com/bikolya).
1113
* [#756](https://github.com/ruby-grape/grape-swagger/pull/756): Fix reference creation when custom type for documentation is provided - [@bikolya](https://github.com/bikolya).

README.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ end
189189
* [base_path](#base_path)
190190
* [mount_path](#mount_path)
191191
* [add_base_path](#add_base_path)
192+
* [add_root](#add_root)
192193
* [add_version](#add_version)
193194
* [doc_version](#doc_version)
194195
* [endpoint_auth_wrapper](#endpoint_auth_wrapper)
@@ -248,6 +249,13 @@ add_swagger_documentation \
248249
add_base_path: true # only if base_path given
249250
```
250251

252+
#### add_root: <a name="add_root"></a>
253+
Add root element to all the responses, default is: `false`.
254+
```ruby
255+
add_swagger_documentation \
256+
add_root: true
257+
```
258+
251259
#### add_version: <a name="add_version"></a>
252260

253261
Add `version` key to the documented path keys, default is: `true`,
@@ -447,6 +455,7 @@ add_swagger_documentation \
447455
* [Extensions](#extensions)
448456
* [Response examples documentation](#response-examples)
449457
* [Response headers documentation](#response-headers)
458+
* [Adding root element to responses](#response-root)
450459

451460
#### Swagger Header Parameters <a name="headers"></a>
452461

@@ -913,7 +922,7 @@ desc 'Attach a field to an entity through a PUT',
913922
failure: [
914923
{ code: 400, message: 'Bad request' },
915924
{ code: 404, message: 'Not found' }
916-
]
925+
]
917926
put do
918927
# your code comes here
919928
end
@@ -1180,6 +1189,60 @@ The result will look like following:
11801189

11811190
Failure information can be passed as an array of arrays or an array of hashes.
11821191

1192+
#### Adding root element to responses <a name="response-root"></a>
1193+
1194+
You can specify a custom root element for a successful response:
1195+
1196+
```ruby
1197+
route_setting :swagger, root: 'cute_kitten'
1198+
desc 'Get a kitten' do
1199+
http_codes [{ code: 200, model: Entities::Kitten }]
1200+
end
1201+
get '/kittens/:id' do
1202+
end
1203+
```
1204+
1205+
The result will look like following:
1206+
1207+
```
1208+
"responses": {
1209+
"200": {
1210+
"description": "Get a kitten",
1211+
"schema": {
1212+
"type": "object",
1213+
"properties": { "cute_kitten": { "$ref": "#/definitions/Kitten" } }
1214+
}
1215+
}
1216+
}
1217+
```
1218+
1219+
If you specify `true`, the value of the root element will be deduced based on the model name.
1220+
E.g. in the following example the root element will be "kittens":
1221+
1222+
```ruby
1223+
route_setting :swagger, root: true
1224+
desc 'Get kittens' do
1225+
is_array true
1226+
http_codes [{ code: 200, model: Entities::Kitten }]
1227+
end
1228+
get '/kittens' do
1229+
end
1230+
```
1231+
1232+
The result will look like following:
1233+
1234+
```
1235+
"responses": {
1236+
"200": {
1237+
"description": "Get kittens",
1238+
"schema": {
1239+
"type": "object",
1240+
"properties": { "type": "array", "items": { "kittens": { "$ref": "#/definitions/Kitten" } } }
1241+
}
1242+
}
1243+
}
1244+
```
1245+
11831246
## Using Grape Entities <a name="grape-entity"></a>
11841247

11851248
Add the [grape-entity](https://github.com/ruby-grape/grape-entity) and [grape-swagger-entity](https://github.com/ruby-grape/grape-swagger-entity) gem to your Gemfile.

lib/grape-swagger/doc_methods.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def defaults
103103
base_path: nil,
104104
add_base_path: false,
105105
add_version: true,
106+
add_root: false,
106107
hide_documentation_path: true,
107108
format: :json,
108109
authorizations: nil,

lib/grape-swagger/endpoint.rb

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def method_object(route, options, path)
120120
method[:consumes] = consumes_object(route, options[:format])
121121
method[:parameters] = params_object(route, options, path)
122122
method[:security] = security_object(route)
123-
method[:responses] = response_object(route)
123+
method[:responses] = response_object(route, options)
124124
method[:tags] = route.options.fetch(:tags, tag_object(route, path))
125125
method[:operationId] = GrapeSwagger::DocMethods::OperationId.build(route, path)
126126
method[:deprecated] = deprecated_object(route)
@@ -195,7 +195,7 @@ def params_object(route, options, path)
195195
parameters.presence
196196
end
197197

198-
def response_object(route)
198+
def response_object(route, options)
199199
codes = http_codes_from_route(route)
200200
codes.map! { |x| x.is_a?(Array) ? { code: x[0], message: x[1], model: x[2], examples: x[3], headers: x[4] } : x }
201201

@@ -220,7 +220,7 @@ def response_object(route)
220220

221221
@definitions[response_model][:description] = description_object(route)
222222

223-
memo[value[:code]][:schema] = build_reference(route, value, response_model)
223+
memo[value[:code]][:schema] = build_reference(route, value, response_model, options)
224224
memo[value[:code]][:examples] = value[:examples] if value[:examples]
225225
end
226226
end
@@ -261,10 +261,27 @@ def tag_object(route, path)
261261

262262
private
263263

264-
def build_reference(route, value, response_model)
264+
def build_reference(route, value, response_model, settings)
265265
# TODO: proof that the definition exist, if model isn't specified
266266
reference = { '$ref' => "#/definitions/#{response_model}" }
267-
route.options[:is_array] && value[:code] < 300 ? { type: 'array', items: reference } : reference
267+
return reference unless value[:code] < 300
268+
269+
reference = { type: 'array', items: reference } if route.options[:is_array]
270+
build_root(route, reference, response_model, settings)
271+
end
272+
273+
def build_root(route, reference, response_model, settings)
274+
default_root = route.options[:is_array] ? response_model.downcase.pluralize : response_model.downcase
275+
case route.settings.dig(:swagger, :root)
276+
when true
277+
{ type: 'object', properties: { default_root => reference } }
278+
when false
279+
reference
280+
when nil
281+
settings[:add_root] ? { type: 'object', properties: { default_root => reference } } : reference
282+
else
283+
{ type: 'object', properties: { route.settings.dig(:swagger, :root) => reference } }
284+
end
268285
end
269286

270287
def file_response?(value)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe 'response with root' do
6+
include_context "#{MODEL_PARSER} swagger example"
7+
8+
before :all do
9+
module TheApi
10+
class ResponseApiWithRoot < Grape::API
11+
format :json
12+
13+
desc 'This returns something',
14+
http_codes: [{ code: 200, model: Entities::Something }]
15+
get '/ordinary_response' do
16+
{ 'declared_params' => declared(params) }
17+
end
18+
19+
desc 'This returns something',
20+
is_array: true,
21+
http_codes: [{ code: 200, model: Entities::Something }]
22+
get '/response_with_array' do
23+
{ 'declared_params' => declared(params) }
24+
end
25+
26+
route_setting :swagger, root: true
27+
desc 'This returns something',
28+
http_codes: [{ code: 200, model: Entities::Something }]
29+
get '/response_with_root' do
30+
{ 'declared_params' => declared(params) }
31+
end
32+
33+
route_setting :swagger, root: true
34+
desc 'This returns something',
35+
is_array: true,
36+
http_codes: [{ code: 200, model: Entities::Something }]
37+
get '/response_with_array_and_root' do
38+
{ 'declared_params' => declared(params) }
39+
end
40+
41+
route_setting :swagger, root: 'custom_root'
42+
desc 'This returns something',
43+
http_codes: [{ code: 200, model: Entities::Something }]
44+
get '/response_with_custom_root' do
45+
{ 'declared_params' => declared(params) }
46+
end
47+
48+
add_swagger_documentation
49+
end
50+
end
51+
end
52+
53+
def app
54+
TheApi::ResponseApiWithRoot
55+
end
56+
57+
describe 'GET /ordinary_response' do
58+
subject do
59+
get '/swagger_doc/ordinary_response'
60+
JSON.parse(last_response.body)
61+
end
62+
63+
it 'does not add root or array' do
64+
schema = subject.dig('paths', '/ordinary_response', 'get', 'responses', '200', 'schema')
65+
expect(schema).to eq(
66+
'$ref' => '#/definitions/Something'
67+
)
68+
end
69+
end
70+
71+
describe 'GET /response_with_array' do
72+
subject do
73+
get '/swagger_doc/response_with_array'
74+
JSON.parse(last_response.body)
75+
end
76+
77+
it 'adds array to the response' do
78+
schema = subject.dig('paths', '/response_with_array', 'get', 'responses', '200', 'schema')
79+
expect(schema).to eq(
80+
'type' => 'array', 'items' => { '$ref' => '#/definitions/Something' }
81+
)
82+
end
83+
end
84+
85+
describe 'GET /response_with_root' do
86+
subject do
87+
get '/swagger_doc/response_with_root'
88+
JSON.parse(last_response.body)
89+
end
90+
91+
it 'adds root to the response' do
92+
schema = subject.dig('paths', '/response_with_root', 'get', 'responses', '200', 'schema')
93+
expect(schema).to eq(
94+
'type' => 'object',
95+
'properties' => { 'something' => { '$ref' => '#/definitions/Something' } }
96+
)
97+
end
98+
end
99+
100+
describe 'GET /response_with_array_and_root' do
101+
subject do
102+
get '/swagger_doc/response_with_array_and_root'
103+
JSON.parse(last_response.body)
104+
end
105+
106+
it 'adds root and array to the response' do
107+
schema = subject.dig('paths', '/response_with_array_and_root', 'get', 'responses', '200', 'schema')
108+
expect(schema).to eq(
109+
'type' => 'object',
110+
'properties' => {
111+
'somethings' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/Something' } }
112+
}
113+
)
114+
end
115+
end
116+
117+
describe 'GET /response_with_custom_root' do
118+
subject do
119+
get '/swagger_doc/response_with_custom_root'
120+
JSON.parse(last_response.body)
121+
end
122+
123+
it 'adds root to the response' do
124+
schema = subject.dig('paths', '/response_with_custom_root', 'get', 'responses', '200', 'schema')
125+
expect(schema).to eq(
126+
'type' => 'object',
127+
'properties' => { 'custom_root' => { '$ref' => '#/definitions/Something' } }
128+
)
129+
end
130+
end
131+
end

0 commit comments

Comments
 (0)