grape-swagger 1.1.0 → 1.4.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/.github/dependabot.yml +14 -0
- data/.github/workflows/rubocop.yml +26 -0
- data/.github/workflows/ruby.yml +32 -0
- data/.rubocop.yml +65 -2
- data/.rubocop_todo.yml +1 -1
- data/CHANGELOG.md +50 -0
- data/Gemfile +8 -3
- data/README.md +182 -13
- data/UPGRADING.md +34 -0
- data/grape-swagger.gemspec +4 -4
- data/lib/grape-swagger.rb +7 -4
- data/lib/grape-swagger/doc_methods.rb +65 -62
- 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 +2 -2
- data/lib/grape-swagger/doc_methods/operation_id.rb +2 -2
- data/lib/grape-swagger/doc_methods/parse_params.rb +20 -4
- 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 +2 -0
- 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/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/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/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_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 +1 -0
- 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 +27 -13
- data/.travis.yml +0 -35
data/UPGRADING.md
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
## Upgrading Grape-swagger
|
2
2
|
|
3
|
+
### Upgrading to >= 1.4.0
|
4
|
+
|
5
|
+
- Official support for ruby < 2.5 removed, ruby 2.5 only in testing mode, but no support.
|
6
|
+
|
7
|
+
### Upgrading to >= 1.3.0
|
8
|
+
|
9
|
+
- The model (entity) description no longer comes from the route description. It will have a default value: `<<EntityName>> model`.
|
10
|
+
|
11
|
+
### Upgrading to >= 1.2.0
|
12
|
+
|
13
|
+
- The entity_name class method is now called on parent classes for inherited entities. Now you can do this
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
module Some::Long::Module
|
17
|
+
class Base < Grape::Entity
|
18
|
+
# ... other shared logic
|
19
|
+
def self.entity_name
|
20
|
+
"V2::#{self.to_s.demodulize}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def MyEntity < Base
|
25
|
+
# ....
|
26
|
+
end
|
27
|
+
|
28
|
+
def OtherEntity < Base
|
29
|
+
# revert back to the default behavior by hiding the method
|
30
|
+
private_class_method :entity_name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
- Full class name is modified to use `_` separator (e.g. `A_B_C` instead of `A::B::C`).
|
36
|
+
|
3
37
|
### Upgrading to >= 1.1.0
|
4
38
|
|
5
39
|
Full class name is used for referencing entity by default (e.g. `A::B::C` instead of just `C`). `Entity` and `Entities` suffixes and prefixes are omitted (e.g. if entity name is `Entities::SomeScope::MyFavourite::Entity` only `SomeScope::MyFavourite` will be used).
|
data/grape-swagger.gemspec
CHANGED
@@ -7,14 +7,14 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.name = 'grape-swagger'
|
8
8
|
s.version = GrapeSwagger::VERSION
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
|
-
s.authors = ['Tim Vandecasteele']
|
11
|
-
s.email = ['tim.vandecasteele@gmail.com']
|
10
|
+
s.authors = ['LeFnord', 'Tim Vandecasteele']
|
11
|
+
s.email = ['pscholz.le@gmail.com', 'tim.vandecasteele@gmail.com']
|
12
12
|
s.homepage = 'https://github.com/ruby-grape/grape-swagger'
|
13
13
|
s.summary = 'Add auto generated documentation to your Grape API that can be displayed with Swagger.'
|
14
14
|
s.license = 'MIT'
|
15
15
|
|
16
|
-
s.required_ruby_version = '>= 2.
|
17
|
-
s.add_runtime_dependency 'grape', '~> 1.3
|
16
|
+
s.required_ruby_version = '>= 2.5'
|
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
@@ -29,7 +29,10 @@ module SwaggerRouting
|
|
29
29
|
route_match = route_path.split(/^.*?#{route.prefix}/).last
|
30
30
|
next unless route_match
|
31
31
|
|
32
|
-
|
32
|
+
# want to match emojis … ;)
|
33
|
+
# route_match = route_match
|
34
|
+
# .match('\/([\p{Alnum}|\p{Emoji}|\-|\_]*?)[\.\/\(]') || route_match.match('\/([\p{Alpha}|\p{Emoji}|\-|\_]*)$')
|
35
|
+
route_match = route_match.match('\/([\p{Alnum}|\-|\_]*?)[\.\/\(]') || route_match.match('\/([\p{Alpha}|\-|\_]*)$')
|
33
36
|
next unless route_match
|
34
37
|
|
35
38
|
resource = route_match.captures.first
|
@@ -85,7 +88,7 @@ module SwaggerRouting
|
|
85
88
|
route_name = name.match(%r{^/?([^/]*).*$})[1]
|
86
89
|
return route_name unless route_name.include? ':'
|
87
90
|
|
88
|
-
matches = name.match(
|
91
|
+
matches = name.match(/\/\p{Alpha}+/)
|
89
92
|
matches.nil? ? route_name : matches[0].delete('/')
|
90
93
|
end
|
91
94
|
|
@@ -107,8 +110,8 @@ module SwaggerRouting
|
|
107
110
|
end
|
108
111
|
|
109
112
|
module SwaggerDocumentationAdder
|
110
|
-
attr_accessor :combined_namespaces, :combined_namespace_identifiers
|
111
|
-
|
113
|
+
attr_accessor :combined_namespaces, :combined_namespace_identifiers, :combined_routes, :combined_namespace_routes
|
114
|
+
|
112
115
|
include SwaggerRouting
|
113
116
|
|
114
117
|
def add_swagger_documentation(options = {})
|
@@ -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
|
@@ -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,7 +30,7 @@ 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)
|
@@ -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)
|
@@ -27,6 +27,7 @@ module GrapeSwagger
|
|
27
27
|
document_required(settings)
|
28
28
|
document_additional_properties(settings)
|
29
29
|
document_add_extensions(settings)
|
30
|
+
document_example(settings)
|
30
31
|
|
31
32
|
@parsed_param
|
32
33
|
end
|
@@ -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,10 +105,7 @@ module GrapeSwagger
|
|
91
105
|
|
92
106
|
array_items[:default] = value_type[:default] if value_type[:default].present?
|
93
107
|
|
94
|
-
|
95
|
-
@parsed_param[:items] = array_items
|
96
|
-
@parsed_param[:type] = 'array'
|
97
|
-
@parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
|
108
|
+
array_items
|
98
109
|
end
|
99
110
|
|
100
111
|
def document_additional_properties(settings)
|
@@ -102,6 +113,11 @@ module GrapeSwagger
|
|
102
113
|
@parsed_param[:additionalProperties] = additional_properties if additional_properties
|
103
114
|
end
|
104
115
|
|
116
|
+
def document_example(settings)
|
117
|
+
example = settings[:example]
|
118
|
+
@parsed_param[:example] = example if example
|
119
|
+
end
|
120
|
+
|
105
121
|
def param_type(value_type)
|
106
122
|
param_type = value_type[:param_type] || value_type[:in]
|
107
123
|
if value_type[:path].include?("{#{value_type[:param_name]}}")
|
@@ -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
|