grape-swagger 0.34.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +55 -0
- data/.travis.yml +14 -10
- data/CHANGELOG.md +37 -4
- data/Gemfile +9 -4
- data/README.md +123 -10
- data/UPGRADING.md +30 -0
- data/grape-swagger.gemspec +1 -1
- data/lib/grape-swagger.rb +1 -0
- data/lib/grape-swagger/doc_methods/build_model_definition.rb +53 -2
- data/lib/grape-swagger/doc_methods/data_type.rb +6 -6
- data/lib/grape-swagger/doc_methods/operation_id.rb +2 -2
- data/lib/grape-swagger/doc_methods/parse_params.rb +19 -4
- data/lib/grape-swagger/endpoint.rb +11 -11
- data/lib/grape-swagger/endpoint/params_parser.rb +12 -5
- data/lib/grape-swagger/rake/oapi_tasks.rb +10 -2
- data/lib/grape-swagger/version.rb +1 -1
- data/spec/issues/427_entity_as_string_spec.rb +1 -1
- data/spec/issues/430_entity_definitions_spec.rb +7 -5
- data/spec/issues/784_extensions_on_params_spec.rb +38 -0
- data/spec/lib/data_type_spec.rb +14 -2
- data/spec/lib/endpoint/params_parser_spec.rb +2 -1
- data/spec/lib/endpoint_spec.rb +1 -1
- data/spec/lib/oapi_tasks_spec.rb +15 -5
- data/spec/support/model_parsers/entity_parser.rb +1 -1
- data/spec/support/model_parsers/representable_parser.rb +1 -1
- data/spec/swagger_v2/api_swagger_v2_hide_param_spec.rb +14 -3
- data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +2 -2
- data/spec/swagger_v2/boolean_params_spec.rb +1 -1
- data/spec/swagger_v2/inheritance_and_discriminator_spec.rb +56 -0
- data/spec/swagger_v2/reference_entity_spec.rb +74 -29
- metadata +14 -16
data/grape-swagger.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.license = 'MIT'
|
15
15
|
|
16
16
|
s.required_ruby_version = '>= 2.4'
|
17
|
-
s.add_runtime_dependency 'grape', '
|
17
|
+
s.add_runtime_dependency 'grape', '~> 1.3'
|
18
18
|
|
19
19
|
s.files = `git ls-files`.split("\n")
|
20
20
|
s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
|
data/lib/grape-swagger.rb
CHANGED
@@ -109,6 +109,7 @@ end
|
|
109
109
|
module SwaggerDocumentationAdder
|
110
110
|
attr_accessor :combined_namespaces, :combined_namespace_identifiers
|
111
111
|
attr_accessor :combined_routes, :combined_namespace_routes
|
112
|
+
|
112
113
|
include SwaggerRouting
|
113
114
|
|
114
115
|
def add_swagger_documentation(options = {})
|
@@ -4,8 +4,8 @@ module GrapeSwagger
|
|
4
4
|
module DocMethods
|
5
5
|
class BuildModelDefinition
|
6
6
|
class << self
|
7
|
-
def build(model, properties, required)
|
8
|
-
definition = { type: 'object', properties: properties }
|
7
|
+
def build(model, properties, required, other_def_properties = {})
|
8
|
+
definition = { type: 'object', properties: properties }.merge(other_def_properties)
|
9
9
|
|
10
10
|
if required.nil?
|
11
11
|
required_attrs = required_attributes(model)
|
@@ -17,6 +17,57 @@ module GrapeSwagger
|
|
17
17
|
definition
|
18
18
|
end
|
19
19
|
|
20
|
+
def parse_params_from_model(parsed_response, model, model_name)
|
21
|
+
if parsed_response.is_a?(Hash) && parsed_response.keys.first == :allOf
|
22
|
+
refs_or_models = parsed_response[:allOf]
|
23
|
+
parsed = parse_refs_and_models(refs_or_models, model)
|
24
|
+
|
25
|
+
{
|
26
|
+
allOf: parsed
|
27
|
+
}
|
28
|
+
else
|
29
|
+
properties, required = parsed_response
|
30
|
+
unless properties&.any?
|
31
|
+
raise GrapeSwagger::Errors::SwaggerSpec,
|
32
|
+
"Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
|
33
|
+
end
|
34
|
+
properties, other_def_properties = parse_properties(properties)
|
35
|
+
|
36
|
+
build(
|
37
|
+
model, properties, required, other_def_properties
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_properties(properties)
|
43
|
+
other_properties = {}
|
44
|
+
|
45
|
+
discriminator_key, discriminator_value =
|
46
|
+
properties.find do |_key, value|
|
47
|
+
value[:documentation].try(:[], :is_discriminator)
|
48
|
+
end
|
49
|
+
|
50
|
+
if discriminator_key
|
51
|
+
discriminator_value.delete(:documentation)
|
52
|
+
properties[discriminator_key] = discriminator_value
|
53
|
+
|
54
|
+
other_properties[:discriminator] = discriminator_key
|
55
|
+
end
|
56
|
+
|
57
|
+
[properties, other_properties]
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_refs_and_models(refs_or_models, model)
|
61
|
+
refs_or_models.map do |ref_or_models|
|
62
|
+
if ref_or_models.is_a?(Hash) && ref_or_models.keys.first == '$ref'
|
63
|
+
ref_or_models
|
64
|
+
else
|
65
|
+
properties, required = ref_or_models
|
66
|
+
GrapeSwagger::DocMethods::BuildModelDefinition.build(model, properties, required)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
20
71
|
private
|
21
72
|
|
22
73
|
def required_attributes(model)
|
@@ -16,7 +16,7 @@ module GrapeSwagger
|
|
16
16
|
'object'
|
17
17
|
when 'Rack::Multipart::UploadedFile', 'File'
|
18
18
|
'file'
|
19
|
-
when '
|
19
|
+
when 'Grape::API::Boolean'
|
20
20
|
'boolean'
|
21
21
|
when 'BigDecimal'
|
22
22
|
'double'
|
@@ -48,14 +48,14 @@ module GrapeSwagger
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def parse_entity_name(model)
|
51
|
-
if model.
|
51
|
+
if model.respond_to?(:entity_name)
|
52
52
|
model.entity_name
|
53
53
|
elsif model.to_s.end_with?('::Entity', '::Entities')
|
54
|
-
model.to_s.split('::')[
|
55
|
-
elsif model.
|
56
|
-
model.
|
54
|
+
model.to_s.split('::')[0..-2].join('_')
|
55
|
+
elsif model.to_s.start_with?('Entity::', 'Entities::', 'Representable::')
|
56
|
+
model.to_s.split('::')[1..-1].join('_')
|
57
57
|
else
|
58
|
-
model.to_s.split('::').
|
58
|
+
model.to_s.split('::').join('_')
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -16,8 +16,8 @@ module GrapeSwagger
|
|
16
16
|
|
17
17
|
def manipulate(path)
|
18
18
|
operation = path.split('/').map(&:capitalize).join
|
19
|
-
operation.gsub!(
|
20
|
-
operation.gsub!(
|
19
|
+
operation.gsub!(/-(\w)/, &:upcase).delete!('-') if operation[/-(\w)/]
|
20
|
+
operation.gsub!(/_(\w)/, &:upcase).delete!('_') if operation.include?('_')
|
21
21
|
operation.gsub!(/\.(\w)/, &:upcase).delete!('.') if operation[/\.(\w)/]
|
22
22
|
if path.include?('{')
|
23
23
|
operation.gsub!(/\{(\w)/, &:upcase)
|
@@ -26,6 +26,7 @@ module GrapeSwagger
|
|
26
26
|
document_range_values(settings) unless value_type[:is_array]
|
27
27
|
document_required(settings)
|
28
28
|
document_additional_properties(settings)
|
29
|
+
document_add_extensions(settings)
|
29
30
|
|
30
31
|
@parsed_param
|
31
32
|
end
|
@@ -62,6 +63,10 @@ module GrapeSwagger
|
|
62
63
|
@parsed_param[:format] = settings[:format] if settings[:format].present?
|
63
64
|
end
|
64
65
|
|
66
|
+
def document_add_extensions(settings)
|
67
|
+
GrapeSwagger::DocMethods::Extensions.add_extensions_to_root(settings, @parsed_param)
|
68
|
+
end
|
69
|
+
|
65
70
|
def document_array_param(value_type, definitions)
|
66
71
|
if value_type[:documentation].present?
|
67
72
|
param_type = value_type[:documentation][:param_type]
|
@@ -72,6 +77,19 @@ module GrapeSwagger
|
|
72
77
|
|
73
78
|
param_type ||= value_type[:param_type]
|
74
79
|
|
80
|
+
array_items = parse_array_item(
|
81
|
+
definitions,
|
82
|
+
type,
|
83
|
+
value_type
|
84
|
+
)
|
85
|
+
|
86
|
+
@parsed_param[:in] = param_type || 'formData'
|
87
|
+
@parsed_param[:items] = array_items
|
88
|
+
@parsed_param[:type] = 'array'
|
89
|
+
@parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_array_item(definitions, type, value_type)
|
75
93
|
array_items = {}
|
76
94
|
if definitions[value_type[:data_type]]
|
77
95
|
array_items['$ref'] = "#/definitions/#{@parsed_param[:type]}"
|
@@ -86,10 +104,7 @@ module GrapeSwagger
|
|
86
104
|
|
87
105
|
array_items[:default] = value_type[:default] if value_type[:default].present?
|
88
106
|
|
89
|
-
|
90
|
-
@parsed_param[:items] = array_items
|
91
|
-
@parsed_param[:type] = 'array'
|
92
|
-
@parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
|
107
|
+
array_items
|
93
108
|
end
|
94
109
|
|
95
110
|
def document_additional_properties(settings)
|
@@ -196,10 +196,7 @@ module Grape
|
|
196
196
|
end
|
197
197
|
|
198
198
|
def response_object(route, options)
|
199
|
-
codes
|
200
|
-
codes.map! { |x| x.is_a?(Array) ? { code: x[0], message: x[1], model: x[2], examples: x[3], headers: x[4] } : x }
|
201
|
-
|
202
|
-
codes.each_with_object({}) do |value, memo|
|
199
|
+
codes(route).each_with_object({}) do |value, memo|
|
203
200
|
value[:message] ||= ''
|
204
201
|
memo[value[:code]] = { description: value[:message] }
|
205
202
|
|
@@ -225,6 +222,12 @@ module Grape
|
|
225
222
|
end
|
226
223
|
end
|
227
224
|
|
225
|
+
def codes(route)
|
226
|
+
http_codes_from_route(route).map do |x|
|
227
|
+
x.is_a?(Array) ? { code: x[0], message: x[1], model: x[2], examples: x[3], headers: x[4] } : x
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
228
231
|
def success_code?(code)
|
229
232
|
status = code.is_a?(Array) ? code.first : code[:code]
|
230
233
|
status.between?(200, 299)
|
@@ -301,7 +304,7 @@ module Grape
|
|
301
304
|
default_type(required)
|
302
305
|
|
303
306
|
request_params = unless declared_params.nil? && route.headers.nil?
|
304
|
-
GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings)
|
307
|
+
GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
|
305
308
|
end || {}
|
306
309
|
|
307
310
|
request_params.empty? ? required : request_params
|
@@ -340,13 +343,10 @@ module Grape
|
|
340
343
|
parser = GrapeSwagger.model_parsers.find(model)
|
341
344
|
raise GrapeSwagger::Errors::UnregisteredParser, "No parser registered for #{model_name}." unless parser
|
342
345
|
|
343
|
-
|
344
|
-
unless properties&.any?
|
345
|
-
raise GrapeSwagger::Errors::SwaggerSpec,
|
346
|
-
"Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
|
347
|
-
end
|
346
|
+
parsed_response = parser.new(model, self).call
|
348
347
|
|
349
|
-
@definitions[model_name] =
|
348
|
+
@definitions[model_name] =
|
349
|
+
GrapeSwagger::DocMethods::BuildModelDefinition.parse_params_from_model(parsed_response, model, model_name)
|
350
350
|
|
351
351
|
model_name
|
352
352
|
end
|
@@ -3,15 +3,16 @@
|
|
3
3
|
module GrapeSwagger
|
4
4
|
module Endpoint
|
5
5
|
class ParamsParser
|
6
|
-
attr_reader :params, :settings
|
6
|
+
attr_reader :params, :settings, :endpoint
|
7
7
|
|
8
|
-
def self.parse_request_params(params, settings)
|
9
|
-
new(params, settings).parse_request_params
|
8
|
+
def self.parse_request_params(params, settings, endpoint)
|
9
|
+
new(params, settings, endpoint).parse_request_params
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(params, settings)
|
12
|
+
def initialize(params, settings, endpoint)
|
13
13
|
@params = params
|
14
14
|
@settings = settings
|
15
|
+
@endpoint = endpoint
|
15
16
|
end
|
16
17
|
|
17
18
|
def parse_request_params
|
@@ -55,7 +56,13 @@ module GrapeSwagger
|
|
55
56
|
return true unless param_options.key?(:documentation) && !param_options[:required]
|
56
57
|
|
57
58
|
param_hidden = param_options[:documentation].fetch(:hidden, false)
|
58
|
-
|
59
|
+
if param_hidden.is_a?(Proc)
|
60
|
+
param_hidden = if settings[:token_owner]
|
61
|
+
param_hidden.call(endpoint.send(settings[:token_owner].to_sym))
|
62
|
+
else
|
63
|
+
param_hidden.call
|
64
|
+
end
|
65
|
+
end
|
59
66
|
!param_hidden
|
60
67
|
end
|
61
68
|
|
@@ -10,17 +10,25 @@ module GrapeSwagger
|
|
10
10
|
include Rack::Test::Methods
|
11
11
|
|
12
12
|
attr_reader :oapi
|
13
|
-
attr_reader :api_class
|
14
13
|
|
15
14
|
def initialize(api_class)
|
16
15
|
super()
|
17
16
|
|
18
|
-
|
17
|
+
if api_class.is_a? String
|
18
|
+
@api_class_name = api_class
|
19
|
+
else
|
20
|
+
@api_class = api_class
|
21
|
+
end
|
22
|
+
|
19
23
|
define_tasks
|
20
24
|
end
|
21
25
|
|
22
26
|
private
|
23
27
|
|
28
|
+
def api_class
|
29
|
+
@api_class ||= @api_class_name.constantize
|
30
|
+
end
|
31
|
+
|
24
32
|
def define_tasks
|
25
33
|
namespace :oapi do
|
26
34
|
fetch
|
@@ -35,5 +35,5 @@ describe '#427 nested entity given as string' do
|
|
35
35
|
JSON.parse(last_response.body)['definitions']
|
36
36
|
end
|
37
37
|
|
38
|
-
specify { expect(subject.keys).to include 'RoleEntity', '
|
38
|
+
specify { expect(subject.keys).to include 'RoleEntity', 'Permission_WithoutRole' }
|
39
39
|
end
|
@@ -55,6 +55,8 @@ describe 'definition names' do
|
|
55
55
|
class Class7
|
56
56
|
class SeventhEntity < Class6::SixthEntity
|
57
57
|
expose :seventh_thing
|
58
|
+
|
59
|
+
private_class_method :entity_name
|
58
60
|
end
|
59
61
|
end
|
60
62
|
end
|
@@ -82,11 +84,11 @@ describe 'definition names' do
|
|
82
84
|
JSON.parse(last_response.body)['definitions']
|
83
85
|
end
|
84
86
|
|
85
|
-
specify { expect(subject).to include '
|
86
|
-
specify { expect(subject).to include '
|
87
|
+
specify { expect(subject).to include 'TestDefinition_DummyEntities_WithVeryLongName_AnotherGroupingModule_Class1' }
|
88
|
+
specify { expect(subject).to include 'TestDefinition_DummyEntities_WithVeryLongName_AnotherGroupingModule_Class2' }
|
87
89
|
specify { expect(subject).to include 'FooKlass' }
|
88
|
-
specify { expect(subject).to include '
|
89
|
-
specify { expect(subject).to include '
|
90
|
+
specify { expect(subject).to include 'TestDefinition_DummyEntities_WithVeryLongName_AnotherGroupingModule_Class4_FourthEntity' }
|
91
|
+
specify { expect(subject).to include 'TestDefinition_DummyEntities_WithVeryLongName_AnotherGroupingModule_Class5_FithEntity' }
|
90
92
|
specify { expect(subject).to include 'BarKlass' }
|
91
|
-
specify { expect(subject).to include '
|
93
|
+
specify { expect(subject).to include 'TestDefinition_DummyEntities_WithVeryLongName_AnotherGroupingModule_Class7_SeventhEntity' }
|
92
94
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe '#532 allow custom format' do
|
6
|
+
let(:app) do
|
7
|
+
Class.new(Grape::API) do
|
8
|
+
namespace :issue_784 do
|
9
|
+
params do
|
10
|
+
requires :logs, type: String, documentation: { format: 'log', x: { name: 'Log' } }
|
11
|
+
optional :phone_number, type: Integer, documentation: { format: 'phone_number', x: { name: 'PhoneNumber' } }
|
12
|
+
end
|
13
|
+
|
14
|
+
post do
|
15
|
+
present params
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
add_swagger_documentation format: :json
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
subject do
|
24
|
+
get '/swagger_doc'
|
25
|
+
JSON.parse(last_response.body)
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:parameters) { subject['paths']['/issue_784']['post']['parameters'] }
|
29
|
+
|
30
|
+
specify do
|
31
|
+
expect(parameters).to eql(
|
32
|
+
[
|
33
|
+
{ 'in' => 'formData', 'name' => 'logs', 'type' => 'string', 'format' => 'log', 'required' => true, 'x-name' => 'Log' },
|
34
|
+
{ 'in' => 'formData', 'name' => 'phone_number', 'type' => 'integer', 'format' => 'phone_number', 'required' => false, 'x-name' => 'PhoneNumber' }
|
35
|
+
]
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
data/spec/lib/data_type_spec.rb
CHANGED
@@ -49,14 +49,26 @@ describe GrapeSwagger::DocMethods::DataType do
|
|
49
49
|
it { is_expected.to eq 'MyInteger' }
|
50
50
|
end
|
51
51
|
|
52
|
+
describe 'Types in array with inherited entity_name' do
|
53
|
+
before do
|
54
|
+
stub_const 'EntityBase', Class.new
|
55
|
+
allow(EntityBase).to receive(:entity_name).and_return 'MyInteger'
|
56
|
+
stub_const 'MyEntity', Class.new(EntityBase)
|
57
|
+
end
|
58
|
+
|
59
|
+
let(:value) { { type: '[MyEntity]' } }
|
60
|
+
|
61
|
+
it { is_expected.to eq 'MyInteger' }
|
62
|
+
end
|
63
|
+
|
52
64
|
describe 'Rack::Multipart::UploadedFile' do
|
53
65
|
let(:value) { { type: Rack::Multipart::UploadedFile } }
|
54
66
|
|
55
67
|
it { is_expected.to eq 'file' }
|
56
68
|
end
|
57
69
|
|
58
|
-
describe '
|
59
|
-
let(:value) { { type:
|
70
|
+
describe 'Grape::API::Boolean' do
|
71
|
+
let(:value) { { type: Grape::API::Boolean } }
|
60
72
|
|
61
73
|
it { is_expected.to eq 'boolean' }
|
62
74
|
end
|
@@ -5,8 +5,9 @@ require 'spec_helper'
|
|
5
5
|
describe GrapeSwagger::Endpoint::ParamsParser do
|
6
6
|
let(:settings) { {} }
|
7
7
|
let(:params) { [] }
|
8
|
+
let(:endpoint) { nil }
|
8
9
|
|
9
|
-
let(:parser) { described_class.new(params, settings) }
|
10
|
+
let(:parser) { described_class.new(params, settings, endpoint) }
|
10
11
|
|
11
12
|
describe '#parse_request_params' do
|
12
13
|
subject(:parse_request_params) { parser.parse_request_params }
|
data/spec/lib/endpoint_spec.rb
CHANGED
@@ -49,7 +49,7 @@ describe Grape::Endpoint do
|
|
49
49
|
describe 'parse_request_params' do
|
50
50
|
let(:subject) { GrapeSwagger::Endpoint::ParamsParser }
|
51
51
|
before do
|
52
|
-
subject.send(:parse_request_params, params, {})
|
52
|
+
subject.send(:parse_request_params, params, {}, nil)
|
53
53
|
end
|
54
54
|
|
55
55
|
context 'when params do not contain an array' do
|
data/spec/lib/oapi_tasks_spec.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
RSpec.describe GrapeSwagger::Rake::OapiTasks do
|
6
|
-
|
7
|
-
|
6
|
+
module Api
|
7
|
+
class Item < Grape::API
|
8
8
|
version 'v1', using: :path
|
9
9
|
|
10
10
|
namespace :item do
|
@@ -16,14 +16,24 @@ RSpec.describe GrapeSwagger::Rake::OapiTasks do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
class Base < Grape::API
|
20
20
|
prefix :api
|
21
|
-
mount
|
21
|
+
mount Api::Item
|
22
22
|
add_swagger_documentation add_version: true
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
subject { described_class.new(
|
26
|
+
subject { described_class.new(Api::Base) }
|
27
|
+
|
28
|
+
describe '.new' do
|
29
|
+
it 'accepts class name as a constant' do
|
30
|
+
expect(described_class.new(::Api::Base).send(:api_class)).to eq(Api::Base)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'accepts class name as a string' do
|
34
|
+
expect(described_class.new('::Api::Base').send(:api_class)).to eq(Api::Base)
|
35
|
+
end
|
36
|
+
end
|
27
37
|
|
28
38
|
describe '#make_request' do
|
29
39
|
describe 'complete documentation' do
|