Skip to content

Commit 7c74647

Browse files
Add support for primitive data types in responses (#945)
* Add support for primitive data types in responses * Use `format` option * Handle `Hash` type * Update CHANGELOG.md
1 parent ecafd1f commit 7c74647

File tree

3 files changed

+110
-7
lines changed

3 files changed

+110
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#### Features
44

5-
* Your contribution here.
5+
* [#945](https://github.com/ruby-grape/grape-swagger/pull/945): Add support for primitive data types in responses - [@gregg-platogo](https://github.com/gregg-platogo).
66

77
#### Fixes
88

lib/grape-swagger/endpoint.rb

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,8 @@ def response_object(route, options)
207207

208208
next build_file_response(memo[value[:code]]) if file_response?(value[:model])
209209

210-
if memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
211-
memo[204] = memo.delete(200)
212-
value[:code] = 204
213-
next
214-
end
210+
next build_delete_response(memo, value) if delete_response?(memo, route, value)
211+
next build_response_for_type_parameter(memo, route, value, options) if value[:type]
215212

216213
# Explicitly request no model with { model: '' }
217214
next if value[:model] == ''
@@ -284,6 +281,15 @@ def default_code_from_route(route)
284281
[default_code]
285282
end
286283

284+
def build_delete_response(memo, value)
285+
memo[204] = memo.delete(200)
286+
value[:code] = 204
287+
end
288+
289+
def delete_response?(memo, route, value)
290+
memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
291+
end
292+
287293
def build_memo_schema(memo, route, value, response_model, options)
288294
if memo[value[:code]][:schema] && value[:as]
289295
memo[value[:code]][:schema][:properties].merge!(build_reference(route, value, response_model, options))
@@ -304,6 +310,29 @@ def build_memo_schema(memo, route, value, response_model, options)
304310
end
305311
end
306312

313+
def build_response_for_type_parameter(memo, _route, value, _options)
314+
type, format = prepare_type_and_format(value)
315+
316+
if memo[value[:code]].include?(:schema) && value.include?(:as)
317+
memo[value[:code]][:schema][:properties].merge!(value[:as] => { type: type, format: format }.compact)
318+
elsif value.include?(:as)
319+
memo[value[:code]][:schema] =
320+
{ type: :object, properties: { value[:as] => { type: type, format: format }.compact } }
321+
else
322+
memo[value[:code]][:schema] = { type: type }
323+
end
324+
end
325+
326+
def prepare_type_and_format(value)
327+
data_type = GrapeSwagger::DocMethods::DataType.call(value[:type])
328+
329+
if GrapeSwagger::DocMethods::DataType.primitive?(data_type)
330+
GrapeSwagger::DocMethods::DataType.mapping(data_type)
331+
else
332+
data_type
333+
end
334+
end
335+
307336
def build_reference(route, value, response_model, settings)
308337
# TODO: proof that the definition exist, if model isn't specified
309338
reference = if value.key?(:as)
@@ -387,7 +416,7 @@ def get_path_params(stackable_values)
387416
return param unless stackable_values
388417
return params unless stackable_values.is_a? Grape::Util::StackableValues
389418

390-
stackable_values&.new_values&.dig(:namespace)&.each do |namespace|
419+
stackable_values&.new_values&.dig(:namespace)&.each do |namespace| # rubocop:disable Style/SafeNavigationChainLength
391420
space = namespace.space.to_s.gsub(':', '')
392421
params[space] = namespace.options || {}
393422
end
@@ -464,6 +493,7 @@ def success_code_from_entity(route, entity)
464493
default_code[:as] = entity[:as] if entity[:as]
465494
default_code[:is_array] = entity[:is_array] if entity[:is_array]
466495
default_code[:required] = entity[:required] if entity[:required]
496+
default_code[:type] = entity[:type] if entity[:type]
467497
else
468498
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
469499
default_code[:model] = entity if entity
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 ResponseApiModelsAndPrimitiveTypes < Grape::API
11+
format :json
12+
13+
desc 'This returns something',
14+
success: [
15+
{ type: 'Integer', as: :integer_response },
16+
{ model: Entities::UseResponse, as: :user_response },
17+
{ type: 'String', as: :string_response },
18+
{ type: 'Float', as: :float_response },
19+
{ type: 'Hash', as: :hash_response }
20+
],
21+
failure: [
22+
{ code: 400, message: 'NotFound', model: '' },
23+
{ code: 404, message: 'BadRequest', model: Entities::ApiError }
24+
],
25+
default_response: { message: 'Error', model: Entities::ApiError }
26+
get '/use-response' do
27+
{ 'declared_params' => declared(params) }
28+
end
29+
30+
add_swagger_documentation
31+
end
32+
end
33+
end
34+
35+
def app
36+
TheApi::ResponseApiModelsAndPrimitiveTypes
37+
end
38+
39+
describe 'uses entity as response object implicitly with route name' do
40+
subject do
41+
get '/swagger_doc/use-response'
42+
JSON.parse(last_response.body)
43+
end
44+
45+
specify do
46+
expect(subject['paths']['/use-response']['get']).to eql(
47+
'description' => 'This returns something',
48+
'produces' => ['application/json'],
49+
'responses' => {
50+
'200' => {
51+
'description' => 'This returns something',
52+
'schema' => {
53+
'type' => 'object',
54+
'properties' => {
55+
'user_response' => { '$ref' => '#/definitions/UseResponse' },
56+
'integer_response' => { 'type' => 'integer', 'format' => 'int32' },
57+
'string_response' => { 'type' => 'string' },
58+
'float_response' => { 'type' => 'number', 'format' => 'float' },
59+
'hash_response' => { 'type' => 'object' }
60+
}
61+
}
62+
},
63+
'400' => { 'description' => 'NotFound' },
64+
'404' => { 'description' => 'BadRequest', 'schema' => { '$ref' => '#/definitions/ApiError' } },
65+
'default' => { 'description' => 'Error', 'schema' => { '$ref' => '#/definitions/ApiError' } }
66+
},
67+
'tags' => ['use-response'],
68+
'operationId' => 'getUseResponse'
69+
)
70+
expect(subject['definitions']).to eql(swagger_entity_as_response_object)
71+
end
72+
end
73+
end

0 commit comments

Comments
 (0)