Skip to content

Commit

Permalink
WIP: Add support for primitive data types in responses
Browse files Browse the repository at this point in the history
  • Loading branch information
gregg-platogo committed Nov 22, 2024
1 parent ecafd1f commit 525bbde
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 6 deletions.
31 changes: 25 additions & 6 deletions lib/grape-swagger/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,8 @@ def response_object(route, options)

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

if memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
memo[204] = memo.delete(200)
value[:code] = 204
next
end
next build_delete_response(memo, value) if delete_response?(memo, route, value)
next build_primitive_response(memo, route, value, options) if value[:type]

# Explicitly request no model with { model: '' }
next if value[:model] == ''
Expand Down Expand Up @@ -284,6 +281,15 @@ def default_code_from_route(route)
[default_code]
end

def build_delete_response(memo, value)
memo[204] = memo.delete(200)
value[:code] = 204
end

def delete_response?(memo, route, value)
memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
end

def build_memo_schema(memo, route, value, response_model, options)
if memo[value[:code]][:schema] && value[:as]
memo[value[:code]][:schema][:properties].merge!(build_reference(route, value, response_model, options))
Expand All @@ -304,6 +310,18 @@ def build_memo_schema(memo, route, value, response_model, options)
end
end

def build_primitive_response(memo, _route, value, _options)
type = GrapeSwagger::DocMethods::DataType.call(value[:type])

if memo[value[:code]].include?(:schema) && value.include?(:as)
memo[value[:code]][:schema][:properties].merge!(value[:as] => { type: type })
elsif value.include?(:as)
memo[value[:code]][:schema] = { type: :object, properties: { value[:as] => { type: type } } }
else
memo[value[:code]][:schema] = { type: type }
end
end

def build_reference(route, value, response_model, settings)
# TODO: proof that the definition exist, if model isn't specified
reference = if value.key?(:as)
Expand Down Expand Up @@ -387,7 +405,7 @@ def get_path_params(stackable_values)
return param unless stackable_values
return params unless stackable_values.is_a? Grape::Util::StackableValues

stackable_values&.new_values&.dig(:namespace)&.each do |namespace|
stackable_values&.new_values&.dig(:namespace)&.each do |namespace| # rubocop:disable Style/SafeNavigationChainLength
space = namespace.space.to_s.gsub(':', '')
params[space] = namespace.options || {}
end
Expand Down Expand Up @@ -464,6 +482,7 @@ def success_code_from_entity(route, entity)
default_code[:as] = entity[:as] if entity[:as]
default_code[:is_array] = entity[:is_array] if entity[:is_array]
default_code[:required] = entity[:required] if entity[:required]
default_code[:type] = entity[:type] if entity[:type]
else
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
default_code[:model] = entity if entity
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

require 'spec_helper'

describe 'response' do
include_context "#{MODEL_PARSER} swagger example"

before :all do
module TheApi
class ResponseApiModelsAndPrimitiveTypes < Grape::API
format :json

desc 'This returns something',
success: [
{ type: 'Integer', as: :integer_response },
{ model: Entities::UseResponse, as: :user_response },
{ type: 'String', as: :string_response }
],
failure: [
{ code: 400, message: 'NotFound', model: '' },
{ code: 404, message: 'BadRequest', model: Entities::ApiError }
],
default_response: { message: 'Error', model: Entities::ApiError }
get '/use-response' do
{ 'declared_params' => declared(params) }
end

add_swagger_documentation
end
end
end

def app
TheApi::ResponseApiModelsAndPrimitiveTypes
end

describe 'uses entity as response object implicitly with route name' do
subject do
get '/swagger_doc/use-response'
JSON.parse(last_response.body)
end

specify do
expect(subject['paths']['/use-response']['get']).to eql(
'description' => 'This returns something',
'produces' => ['application/json'],
'responses' => {
'200' => {
'description' => 'This returns something',
'schema' => {
'type' => 'object',
'properties' => {
'user_response' => { '$ref' => '#/definitions/UseResponse' },
'integer_response' => { 'type' => 'integer' },
'string_response' => { 'type' => 'string' }
}
}
},
'400' => { 'description' => 'NotFound' },
'404' => { 'description' => 'BadRequest', 'schema' => { '$ref' => '#/definitions/ApiError' } },
'default' => { 'description' => 'Error', 'schema' => { '$ref' => '#/definitions/ApiError' } }
},
'tags' => ['use-response'],
'operationId' => 'getUseResponse'
)
expect(subject['definitions']).to eql(swagger_entity_as_response_object)
end
end
end

0 comments on commit 525bbde

Please sign in to comment.