Skip to content

Commit 9186d70

Browse files
author
Peter Scholz
committed
WIP: swagger2.0: adds schema to response
1 parent e406abf commit 9186d70

File tree

3 files changed

+124
-50
lines changed

3 files changed

+124
-50
lines changed

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,9 @@ end
504504

505505
## Response documentation
506506

507-
You can also document the HTTP status codes with a description ~~and a specified model~~ that your API returns with one of the following syntax.
507+
You can also document the HTTP status codes with a description and a specified model, as ref in the schema to the definitions, that your API returns with one of the following syntax.
508508

509+
In the following cases, the schema ref would be taken from route.
509510
``` ruby
510511
desc 'thing', http_codes: [ { code: 400, message: "Invalid parameter entry" } ]
511512
get '/thing' do
@@ -524,16 +525,44 @@ end
524525
```
525526

526527
``` ruby
527-
get '/', http_codes: [
528+
get '/thing', http_codes: [
528529
{ code: 200, message: 'Ok' },
529530
{ code: 400, message: "Invalid parameter entry" }
530531
] do
531532
...
532533
end
533534
```
534535

536+
By adding a `model` key, e.g. this would be taken.
537+
``` ruby
538+
get '/thing', http_codes: [
539+
{ code: 200, message: 'Ok' },
540+
{ code: 422, message: "Invalid parameter entry", model: Entities::ApiError }
541+
] do
542+
...
543+
end
544+
```
545+
535546
If no status code is defined [defaults](/lib/grape-swagger/endpoint.rb#L121) would be taken.
536547

548+
The result is then something like following:
549+
550+
```json
551+
"responses": {
552+
"200": {
553+
"description": "get Horses",
554+
"schema": {
555+
"$ref": "#/definitions/Thing"
556+
}
557+
},
558+
"401": {
559+
"description": "HorsesOutError",
560+
"schema": {
561+
"$ref": "#/definitions/ApiError"
562+
}
563+
}
564+
},
565+
```
537566

538567
## Contributing to grape-swagger
539568

lib/grape-swagger/endpoint.rb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,21 @@ def method_object(method, route, options)
115115

116116
def response_object(route)
117117
codes = default_staus_codes[route.route_method.downcase.to_sym] + (route.route_http_codes || [])
118-
codes.inject({}) {|h, v| h[v[:code]] = {description: v[:message].sub('{item}',@item)}; h }
118+
119+
codes.map!{|x| x.is_a?(Array)? {code: x[0], message: x[1], model: x[2].to_s} : x }
120+
121+
codes.inject({}) do |h, v|
122+
h[v[:code]] = { description: v[:message].sub('{item}',@item) }
123+
124+
response_model = @item
125+
response_model = expose_params_from_model(v[:model]) if v[:model]
126+
127+
# TODO: proof that the definition exist, if model isn't specified
128+
unless response_model.start_with?('Swagger_doc')
129+
h[v[:code]][:schema] = { '$ref' => "#/definitions/#{response_model}" }
130+
end
131+
h
132+
end
119133
end
120134

121135
def default_staus_codes
@@ -158,6 +172,19 @@ def parse_expose_params(params, route)
158172
@definitions[@item] = {properties: properties}
159173
end
160174

175+
def expose_params_from_model(model)
176+
model_name = model.name.split('::').last
177+
178+
properties = model.documentation.inject({}) do |h,x|
179+
h[x.first] = {type: data_type(x.last)}
180+
h[x.first][:enum] = x.last[:values] if x.last[:values] && x.last[:values].is_a?(Array)
181+
h
182+
end
183+
@definitions[model_name] = {properties: properties}
184+
185+
model_name
186+
end
187+
161188
def parse_request_params(param, value, path, method)
162189
items = {}
163190

spec/api_swagger_v2_spec.rb

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Something < Grape::Entity
1313

1414
class EnumValues < Grape::Entity
1515
expose :gender, documentation: { type: 'string', desc: 'Content of something.', values: %w(Male Female) }
16-
expose :number, documentation: { type: 'integer', desc: 'Content of something.', values: proc { [1, 2] } }
16+
expose :number, documentation: { type: 'integer', desc: 'Content of something.', values: [1, 2] }
1717
end
1818

1919
class ComposedOf < Grape::Entity
@@ -76,6 +76,12 @@ class ThingWithRoot < Grape::Entity
7676
root 'things', 'thing'
7777
expose :text, documentation: { type: 'string', desc: 'Content of something.' }
7878
end
79+
80+
class ApiError < Grape::Entity
81+
expose :code, documentation: { type: Integer, desc: 'status code' }
82+
expose :message, documentation: { type: String, desc: 'error message' }
83+
end
84+
7985
end
8086

8187
end
@@ -84,62 +90,74 @@ def app
8490
Class.new(Grape::API) do
8591
format :json
8692

87-
# # Something stuff
88-
# desc 'This gets Somethings.', entity: Entities::Something, params: Entities::Something.documentation
89-
# get '/something' do
90-
# something = OpenStruct.new text: 'something'
91-
# present something, with: Entities::Something
92-
# end
93-
#
94-
# desc 'This gets Something.', entity: Entities::Something, params: Entities::Something.documentation
95-
# params do
96-
# requires :id, type: Integer, desc: 'Identity'
97-
# end
98-
# get '/something/:id' do
99-
# something = OpenStruct.new text: 'something'
100-
# present something, with: Entities::Something
101-
# end
102-
#
103-
# desc 'This creates Something.', entity: Entities::Something, params: Entities::Something.documentation
104-
# params do
105-
# requires :text, type: String, documentation: { type: 'string', desc: 'Content of something.' }
106-
# requires :links, type: Array, documentation: { type: 'link', is_array: true }
107-
# end
108-
# post '/something' do
109-
# something = OpenStruct.new text: 'something'
110-
# present something, with: Entities::Something
111-
# end
112-
#
113-
# desc 'This updates Something.', entity: Entities::Something, params: Entities::Something.documentation
114-
# params do
115-
# requires :id, type: Integer
116-
# optional :text, type: String, documentation: { type: 'string', desc: 'Content of something.' }
117-
# optional :links, type: Array, documentation: { type: 'link', is_array: true }
118-
# end
119-
# put '/something/:id' do
120-
# something = OpenStruct.new text: 'something'
121-
# present something, with: Entities::Something
122-
# end
123-
#
124-
# desc 'This deletes something.', entity: Entities::Something, params: Entities::Something.documentation
125-
# params do
126-
# requires :id, type: Integer
127-
# end
128-
# delete '/something/:id' do
129-
# something = OpenStruct.new text: 'something'
130-
# present something, with: Entities::Something
131-
# end
93+
# Something stuff
94+
desc 'This gets Somethings.', entity: Entities::Something, params: Entities::Something.documentation
95+
get '/something' do
96+
something = OpenStruct.new text: 'something'
97+
present something, with: Entities::Something
98+
end
99+
100+
desc 'This gets Something.', entity: Entities::Something, params: Entities::Something.documentation
101+
params do
102+
requires :id, type: Integer, desc: 'Identity'
103+
end
104+
get '/something/:id' do
105+
something = OpenStruct.new text: 'something'
106+
present something, with: Entities::Something
107+
end
108+
109+
desc 'This creates Something.', entity: Entities::Something, params: Entities::Something.documentation
110+
params do
111+
requires :text, type: String, documentation: { type: 'string', desc: 'Content of something.' }
112+
requires :links, type: Array, documentation: { type: 'link', is_array: true }
113+
end
114+
post '/something' do
115+
something = OpenStruct.new text: 'something'
116+
present something, with: Entities::Something
117+
end
118+
119+
desc 'This updates Something.', entity: Entities::Something, params: Entities::Something.documentation
120+
params do
121+
requires :id, type: Integer
122+
optional :text, type: String, documentation: { type: 'string', desc: 'Content of something.' }
123+
optional :links, type: Array, documentation: { type: 'link', is_array: true }
124+
end
125+
put '/something/:id' do
126+
something = OpenStruct.new text: 'something'
127+
present something, with: Entities::Something
128+
end
129+
130+
desc 'This deletes something.', entity: Entities::Something, params: Entities::Something.documentation
131+
params do
132+
requires :id, type: Integer
133+
end
134+
delete '/something/:id' do
135+
something = OpenStruct.new text: 'something'
136+
present something, with: Entities::Something
137+
end
132138

133139
# Thing stuff
134140
desc 'This gets Things.' do
135141
params Entities::Something.documentation
136-
http_codes [ { code: 401, message: 'Unauthorized' } ]
142+
http_codes [ { code: 401, message: 'Unauthorized', model: Entities::ApiError } ]
137143
end
138144
get '/thing' do
139145
something = OpenStruct.new text: 'something'
140146
present something, with: Entities::Something
141147
end
142148

149+
desc 'This gets Things.' do
150+
params Entities::Something.documentation
151+
http_codes [
152+
{ code: 200, message: 'get Horses', model: Entities::EnumValues },
153+
{ code: 401, message: 'HorsesOutError', model: Entities::ApiError }
154+
]
155+
end
156+
get '/thing2' do
157+
something = OpenStruct.new text: 'something'
158+
present something, with: Entities::Something
159+
end
160+
143161
desc 'This gets Thing.' do
144162
params Entities::Something.documentation
145163
http_codes [ { code: 200, message: 'getting a single thing' }, { code: 401, message: 'Unauthorized' } ]

0 commit comments

Comments
 (0)