Update CHANGELOG.md
This commit is contained in:
parent
3a97ec1524
commit
8b9825d0e9
|
|
@ -13,7 +13,7 @@ Metrics/AbcSize:
|
|||
# Offense count: 1
|
||||
# Configuration parameters: CountComments.
|
||||
Metrics/ClassLength:
|
||||
Max: 300
|
||||
Max: 230
|
||||
|
||||
# Offense count: 6
|
||||
Metrics/CyclomaticComplexity:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
|
||||
n.n.n / 2016-03-16
|
||||
==================
|
||||
[#356](https://github.com/ruby-grape/grape-swagger/pull/356)
|
||||
|
||||
- adds `consumes` setting
|
||||
- refactoring
|
||||
|
||||
[#354](https://github.com/ruby-grape/grape-swagger/pull/354) some improvements
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ require 'grape-swagger/doc_methods/operation_id'
|
|||
require 'grape-swagger/doc_methods/optional_object'
|
||||
require 'grape-swagger/doc_methods/path_string'
|
||||
require 'grape-swagger/doc_methods/tag_name_description'
|
||||
require 'grape-swagger/doc_methods/parse_params'
|
||||
|
||||
require 'grape-swagger/doc_methods'
|
||||
|
||||
require 'grape-swagger/markdown/kramdown_adapter'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
module GrapeSwagger
|
||||
module DocMethods
|
||||
class ParseParams
|
||||
class << self
|
||||
def call(param, value, route)
|
||||
@array_items = {}
|
||||
path = route.route_path
|
||||
method = route.route_method
|
||||
|
||||
additional_documentation = value.is_a?(Hash) ? value[:documentation] : nil
|
||||
data_type = GrapeSwagger::DocMethods::DataType.call(value)
|
||||
|
||||
if additional_documentation && value.is_a?(Hash)
|
||||
value = additional_documentation.merge(value)
|
||||
end
|
||||
|
||||
description = value.is_a?(Hash) ? value[:desc] || value[:description] : nil
|
||||
required = value.is_a?(Hash) ? value[:required] : false
|
||||
default_value = value.is_a?(Hash) ? value[:default] : nil
|
||||
example = value.is_a?(Hash) ? value[:example] : nil
|
||||
is_array = value.is_a?(Hash) ? (value[:is_array] || false) : false
|
||||
values = value.is_a?(Hash) ? value[:values] : nil
|
||||
name = (value.is_a?(Hash) && value[:full_name]) || param
|
||||
enum_or_range_values = parse_enum_or_range_values(values)
|
||||
|
||||
value_type = { value: value, data_type: data_type, path: path }
|
||||
|
||||
parsed_params = {
|
||||
in: param_type(value_type, param, method, is_array),
|
||||
name: name,
|
||||
description: description,
|
||||
type: data_type,
|
||||
required: required,
|
||||
allowMultiple: is_array
|
||||
}
|
||||
|
||||
if GrapeSwagger::DocMethods::DataType::PRIMITIVE_MAPPINGS.key?(data_type)
|
||||
parsed_params[:type], parsed_params[:format] = GrapeSwagger::DocMethods::DataType::PRIMITIVE_MAPPINGS[data_type]
|
||||
end
|
||||
|
||||
parsed_params[:items] = @array_items if @array_items.present?
|
||||
|
||||
parsed_params[:defaultValue] = example if example
|
||||
parsed_params[:defaultValue] = default_value if default_value && example.blank?
|
||||
|
||||
parsed_params.merge!(enum_or_range_values) if enum_or_range_values
|
||||
parsed_params
|
||||
end
|
||||
|
||||
def primitive?(type)
|
||||
%w(object integer long float double string byte boolean date datetime).include? type.to_s.downcase
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def param_type(value_type, param, method, is_array)
|
||||
# TODO: use `value_type.dig():value, :documentation, :param_type)` instead req ruby2.3
|
||||
#
|
||||
if value_type[:value].is_a?(Hash) &&
|
||||
value_type[:value].key?(:documentation) &&
|
||||
value_type[:value][:documentation].key?(:param_type)
|
||||
|
||||
if is_array
|
||||
@array_items = { 'type' => value_type[:data_type] }
|
||||
|
||||
'array'
|
||||
end
|
||||
else
|
||||
case
|
||||
when value_type[:path].include?("{#{param}}")
|
||||
'path'
|
||||
when %w(POST PUT PATCH).include?(method)
|
||||
primitive?(value_type[:data_type]) ? 'formData' : 'body'
|
||||
else
|
||||
'query'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_enum_or_range_values(values)
|
||||
case values
|
||||
when Range
|
||||
parse_range_values(values) if values.first.is_a?(Integer)
|
||||
when Proc
|
||||
values_result = values.call
|
||||
if values_result.is_a?(Range) && values_result.first.is_a?(Integer)
|
||||
parse_range_values(values_result)
|
||||
else
|
||||
{ enum: values_result }
|
||||
end
|
||||
else
|
||||
{ enum: values } if values
|
||||
end
|
||||
end
|
||||
|
||||
def parse_range_values(values)
|
||||
{ minimum: values.first, maximum: values.last }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -65,9 +65,9 @@ module Grape
|
|||
# contact
|
||||
def contact_object(infos)
|
||||
{
|
||||
contact_name: infos.delete(:contact_name),
|
||||
contact_email: infos.delete(:contact_email),
|
||||
contact_url: infos.delete(:contact_url)
|
||||
name: infos.delete(:contact_name),
|
||||
email: infos.delete(:contact_email),
|
||||
url: infos.delete(:contact_url)
|
||||
}.delete_if { |_, value| value.blank? }
|
||||
end
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ module Grape
|
|||
def params_object(route)
|
||||
partition_params(route).map do |param, value|
|
||||
value = { required: false }.merge(value) if value.is_a?(Hash)
|
||||
parse_params(param, value, route.route_path, route.route_method)
|
||||
GrapeSwagger::DocMethods::ParseParams.call(param, value, route)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -236,8 +236,12 @@ module Grape
|
|||
|
||||
params.each_with_object({}) do |x, memo|
|
||||
x[0] = x.last[:as] if x.last[:as]
|
||||
if x.last[:using].present? || could_it_be_a_model?(x.last)
|
||||
name = expose_params_from_model(x.last[:using] || x.last[:type])
|
||||
|
||||
model = x.last[:using] if x.last[:using].present?
|
||||
model ||= x.last[:documentation][:type] if x.last[:documentation] && could_it_be_a_model?(x.last[:documentation])
|
||||
|
||||
if model
|
||||
name = expose_params_from_model(model)
|
||||
memo[x.first] = if x.last[:documentation] && x.last[:documentation][:is_array]
|
||||
{ 'type' => 'array', 'items' => { '$ref' => "#/definitions/#{name}" } }
|
||||
else
|
||||
|
|
@ -251,7 +255,7 @@ module Grape
|
|||
end
|
||||
|
||||
def expose_params_from_model(model)
|
||||
model_name = model.name.demodulize.camelize
|
||||
model_name = model.respond_to?(:name) ? model.name.demodulize.camelize : model.split('::').last
|
||||
|
||||
# DONE: has to be adept, to be ready for grape-entity >0.5.0
|
||||
# TODO: this should only be a temporary hack ;)
|
||||
|
|
@ -270,10 +274,14 @@ module Grape
|
|||
end
|
||||
|
||||
def could_it_be_a_model?(value)
|
||||
value[:type] &&
|
||||
(
|
||||
value[:type].to_s.include?('Entity') || value[:type].to_s.include?('Entities')
|
||||
) || (
|
||||
value[:type] &&
|
||||
value[:type].is_a?(Class) &&
|
||||
!primitive?(value[:type].name.downcase) &&
|
||||
!GrapeSwagger::DocMethods::ParseParams.primitive?(value[:type].name.downcase) &&
|
||||
!value[:type] == Array
|
||||
)
|
||||
end
|
||||
|
||||
def hidden?(route)
|
||||
|
|
@ -284,97 +292,6 @@ module Grape
|
|||
false
|
||||
end
|
||||
|
||||
# original methods
|
||||
#
|
||||
def parse_params(param, value, path, method)
|
||||
@array_items = {}
|
||||
|
||||
additional_documentation = value.is_a?(Hash) ? value[:documentation] : nil
|
||||
data_type = GrapeSwagger::DocMethods::DataType.call(value)
|
||||
|
||||
if additional_documentation && value.is_a?(Hash)
|
||||
value = additional_documentation.merge(value)
|
||||
end
|
||||
|
||||
description = value.is_a?(Hash) ? value[:desc] || value[:description] : nil
|
||||
required = value.is_a?(Hash) ? value[:required] : false
|
||||
default_value = value.is_a?(Hash) ? value[:default] : nil
|
||||
example = value.is_a?(Hash) ? value[:example] : nil
|
||||
is_array = value.is_a?(Hash) ? (value[:is_array] || false) : false
|
||||
values = value.is_a?(Hash) ? value[:values] : nil
|
||||
name = (value.is_a?(Hash) && value[:full_name]) || param
|
||||
enum_or_range_values = parse_enum_or_range_values(values)
|
||||
|
||||
value_type = { value: value, data_type: data_type, path: path }
|
||||
|
||||
parsed_params = {
|
||||
in: param_type(value_type, param, method, is_array),
|
||||
name: name,
|
||||
description: description,
|
||||
type: data_type,
|
||||
required: required,
|
||||
allowMultiple: is_array
|
||||
}
|
||||
|
||||
if GrapeSwagger::DocMethods::DataType::PRIMITIVE_MAPPINGS.key?(data_type)
|
||||
parsed_params[:type], parsed_params[:format] = GrapeSwagger::DocMethods::DataType::PRIMITIVE_MAPPINGS[data_type]
|
||||
end
|
||||
|
||||
parsed_params[:items] = @array_items if @array_items.present?
|
||||
|
||||
parsed_params[:defaultValue] = example if example
|
||||
parsed_params[:defaultValue] = default_value if default_value && example.blank?
|
||||
|
||||
parsed_params.merge!(enum_or_range_values) if enum_or_range_values
|
||||
parsed_params
|
||||
end
|
||||
|
||||
def param_type(value_type, param, method, is_array)
|
||||
if value_type[:value].is_a?(Hash) &&
|
||||
value_type[:value].key?(:documentation) &&
|
||||
value_type[:value][:documentation].key?(:param_type)
|
||||
|
||||
if is_array
|
||||
@array_items = { 'type' => value_type[:data_type] }
|
||||
|
||||
'array'
|
||||
end
|
||||
else
|
||||
case
|
||||
when value_type[:path].include?("{#{param}}")
|
||||
'path'
|
||||
when %w(POST PUT PATCH).include?(method)
|
||||
primitive?(value_type[:data_type]) ? 'formData' : 'body'
|
||||
else
|
||||
'query'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_enum_or_range_values(values)
|
||||
case values
|
||||
when Range
|
||||
parse_range_values(values) if values.first.is_a?(Integer)
|
||||
when Proc
|
||||
values_result = values.call
|
||||
if values_result.is_a?(Range) && values_result.first.is_a?(Integer)
|
||||
parse_range_values(values_result)
|
||||
else
|
||||
{ enum: values_result }
|
||||
end
|
||||
else
|
||||
{ enum: values } if values
|
||||
end
|
||||
end
|
||||
|
||||
def parse_range_values(values)
|
||||
{ minimum: values.first, maximum: values.last }
|
||||
end
|
||||
|
||||
def primitive?(type)
|
||||
%w(object integer long float double string byte boolean date dateTime).include? type
|
||||
end
|
||||
|
||||
def tag_object(route, version)
|
||||
Array(route.route_path.split('{')[0].split('/').reject(&:empty?).delete_if { |i| ((i == route.route_prefix.to_s) || (i == version)) }.first)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ describe GrapeSwagger::DocMethods::PathString do
|
|||
expect(subject.build('/{version}/thing/:id', options)).to eql ['Thing', '/v1/thing/{id}']
|
||||
expect(subject.build('/{version}/thing/foo/:id', options)).to eql ['Foo', '/v1/thing/foo/{id}']
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ RSpec.shared_context "swagger example" do
|
|||
"title"=>"The API title to be displayed on the API homepage.",
|
||||
"description"=>"A description of the API.",
|
||||
"termsOfServiceUrl"=>"www.The-URL-of-the-terms-and-service.com",
|
||||
"contact"=>{"contact_name"=>"Contact name", "contact_email"=>"Contact@email.com", "contact_url"=>"Contact URL"},
|
||||
"contact"=>{"name"=>"Contact name", "email"=>"Contact@email.com", "url"=>"Contact URL"},
|
||||
"license"=>{"name"=>"The name of the license.", "url"=>"www.The-URL-of-the-license.org"},
|
||||
"version"=>"v1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ RSpec.shared_context "the api entities" do
|
|||
expose :description, documentation: { type: String }
|
||||
expose :items, as: '$responses', using: Entities::ResponseItem, documentation: { is_array: true }
|
||||
end
|
||||
|
||||
class UseTemResponseAsType < Grape::Entity
|
||||
expose :description, documentation: { type: String }
|
||||
expose :responses, documentation: { type: Entities::ResponseItem, is_array: false }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ describe 'exposing' do
|
|||
{ "declared_params" => declared(params) }
|
||||
end
|
||||
|
||||
desc 'This returns something',
|
||||
entity: Entities::UseTemResponseAsType,
|
||||
failure: [{code: 400, message: 'NotFound', model: Entities::ApiError}]
|
||||
get '/nested_type' do
|
||||
{ "declared_params" => declared(params) }
|
||||
end
|
||||
|
||||
|
||||
add_swagger_documentation
|
||||
end
|
||||
end
|
||||
|
|
@ -31,6 +39,42 @@ describe 'exposing' do
|
|||
TheApi::ResponseApi
|
||||
end
|
||||
|
||||
describe "uses nested type as response object" do
|
||||
subject do
|
||||
get '/swagger_doc/nested_type'
|
||||
JSON.parse(last_response.body)
|
||||
end
|
||||
specify do
|
||||
expect(subject).to eql({
|
||||
"info"=>{"title"=>"API title", "version"=>"v1"},
|
||||
"swagger"=>"2.0",
|
||||
"produces"=>["application/json"],
|
||||
"host"=>"example.org",
|
||||
"tags"=>[
|
||||
{"name"=>"params_response", "description"=>"Operations about params_responses"},
|
||||
{"name"=>"entity_response", "description"=>"Operations about entity_responses"},
|
||||
{"name"=>"nested_type", "description"=>"Operations about nested_types"}
|
||||
],
|
||||
"schemes"=>["https", "http"],
|
||||
"paths"=>{
|
||||
"/nested_type"=>{
|
||||
"get"=>{
|
||||
"produces"=>["application/json"],
|
||||
"responses"=>{
|
||||
"200"=>{"description"=>"This returns something", "schema"=>{"$ref"=>"#/definitions/UseTemResponseAsType"}},
|
||||
"400"=>{"description"=>"NotFound", "schema"=>{"$ref"=>"#/definitions/ApiError"}}
|
||||
},
|
||||
"tags"=>["nested_type"],
|
||||
"operationId"=>"getNestedType"
|
||||
}}},
|
||||
"definitions"=>{
|
||||
"ResponseItem"=>{"type"=>"object", "properties"=>{"id"=>{"type"=>"integer"}, "name"=>{"type"=>"string"}}},
|
||||
"UseTemResponseAsType"=>{"type"=>"object", "properties"=>{"description"=>{"type"=>"string"}, "responses"=>{"$ref"=>"#/definitions/ResponseItem"}}},
|
||||
"ApiError"=>{"type"=>"object", "properties"=>{"code"=>{"type"=>"integer"}, "message"=>{"type"=>"string"}}}
|
||||
}})
|
||||
end
|
||||
end
|
||||
|
||||
describe "uses entity as response object" do
|
||||
subject do
|
||||
get '/swagger_doc/entity_response'
|
||||
|
|
@ -43,7 +87,11 @@ describe 'exposing' do
|
|||
"swagger"=>"2.0",
|
||||
"produces"=>["application/json"],
|
||||
"host"=>"example.org",
|
||||
"tags" => [{"name"=>"params_response", "description"=>"Operations about params_responses"}, {"name"=>"entity_response", "description"=>"Operations about entity_responses"}],
|
||||
"tags" => [
|
||||
{"name"=>"params_response", "description"=>"Operations about params_responses"},
|
||||
{"name"=>"entity_response", "description"=>"Operations about entity_responses"},
|
||||
{"name"=>"nested_type", "description"=>"Operations about nested_types"}
|
||||
],
|
||||
"schemes"=>["https", "http"],
|
||||
"paths"=>{
|
||||
"/entity_response"=>{
|
||||
|
|
@ -74,21 +122,17 @@ describe 'exposing' do
|
|||
JSON.parse(last_response.body)
|
||||
end
|
||||
|
||||
|
||||
# usage of grape-entity 0.4.8 preduces a wrong definition for ParamsResponse, this one:
|
||||
# "definitions" => {
|
||||
# "ParamsResponse"=>{"properties"=>{"description"=>{"type"=>"string"}}},
|
||||
# "ApiError"=>{"type"=>"object", "properties"=>{"code"=>{"type"=>"integer"}, "message"=>{"type"=>"string"}}}
|
||||
# }
|
||||
# (`$response` property is missing)
|
||||
|
||||
specify do
|
||||
expect(subject).to eql({
|
||||
"info"=>{"title"=>"API title", "version"=>"v1"},
|
||||
"swagger"=>"2.0",
|
||||
"produces"=>["application/json"],
|
||||
"host"=>"example.org",
|
||||
"tags" => [{"name"=>"params_response", "description"=>"Operations about params_responses"}, {"name"=>"entity_response", "description"=>"Operations about entity_responses"}],
|
||||
"tags" => [
|
||||
{"name"=>"params_response", "description"=>"Operations about params_responses"},
|
||||
{"name"=>"entity_response", "description"=>"Operations about entity_responses"},
|
||||
{"name"=>"nested_type", "description"=>"Operations about nested_types"}
|
||||
],
|
||||
"schemes"=>["https", "http"],
|
||||
"paths"=>{
|
||||
"/params_response"=>{
|
||||
|
|
|
|||
|
|
@ -155,12 +155,12 @@ describe 'swagger spec v2.0' do
|
|||
describe 'contact object' do
|
||||
let(:contact) { json['info']['contact'] }
|
||||
|
||||
it { expect(contact.keys).to include 'contact_name' }
|
||||
it { expect(contact['contact_name']).to be_a String }
|
||||
it { expect(contact.keys).to include 'contact_email' }
|
||||
it { expect(contact['contact_email']).to be_a String }
|
||||
it { expect(contact.keys).to include 'contact_url' }
|
||||
it { expect(contact['contact_url']).to be_a String }
|
||||
it { expect(contact.keys).to include 'name' }
|
||||
it { expect(contact['name']).to be_a String }
|
||||
it { expect(contact.keys).to include 'email' }
|
||||
it { expect(contact['email']).to be_a String }
|
||||
it { expect(contact.keys).to include 'url' }
|
||||
it { expect(contact['url']).to be_a String }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ describe 'Default API' do
|
|||
end
|
||||
|
||||
it 'documents the contact email' do
|
||||
expect(subject['contact']['contact_email']).to eql('support@test.com')
|
||||
expect(subject['contact']['email']).to eql('support@test.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue