grape-swagger 1.1.0 → 1.4.2
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/.github/dependabot.yml +14 -0
- data/.github/workflows/rubocop.yml +26 -0
- data/.github/workflows/ruby.yml +30 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.rubocop.yml +65 -2
- data/.rubocop_todo.yml +1 -1
- data/CHANGELOG.md +66 -0
- data/Gemfile +9 -4
- data/README.md +213 -13
- data/UPGRADING.md +38 -0
- data/grape-swagger.gemspec +4 -4
- data/lib/grape-swagger/doc_methods/build_model_definition.rb +53 -2
- data/lib/grape-swagger/doc_methods/data_type.rb +4 -4
- data/lib/grape-swagger/doc_methods/format_data.rb +4 -2
- data/lib/grape-swagger/doc_methods/move_params.rb +6 -7
- data/lib/grape-swagger/doc_methods/operation_id.rb +2 -2
- data/lib/grape-swagger/doc_methods/parse_params.rb +51 -9
- data/lib/grape-swagger/doc_methods.rb +65 -62
- data/lib/grape-swagger/endpoint.rb +83 -32
- data/lib/grape-swagger/errors.rb +2 -0
- data/lib/grape-swagger/model_parsers.rb +2 -2
- data/lib/grape-swagger/rake/oapi_tasks.rb +3 -1
- data/lib/grape-swagger/version.rb +1 -1
- data/lib/grape-swagger.rb +7 -4
- data/spec/issues/427_entity_as_string_spec.rb +1 -1
- data/spec/issues/430_entity_definitions_spec.rb +7 -5
- data/spec/issues/537_enum_values_spec.rb +1 -0
- data/spec/issues/776_multiple_presents_spec.rb +59 -0
- data/spec/issues/809_utf8_routes_spec.rb +55 -0
- data/spec/issues/832_array_hash_float_decimal_spec.rb +111 -0
- data/spec/lib/data_type_spec.rb +12 -0
- data/spec/lib/format_data_spec.rb +24 -0
- data/spec/lib/move_params_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/support/empty_model_parser.rb +3 -2
- data/spec/support/mock_parser.rb +1 -2
- data/spec/support/model_parsers/entity_parser.rb +8 -8
- data/spec/support/model_parsers/mock_parser.rb +24 -8
- data/spec/support/model_parsers/representable_parser.rb +8 -8
- data/spec/support/namespace_tags.rb +3 -0
- data/spec/support/the_paths_definitions.rb +4 -4
- data/spec/swagger_v2/api_swagger_v2_additional_properties_spec.rb +83 -0
- data/spec/swagger_v2/api_swagger_v2_hide_param_spec.rb +1 -1
- data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +1 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +73 -1
- data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +2 -2
- data/spec/swagger_v2/api_swagger_v2_response_with_headers_spec.rb +4 -2
- data/spec/swagger_v2/api_swagger_v2_response_with_models_spec.rb +53 -0
- data/spec/swagger_v2/api_swagger_v2_spec.rb +1 -0
- data/spec/swagger_v2/boolean_params_spec.rb +4 -1
- data/spec/swagger_v2/float_api_spec.rb +1 -0
- data/spec/swagger_v2/inheritance_and_discriminator_spec.rb +57 -0
- data/spec/swagger_v2/namespace_tags_prefix_spec.rb +1 -0
- data/spec/swagger_v2/param_multi_type_spec.rb +2 -0
- data/spec/swagger_v2/param_type_spec.rb +3 -0
- data/spec/swagger_v2/param_values_spec.rb +6 -0
- data/spec/swagger_v2/{params_array_collection_fromat_spec.rb → params_array_collection_format_spec.rb} +0 -0
- data/spec/swagger_v2/params_example_spec.rb +40 -0
- data/spec/swagger_v2/reference_entity_spec.rb +74 -29
- data/spec/swagger_v2/security_requirement_spec.rb +2 -2
- data/spec/swagger_v2/simple_mounted_api_spec.rb +3 -0
- metadata +31 -13
- data/.travis.yml +0 -35
@@ -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)
|
@@ -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('::')[0..-2].join('
|
54
|
+
model.to_s.split('::')[0..-2].join('_')
|
55
55
|
elsif model.to_s.start_with?('Entity::', 'Entities::', 'Representable::')
|
56
|
-
model.to_s.split('::')[1..-1].join('
|
56
|
+
model.to_s.split('::')[1..-1].join('_')
|
57
57
|
else
|
58
|
-
model.to_s
|
58
|
+
model.to_s.split('::').join('_')
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -7,7 +7,7 @@ module GrapeSwagger
|
|
7
7
|
def to_format(parameters)
|
8
8
|
parameters.reject { |parameter| parameter[:in] == 'body' }.each do |b|
|
9
9
|
related_parameters = parameters.select do |p|
|
10
|
-
p[:name] != b[:name] && p[:name].to_s.
|
10
|
+
p[:name] != b[:name] && p[:name].to_s.start_with?("#{b[:name].to_s.gsub(/\[\]\z/, '')}[")
|
11
11
|
end
|
12
12
|
parameters.reject! { |p| p[:name] == b[:name] } if move_down(b, related_parameters)
|
13
13
|
end
|
@@ -30,11 +30,13 @@ module GrapeSwagger
|
|
30
30
|
|
31
31
|
def add_braces(parameter, related_parameters)
|
32
32
|
param_name = parameter[:name].gsub(/\A(.*)\[\]\z/, '\1')
|
33
|
-
related_parameters.each { |p| p[:name] = p[:name].gsub(param_name, param_name
|
33
|
+
related_parameters.each { |p| p[:name] = p[:name].gsub(param_name, "#{param_name}[]") }
|
34
34
|
end
|
35
35
|
|
36
36
|
def add_array(parameter, related_parameters)
|
37
37
|
related_parameters.each do |p|
|
38
|
+
next if p.key?(:items)
|
39
|
+
|
38
40
|
p_type = p[:type] == 'array' ? 'string' : p[:type]
|
39
41
|
p[:items] = { type: p_type, format: p[:format], enum: p[:enum], is_array: p[:is_array] }
|
40
42
|
p[:items].delete_if { |_k, v| v.nil? }
|
@@ -103,9 +103,9 @@ module GrapeSwagger
|
|
103
103
|
|
104
104
|
def document_as_property(param)
|
105
105
|
property_keys.each_with_object({}) do |x, memo|
|
106
|
-
|
107
|
-
next if value.blank?
|
106
|
+
next unless param.key?(x)
|
108
107
|
|
108
|
+
value = param[x]
|
109
109
|
if x == :type && @definitions[value].present?
|
110
110
|
memo['$ref'] = "#/definitions/#{value}"
|
111
111
|
else
|
@@ -181,7 +181,8 @@ module GrapeSwagger
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def property_keys
|
184
|
-
%i[type format description minimum maximum items enum default additionalProperties
|
184
|
+
%i[type format description minimum maximum items enum default additional_properties additionalProperties
|
185
|
+
example]
|
185
186
|
end
|
186
187
|
|
187
188
|
def deletable?(param)
|
@@ -193,8 +194,7 @@ module GrapeSwagger
|
|
193
194
|
end
|
194
195
|
|
195
196
|
def includes_body_param?(params)
|
196
|
-
params.
|
197
|
-
false
|
197
|
+
params.any? { |x| x[:in] == 'body' || x[:param_type] == 'body' }
|
198
198
|
end
|
199
199
|
|
200
200
|
def should_expose_as_array?(params)
|
@@ -202,8 +202,7 @@ module GrapeSwagger
|
|
202
202
|
end
|
203
203
|
|
204
204
|
def should_exposed_as(params)
|
205
|
-
params.
|
206
|
-
'array'
|
205
|
+
params.any? { |x| x[:type] && x[:type] != 'array' } ? 'object' : 'array'
|
207
206
|
end
|
208
207
|
end
|
209
208
|
end
|
@@ -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)
|
@@ -25,8 +25,9 @@ module GrapeSwagger
|
|
25
25
|
document_default_value(settings) unless value_type[:is_array]
|
26
26
|
document_range_values(settings) unless value_type[:is_array]
|
27
27
|
document_required(settings)
|
28
|
-
document_additional_properties(settings)
|
28
|
+
document_additional_properties(definitions, settings) unless value_type[:is_array]
|
29
29
|
document_add_extensions(settings)
|
30
|
+
document_example(settings)
|
30
31
|
|
31
32
|
@parsed_param
|
32
33
|
end
|
@@ -50,7 +51,7 @@ module GrapeSwagger
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def document_default_value(settings)
|
53
|
-
@parsed_param[:default] = settings[:default] if settings
|
54
|
+
@parsed_param[:default] = settings[:default] if settings.key?(:default)
|
54
55
|
end
|
55
56
|
|
56
57
|
def document_type_and_format(settings, data_type)
|
@@ -77,6 +78,19 @@ module GrapeSwagger
|
|
77
78
|
|
78
79
|
param_type ||= value_type[:param_type]
|
79
80
|
|
81
|
+
array_items = parse_array_item(
|
82
|
+
definitions,
|
83
|
+
type,
|
84
|
+
value_type
|
85
|
+
)
|
86
|
+
|
87
|
+
@parsed_param[:in] = param_type || 'formData'
|
88
|
+
@parsed_param[:items] = array_items
|
89
|
+
@parsed_param[:type] = 'array'
|
90
|
+
@parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_array_item(definitions, type, value_type)
|
80
94
|
array_items = {}
|
81
95
|
if definitions[value_type[:data_type]]
|
82
96
|
array_items['$ref'] = "#/definitions/#{@parsed_param[:type]}"
|
@@ -91,15 +105,43 @@ module GrapeSwagger
|
|
91
105
|
|
92
106
|
array_items[:default] = value_type[:default] if value_type[:default].present?
|
93
107
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
108
|
+
set_additional_properties, additional_properties = parse_additional_properties(definitions, value_type)
|
109
|
+
array_items[:additionalProperties] = additional_properties if set_additional_properties
|
110
|
+
|
111
|
+
array_items
|
112
|
+
end
|
113
|
+
|
114
|
+
def document_additional_properties(definitions, settings)
|
115
|
+
set_additional_properties, additional_properties = parse_additional_properties(definitions, settings)
|
116
|
+
@parsed_param[:additionalProperties] = additional_properties if set_additional_properties
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse_additional_properties(definitions, settings)
|
120
|
+
return false unless settings.key?(:additionalProperties) || settings.key?(:additional_properties)
|
121
|
+
|
122
|
+
value =
|
123
|
+
if settings.key?(:additionalProperties)
|
124
|
+
GrapeSwagger::Errors::SwaggerSpecDeprecated.tell!(:additionalProperties)
|
125
|
+
settings[:additionalProperties]
|
126
|
+
else
|
127
|
+
settings[:additional_properties]
|
128
|
+
end
|
129
|
+
|
130
|
+
parsed_value =
|
131
|
+
if definitions[value.to_s]
|
132
|
+
{ '$ref': "#/definitions/#{value}" }
|
133
|
+
elsif value.is_a?(Class)
|
134
|
+
{ type: DataType.call(value) }
|
135
|
+
else
|
136
|
+
value
|
137
|
+
end
|
138
|
+
|
139
|
+
[true, parsed_value]
|
98
140
|
end
|
99
141
|
|
100
|
-
def
|
101
|
-
|
102
|
-
@parsed_param[:
|
142
|
+
def document_example(settings)
|
143
|
+
example = settings[:example]
|
144
|
+
@parsed_param[:example] = example if example
|
103
145
|
end
|
104
146
|
|
105
147
|
def param_type(value_type)
|
@@ -17,6 +17,61 @@ require 'grape-swagger/doc_methods/version'
|
|
17
17
|
|
18
18
|
module GrapeSwagger
|
19
19
|
module DocMethods
|
20
|
+
DEFAULTS =
|
21
|
+
{
|
22
|
+
info: {},
|
23
|
+
models: [],
|
24
|
+
doc_version: '0.0.1',
|
25
|
+
target_class: nil,
|
26
|
+
mount_path: '/swagger_doc',
|
27
|
+
host: nil,
|
28
|
+
base_path: nil,
|
29
|
+
add_base_path: false,
|
30
|
+
add_version: true,
|
31
|
+
add_root: false,
|
32
|
+
hide_documentation_path: true,
|
33
|
+
format: :json,
|
34
|
+
authorizations: nil,
|
35
|
+
security_definitions: nil,
|
36
|
+
security: nil,
|
37
|
+
api_documentation: { desc: 'Swagger compatible API description' },
|
38
|
+
specific_api_documentation: { desc: 'Swagger compatible API description for specific API' },
|
39
|
+
endpoint_auth_wrapper: nil,
|
40
|
+
swagger_endpoint_guard: nil,
|
41
|
+
token_owner: nil
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
FORMATTER_METHOD = %i[format default_format default_error_formatter].freeze
|
45
|
+
|
46
|
+
def self.output_path_definitions(combi_routes, endpoint, target_class, options)
|
47
|
+
output = endpoint.swagger_object(
|
48
|
+
target_class,
|
49
|
+
endpoint.request,
|
50
|
+
options
|
51
|
+
)
|
52
|
+
|
53
|
+
paths, definitions = endpoint.path_and_definition_objects(combi_routes, options)
|
54
|
+
tags = tags_from(paths, options)
|
55
|
+
|
56
|
+
output[:tags] = tags unless tags.empty? || paths.blank?
|
57
|
+
output[:paths] = paths unless paths.blank?
|
58
|
+
output[:definitions] = definitions unless definitions.blank?
|
59
|
+
|
60
|
+
output
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.tags_from(paths, options)
|
64
|
+
tags = GrapeSwagger::DocMethods::TagNameDescription.build(paths)
|
65
|
+
|
66
|
+
if options[:tags]
|
67
|
+
names = options[:tags].map { |t| t[:name] }
|
68
|
+
tags.reject! { |t| names.include?(t[:name]) }
|
69
|
+
tags += options[:tags]
|
70
|
+
end
|
71
|
+
|
72
|
+
tags
|
73
|
+
end
|
74
|
+
|
20
75
|
def hide_documentation_path
|
21
76
|
@@hide_documentation_path
|
22
77
|
end
|
@@ -26,54 +81,32 @@ module GrapeSwagger
|
|
26
81
|
end
|
27
82
|
|
28
83
|
def setup(options)
|
29
|
-
options =
|
84
|
+
options = DEFAULTS.merge(options)
|
30
85
|
|
31
86
|
# options could be set on #add_swagger_documentation call,
|
32
87
|
# for available options see #defaults
|
33
88
|
target_class = options[:target_class]
|
34
89
|
guard = options[:swagger_endpoint_guard]
|
35
|
-
formatter = options[:format]
|
36
90
|
api_doc = options[:api_documentation].dup
|
37
91
|
specific_api_doc = options[:specific_api_documentation].dup
|
38
92
|
|
39
93
|
class_variables_from(options)
|
40
94
|
|
41
|
-
|
42
|
-
%i[format default_format default_error_formatter].each do |method|
|
43
|
-
send(method, formatter)
|
44
|
-
end
|
45
|
-
end
|
95
|
+
setup_formatter(options[:format])
|
46
96
|
|
47
97
|
desc api_doc.delete(:desc), api_doc
|
48
98
|
|
49
|
-
output_path_definitions = proc do |combi_routes, endpoint|
|
50
|
-
output = endpoint.swagger_object(
|
51
|
-
target_class,
|
52
|
-
endpoint.request,
|
53
|
-
options
|
54
|
-
)
|
55
|
-
|
56
|
-
paths, definitions = endpoint.path_and_definition_objects(combi_routes, options)
|
57
|
-
tags = tags_from(paths, options)
|
58
|
-
|
59
|
-
output[:tags] = tags unless tags.empty? || paths.blank?
|
60
|
-
output[:paths] = paths unless paths.blank?
|
61
|
-
output[:definitions] = definitions unless definitions.blank?
|
62
|
-
|
63
|
-
output
|
64
|
-
end
|
65
|
-
|
66
99
|
instance_eval(guard) unless guard.nil?
|
67
100
|
|
68
101
|
get mount_path do
|
69
102
|
header['Access-Control-Allow-Origin'] = '*'
|
70
103
|
header['Access-Control-Request-Method'] = '*'
|
71
104
|
|
72
|
-
|
105
|
+
GrapeSwagger::DocMethods
|
106
|
+
.output_path_definitions(target_class.combined_namespace_routes, self, target_class, options)
|
73
107
|
end
|
74
108
|
|
75
|
-
desc specific_api_doc.delete(:desc), { params:
|
76
|
-
specific_api_doc.delete(:params) || {} }.merge(specific_api_doc)
|
109
|
+
desc specific_api_doc.delete(:desc), { params: specific_api_doc.delete(:params) || {}, **specific_api_doc }
|
77
110
|
|
78
111
|
params do
|
79
112
|
requires :name, type: String, desc: 'Resource name of mounted API'
|
@@ -88,51 +121,21 @@ module GrapeSwagger
|
|
88
121
|
combined_routes = target_class.combined_namespace_routes[params[:name]]
|
89
122
|
error!({ error: 'named resource not exist' }, 400) if combined_routes.nil?
|
90
123
|
|
91
|
-
|
124
|
+
GrapeSwagger::DocMethods
|
125
|
+
.output_path_definitions({ params[:name] => combined_routes }, self, target_class, options)
|
92
126
|
end
|
93
127
|
end
|
94
128
|
|
95
|
-
def defaults
|
96
|
-
{
|
97
|
-
info: {},
|
98
|
-
models: [],
|
99
|
-
doc_version: '0.0.1',
|
100
|
-
target_class: nil,
|
101
|
-
mount_path: '/swagger_doc',
|
102
|
-
host: nil,
|
103
|
-
base_path: nil,
|
104
|
-
add_base_path: false,
|
105
|
-
add_version: true,
|
106
|
-
add_root: false,
|
107
|
-
hide_documentation_path: true,
|
108
|
-
format: :json,
|
109
|
-
authorizations: nil,
|
110
|
-
security_definitions: nil,
|
111
|
-
security: nil,
|
112
|
-
api_documentation: { desc: 'Swagger compatible API description' },
|
113
|
-
specific_api_documentation: { desc: 'Swagger compatible API description for specific API' },
|
114
|
-
endpoint_auth_wrapper: nil,
|
115
|
-
swagger_endpoint_guard: nil,
|
116
|
-
token_owner: nil
|
117
|
-
}
|
118
|
-
end
|
119
|
-
|
120
129
|
def class_variables_from(options)
|
121
130
|
@@mount_path = options[:mount_path]
|
122
131
|
@@class_name = options[:class_name] || options[:mount_path].delete('/')
|
123
132
|
@@hide_documentation_path = options[:hide_documentation_path]
|
124
133
|
end
|
125
134
|
|
126
|
-
def
|
127
|
-
|
135
|
+
def setup_formatter(formatter)
|
136
|
+
return unless formatter
|
128
137
|
|
129
|
-
|
130
|
-
names = options[:tags].map { |t| t[:name] }
|
131
|
-
tags.reject! { |t| names.include?(t[:name]) }
|
132
|
-
tags += options[:tags]
|
133
|
-
end
|
134
|
-
|
135
|
-
tags
|
138
|
+
FORMATTER_METHOD.each { |method| send(method, formatter) }
|
136
139
|
end
|
137
140
|
end
|
138
141
|
end
|
@@ -11,7 +11,7 @@ module Grape
|
|
11
11
|
|
12
12
|
if content_types.empty?
|
13
13
|
formats = [target_class.format, target_class.default_format].compact.uniq
|
14
|
-
formats = Grape::Formatter.formatters({}).keys if formats.empty?
|
14
|
+
formats = Grape::Formatter.formatters(**{}).keys if formats.empty?
|
15
15
|
content_types = Grape::ContentTypes::CONTENT_TYPES.select do |content_type, _mime_type|
|
16
16
|
formats.include? content_type
|
17
17
|
end.values
|
@@ -78,11 +78,11 @@ module Grape
|
|
78
78
|
def path_and_definition_objects(namespace_routes, options)
|
79
79
|
@paths = {}
|
80
80
|
@definitions = {}
|
81
|
+
add_definitions_from options[:models]
|
81
82
|
namespace_routes.each_value do |routes|
|
82
83
|
path_item(routes, options)
|
83
84
|
end
|
84
85
|
|
85
|
-
add_definitions_from options[:models]
|
86
86
|
[@paths, @definitions]
|
87
87
|
end
|
88
88
|
|
@@ -175,15 +175,18 @@ module Grape
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def params_object(route, options, path)
|
178
|
-
parameters =
|
178
|
+
parameters = build_request_params(route, options).each_with_object([]) do |(param, value), memo|
|
179
|
+
next if hidden_parameter?(value)
|
180
|
+
|
179
181
|
value = { required: false }.merge(value) if value.is_a?(Hash)
|
180
182
|
_, value = default_type([[param, value]]).first if value == ''
|
183
|
+
|
181
184
|
if value.dig(:documentation, :type)
|
182
185
|
expose_params(value[:documentation][:type])
|
183
186
|
elsif value[:type]
|
184
187
|
expose_params(value[:type])
|
185
188
|
end
|
186
|
-
GrapeSwagger::DocMethods::ParseParams.call(param, value, path, route, @definitions)
|
189
|
+
memo << GrapeSwagger::DocMethods::ParseParams.call(param, value, path, route, @definitions)
|
187
190
|
end
|
188
191
|
|
189
192
|
if GrapeSwagger::DocMethods::MoveParams.can_be_moved?(route.request_method, parameters)
|
@@ -196,35 +199,38 @@ module Grape
|
|
196
199
|
end
|
197
200
|
|
198
201
|
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|
|
202
|
+
codes(route).each_with_object({}) do |value, memo|
|
203
203
|
value[:message] ||= ''
|
204
|
-
memo[value[:code]] = { description: value[:message] }
|
205
|
-
|
204
|
+
memo[value[:code]] = { description: value[:message] ||= '' } unless memo[value[:code]].present?
|
206
205
|
memo[value[:code]][:headers] = value[:headers] if value[:headers]
|
207
206
|
|
208
207
|
next build_file_response(memo[value[:code]]) if file_response?(value[:model])
|
209
208
|
|
210
|
-
response_model = @item
|
211
|
-
response_model = expose_params_from_model(value[:model]) if value[:model]
|
212
|
-
|
213
209
|
if memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
|
214
210
|
memo[204] = memo.delete(200)
|
215
211
|
value[:code] = 204
|
212
|
+
next
|
216
213
|
end
|
217
214
|
|
218
|
-
|
219
|
-
next
|
215
|
+
# Explicitly request no model with { model: '' }
|
216
|
+
next if value[:model] == ''
|
220
217
|
|
221
|
-
|
218
|
+
response_model = value[:model] ? expose_params_from_model(value[:model]) : @item
|
219
|
+
next unless @definitions[response_model]
|
220
|
+
next if response_model.start_with?('Swagger_doc')
|
222
221
|
|
223
|
-
|
222
|
+
@definitions[response_model][:description] ||= "#{response_model} model"
|
223
|
+
build_memo_schema(memo, route, value, response_model, options)
|
224
224
|
memo[value[:code]][:examples] = value[:examples] if value[:examples]
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
+
def codes(route)
|
229
|
+
http_codes_from_route(route).map do |x|
|
230
|
+
x.is_a?(Array) ? { code: x[0], message: x[1], model: x[2], examples: x[3], headers: x[4] } : x
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
228
234
|
def success_code?(code)
|
229
235
|
status = code.is_a?(Array) ? code.first : code[:code]
|
230
236
|
status.between?(200, 299)
|
@@ -250,7 +256,7 @@ module Grape
|
|
250
256
|
|
251
257
|
def tag_object(route, path)
|
252
258
|
version = GrapeSwagger::DocMethods::Version.get(route)
|
253
|
-
version =
|
259
|
+
version = Array(version)
|
254
260
|
prefix = route.prefix.to_s.split('/').reject(&:empty?)
|
255
261
|
Array(
|
256
262
|
path.split('{')[0].split('/').reject(&:empty?).delete_if do |i|
|
@@ -261,15 +267,52 @@ module Grape
|
|
261
267
|
|
262
268
|
private
|
263
269
|
|
270
|
+
def build_memo_schema(memo, route, value, response_model, options)
|
271
|
+
if memo[value[:code]][:schema] && value[:as]
|
272
|
+
memo[value[:code]][:schema][:properties].merge!(build_reference(route, value, response_model, options))
|
273
|
+
|
274
|
+
if value[:required]
|
275
|
+
memo[value[:code]][:schema][:required] ||= []
|
276
|
+
memo[value[:code]][:schema][:required] << value[:as].to_s
|
277
|
+
end
|
278
|
+
|
279
|
+
elsif value[:as]
|
280
|
+
memo[value[:code]][:schema] = {
|
281
|
+
type: :object,
|
282
|
+
properties: build_reference(route, value, response_model, options)
|
283
|
+
}
|
284
|
+
memo[value[:code]][:schema][:required] = [value[:as].to_s] if value[:required]
|
285
|
+
else
|
286
|
+
memo[value[:code]][:schema] = build_reference(route, value, response_model, options)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
264
290
|
def build_reference(route, value, response_model, settings)
|
265
291
|
# TODO: proof that the definition exist, if model isn't specified
|
266
|
-
reference =
|
292
|
+
reference = if value.key?(:as)
|
293
|
+
{ value[:as] => build_reference_hash(response_model) }
|
294
|
+
else
|
295
|
+
build_reference_hash(response_model)
|
296
|
+
end
|
267
297
|
return reference unless value[:code] < 300
|
268
298
|
|
269
|
-
|
299
|
+
if value.key?(:as) && value.key?(:is_array)
|
300
|
+
reference[value[:as]] = build_reference_array(reference[value[:as]])
|
301
|
+
elsif route.options[:is_array]
|
302
|
+
reference = build_reference_array(reference)
|
303
|
+
end
|
304
|
+
|
270
305
|
build_root(route, reference, response_model, settings)
|
271
306
|
end
|
272
307
|
|
308
|
+
def build_reference_hash(response_model)
|
309
|
+
{ '$ref' => "#/definitions/#{response_model}" }
|
310
|
+
end
|
311
|
+
|
312
|
+
def build_reference_array(reference)
|
313
|
+
{ type: 'array', items: reference }
|
314
|
+
end
|
315
|
+
|
273
316
|
def build_root(route, reference, response_model, settings)
|
274
317
|
default_root = response_model.underscore
|
275
318
|
default_root = default_root.pluralize if route.options[:is_array]
|
@@ -286,23 +329,20 @@ module Grape
|
|
286
329
|
end
|
287
330
|
|
288
331
|
def file_response?(value)
|
289
|
-
value.to_s.casecmp('file').zero?
|
332
|
+
value.to_s.casecmp('file').zero?
|
290
333
|
end
|
291
334
|
|
292
335
|
def build_file_response(memo)
|
293
336
|
memo['schema'] = { type: 'file' }
|
294
337
|
end
|
295
338
|
|
296
|
-
def
|
297
|
-
declared_params = route.settings[:declared_params] if route.settings[:declared_params].present?
|
339
|
+
def build_request_params(route, settings)
|
298
340
|
required = merge_params(route)
|
299
341
|
required = GrapeSwagger::DocMethods::Headers.parse(route) + required unless route.headers.nil?
|
300
342
|
|
301
343
|
default_type(required)
|
302
344
|
|
303
|
-
request_params =
|
304
|
-
GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
|
305
|
-
end || {}
|
345
|
+
request_params = GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
|
306
346
|
|
307
347
|
request_params.empty? ? required : request_params
|
308
348
|
end
|
@@ -340,12 +380,10 @@ module Grape
|
|
340
380
|
parser = GrapeSwagger.model_parsers.find(model)
|
341
381
|
raise GrapeSwagger::Errors::UnregisteredParser, "No parser registered for #{model_name}." unless parser
|
342
382
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
end
|
348
|
-
@definitions[model_name] = GrapeSwagger::DocMethods::BuildModelDefinition.build(model, properties, required)
|
383
|
+
parsed_response = parser.new(model, self).call
|
384
|
+
|
385
|
+
@definitions[model_name] =
|
386
|
+
GrapeSwagger::DocMethods::BuildModelDefinition.parse_params_from_model(parsed_response, model, model_name)
|
349
387
|
|
350
388
|
model_name
|
351
389
|
end
|
@@ -362,6 +400,16 @@ module Grape
|
|
362
400
|
options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
|
363
401
|
end
|
364
402
|
|
403
|
+
def hidden_parameter?(value)
|
404
|
+
return false if value[:required]
|
405
|
+
|
406
|
+
if value.dig(:documentation, :hidden).is_a?(Proc)
|
407
|
+
value.dig(:documentation, :hidden).call
|
408
|
+
else
|
409
|
+
value.dig(:documentation, :hidden)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
365
413
|
def success_code_from_entity(route, entity)
|
366
414
|
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
|
367
415
|
if entity.is_a?(Hash)
|
@@ -370,6 +418,9 @@ module Grape
|
|
370
418
|
default_code[:message] = entity[:message] || route.description || default_code[:message].sub('{item}', @item)
|
371
419
|
default_code[:examples] = entity[:examples] if entity[:examples]
|
372
420
|
default_code[:headers] = entity[:headers] if entity[:headers]
|
421
|
+
default_code[:as] = entity[:as] if entity[:as]
|
422
|
+
default_code[:is_array] = entity[:is_array] if entity[:is_array]
|
423
|
+
default_code[:required] = entity[:required] if entity[:required]
|
373
424
|
else
|
374
425
|
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
|
375
426
|
default_code[:model] = entity if entity
|