grape-swagger 1.0.0 → 1.3.1
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 +94 -1
- data/.rubocop_todo.yml +1 -1
- data/.travis.yml +14 -8
- data/CHANGELOG.md +56 -6
- data/Gemfile +9 -4
- data/README.md +191 -12
- data/UPGRADING.md +34 -0
- data/grape-swagger.gemspec +1 -1
- 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 +5 -5
- 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 +19 -4
- data/lib/grape-swagger/endpoint.rb +73 -31
- data/lib/grape-swagger/rake/oapi_tasks.rb +12 -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/537_enum_values_spec.rb +1 -0
- data/spec/issues/776_multiple_presents_spec.rb +56 -0
- data/spec/issues/784_extensions_on_params_spec.rb +38 -0
- data/spec/issues/809_utf8_routes_spec.rb +55 -0
- data/spec/lib/data_type_spec.rb +12 -0
- data/spec/lib/move_params_spec.rb +2 -2
- data/spec/lib/oapi_tasks_spec.rb +15 -5
- data/spec/support/empty_model_parser.rb +1 -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 +8 -8
- data/spec/support/model_parsers/representable_parser.rb +8 -8
- data/spec/support/namespace_tags.rb +1 -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_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 +56 -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/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 +1 -0
- metadata +19 -9
data/UPGRADING.md
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
## Upgrading Grape-swagger
|
2
2
|
|
3
|
+
### Upgrading to >= 1.3.0
|
4
|
+
|
5
|
+
- The model (entity) description no longer comes from the route description. It will have a default value: `<<EntityName>> model`.
|
6
|
+
|
7
|
+
### Upgrading to >= 1.2.0
|
8
|
+
|
9
|
+
- The entity_name class method is now called on parent classes for inherited entities. Now you can do this
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
module Some::Long::Module
|
13
|
+
class Base < Grape::Entity
|
14
|
+
# ... other shared logic
|
15
|
+
def self.entity_name
|
16
|
+
"V2::#{self.to_s.demodulize}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def MyEntity < Base
|
21
|
+
# ....
|
22
|
+
end
|
23
|
+
|
24
|
+
def OtherEntity < Base
|
25
|
+
# revert back to the default behavior by hiding the method
|
26
|
+
private_class_method :entity_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
- Full class name is modified to use `_` separator (e.g. `A_B_C` instead of `A::B::C`).
|
32
|
+
|
33
|
+
### Upgrading to >= 1.1.0
|
34
|
+
|
35
|
+
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).
|
36
|
+
|
3
37
|
### Upgrading to >= 0.26.1
|
4
38
|
|
5
39
|
The format can now be specified,
|
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', '~> 1.3
|
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('::')[
|
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
|
|
@@ -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.include?(b[:name].to_s.gsub(/\[\]\z/, '')
|
10
|
+
p[:name] != b[:name] && p[:name].to_s.include?("#{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)
|
@@ -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)
|
@@ -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,45 @@ 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
|
+
elsif value[:as]
|
274
|
+
memo[value[:code]][:schema] = {
|
275
|
+
type: :object,
|
276
|
+
properties: build_reference(route, value, response_model, options)
|
277
|
+
}
|
278
|
+
else
|
279
|
+
memo[value[:code]][:schema] = build_reference(route, value, response_model, options)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
264
283
|
def build_reference(route, value, response_model, settings)
|
265
284
|
# TODO: proof that the definition exist, if model isn't specified
|
266
|
-
reference =
|
285
|
+
reference = if value.key?(:as)
|
286
|
+
{ value[:as] => build_reference_hash(response_model) }
|
287
|
+
else
|
288
|
+
build_reference_hash(response_model)
|
289
|
+
end
|
267
290
|
return reference unless value[:code] < 300
|
268
291
|
|
269
|
-
|
292
|
+
if value.key?(:as) && value.key?(:is_array)
|
293
|
+
reference[value[:as]] = build_reference_array(reference[value[:as]])
|
294
|
+
elsif route.options[:is_array]
|
295
|
+
reference = build_reference_array(reference)
|
296
|
+
end
|
297
|
+
|
270
298
|
build_root(route, reference, response_model, settings)
|
271
299
|
end
|
272
300
|
|
301
|
+
def build_reference_hash(response_model)
|
302
|
+
{ '$ref' => "#/definitions/#{response_model}" }
|
303
|
+
end
|
304
|
+
|
305
|
+
def build_reference_array(reference)
|
306
|
+
{ type: 'array', items: reference }
|
307
|
+
end
|
308
|
+
|
273
309
|
def build_root(route, reference, response_model, settings)
|
274
310
|
default_root = response_model.underscore
|
275
311
|
default_root = default_root.pluralize if route.options[:is_array]
|
@@ -293,16 +329,13 @@ module Grape
|
|
293
329
|
memo['schema'] = { type: 'file' }
|
294
330
|
end
|
295
331
|
|
296
|
-
def
|
297
|
-
declared_params = route.settings[:declared_params] if route.settings[:declared_params].present?
|
332
|
+
def build_request_params(route, settings)
|
298
333
|
required = merge_params(route)
|
299
334
|
required = GrapeSwagger::DocMethods::Headers.parse(route) + required unless route.headers.nil?
|
300
335
|
|
301
336
|
default_type(required)
|
302
337
|
|
303
|
-
request_params =
|
304
|
-
GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
|
305
|
-
end || {}
|
338
|
+
request_params = GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
|
306
339
|
|
307
340
|
request_params.empty? ? required : request_params
|
308
341
|
end
|
@@ -340,13 +373,10 @@ module Grape
|
|
340
373
|
parser = GrapeSwagger.model_parsers.find(model)
|
341
374
|
raise GrapeSwagger::Errors::UnregisteredParser, "No parser registered for #{model_name}." unless parser
|
342
375
|
|
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
|
376
|
+
parsed_response = parser.new(model, self).call
|
348
377
|
|
349
|
-
@definitions[model_name] =
|
378
|
+
@definitions[model_name] =
|
379
|
+
GrapeSwagger::DocMethods::BuildModelDefinition.parse_params_from_model(parsed_response, model, model_name)
|
350
380
|
|
351
381
|
model_name
|
352
382
|
end
|
@@ -363,6 +393,16 @@ module Grape
|
|
363
393
|
options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
|
364
394
|
end
|
365
395
|
|
396
|
+
def hidden_parameter?(value)
|
397
|
+
return false if value[:required]
|
398
|
+
|
399
|
+
if value.dig(:documentation, :hidden).is_a?(Proc)
|
400
|
+
value.dig(:documentation, :hidden).call
|
401
|
+
else
|
402
|
+
value.dig(:documentation, :hidden)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
366
406
|
def success_code_from_entity(route, entity)
|
367
407
|
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
|
368
408
|
if entity.is_a?(Hash)
|
@@ -371,6 +411,8 @@ module Grape
|
|
371
411
|
default_code[:message] = entity[:message] || route.description || default_code[:message].sub('{item}', @item)
|
372
412
|
default_code[:examples] = entity[:examples] if entity[:examples]
|
373
413
|
default_code[:headers] = entity[:headers] if entity[:headers]
|
414
|
+
default_code[:as] = entity[:as] if entity[:as]
|
415
|
+
default_code[:is_array] = entity[:is_array] if entity[:is_array]
|
374
416
|
else
|
375
417
|
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
|
376
418
|
default_code[:model] = entity if entity
|