grape-swagger 0.34.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +86 -0
  3. data/.travis.yml +19 -12
  4. data/CHANGELOG.md +47 -4
  5. data/Gemfile +9 -3
  6. data/README.md +125 -12
  7. data/UPGRADING.md +34 -0
  8. data/grape-swagger.gemspec +1 -2
  9. data/lib/grape-swagger.rb +2 -2
  10. data/lib/grape-swagger/doc_methods.rb +65 -62
  11. data/lib/grape-swagger/doc_methods/build_model_definition.rb +53 -2
  12. data/lib/grape-swagger/doc_methods/data_type.rb +6 -6
  13. data/lib/grape-swagger/doc_methods/format_data.rb +2 -2
  14. data/lib/grape-swagger/doc_methods/operation_id.rb +2 -2
  15. data/lib/grape-swagger/doc_methods/parse_params.rb +19 -4
  16. data/lib/grape-swagger/endpoint.rb +37 -26
  17. data/lib/grape-swagger/endpoint/params_parser.rb +12 -5
  18. data/lib/grape-swagger/rake/oapi_tasks.rb +12 -2
  19. data/lib/grape-swagger/version.rb +1 -1
  20. data/spec/issues/427_entity_as_string_spec.rb +1 -1
  21. data/spec/issues/430_entity_definitions_spec.rb +7 -5
  22. data/spec/issues/784_extensions_on_params_spec.rb +38 -0
  23. data/spec/lib/data_type_spec.rb +14 -2
  24. data/spec/lib/endpoint/params_parser_spec.rb +2 -1
  25. data/spec/lib/endpoint_spec.rb +1 -1
  26. data/spec/lib/move_params_spec.rb +2 -2
  27. data/spec/lib/oapi_tasks_spec.rb +15 -5
  28. data/spec/support/empty_model_parser.rb +1 -2
  29. data/spec/support/mock_parser.rb +1 -2
  30. data/spec/support/model_parsers/entity_parser.rb +9 -9
  31. data/spec/support/model_parsers/mock_parser.rb +8 -8
  32. data/spec/support/model_parsers/representable_parser.rb +9 -9
  33. data/spec/swagger_v2/api_swagger_v2_hide_param_spec.rb +14 -3
  34. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +2 -2
  35. data/spec/swagger_v2/api_swagger_v2_response_with_models_spec.rb +53 -0
  36. data/spec/swagger_v2/boolean_params_spec.rb +1 -1
  37. data/spec/swagger_v2/inheritance_and_discriminator_spec.rb +56 -0
  38. data/spec/swagger_v2/reference_entity_spec.rb +74 -29
  39. data/spec/swagger_v2/security_requirement_spec.rb +2 -2
  40. metadata +17 -31
@@ -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,
@@ -14,8 +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', '>= 0.16.2', '< 1.3.0'
18
- s.add_runtime_dependency 'rack', '2.0.8'
17
+ s.add_runtime_dependency 'grape', '~> 1.3'
19
18
 
20
19
  s.files = `git ls-files`.split("\n")
21
20
  s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
@@ -107,8 +107,8 @@ module SwaggerRouting
107
107
  end
108
108
 
109
109
  module SwaggerDocumentationAdder
110
- attr_accessor :combined_namespaces, :combined_namespace_identifiers
111
- attr_accessor :combined_routes, :combined_namespace_routes
110
+ attr_accessor :combined_namespaces, :combined_namespace_identifiers, :combined_routes, :combined_namespace_routes
111
+
112
112
  include SwaggerRouting
113
113
 
114
114
  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 = defaults.merge(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
- if formatter
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
- output_path_definitions.call(target_class.combined_namespace_routes, self)
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
- output_path_definitions.call({ params[:name] => combined_routes }, self)
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 tags_from(paths, options)
127
- tags = GrapeSwagger::DocMethods::TagNameDescription.build(paths)
135
+ def setup_formatter(formatter)
136
+ return unless formatter
128
137
 
129
- if options[:tags]
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)
@@ -16,7 +16,7 @@ module GrapeSwagger
16
16
  'object'
17
17
  when 'Rack::Multipart::UploadedFile', 'File'
18
18
  'file'
19
- when 'Virtus::Attribute::Boolean'
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.methods(false).include?(:entity_name)
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('::')[-2]
55
- elsif model.respond_to?(:name)
56
- model.name.demodulize.camelize
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('::').last
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!(/\-(\w)/, &:upcase).delete!('-') if operation[/\-(\w)/]
20
- operation.gsub!(/\_(\w)/, &:upcase).delete!('_') if operation.include?('_')
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
- @parsed_param[:in] = param_type || 'formData'
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 = partition_params(route, options).map do |param, value|
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,10 +199,7 @@ module Grape
196
199
  end
197
200
 
198
201
  def response_object(route, options)
199
- codes = http_codes_from_route(route)
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
204
  memo[value[:code]] = { description: value[:message] }
205
205
 
@@ -207,24 +207,31 @@ module Grape
207
207
 
208
208
  next build_file_response(memo[value[:code]]) if file_response?(value[:model])
209
209
 
210
- response_model = @item
211
- response_model = expose_params_from_model(value[:model]) if value[:model]
212
-
213
210
  if memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
214
211
  memo[204] = memo.delete(200)
215
212
  value[:code] = 204
213
+ next
216
214
  end
217
215
 
218
- next if value[:code] == 204
219
- next unless !response_model.start_with?('Swagger_doc') && (@definitions[response_model] || value[:model])
216
+ # Explicitly request no model with { model: '' }
217
+ next if value[:model] == ''
220
218
 
221
- @definitions[response_model][:description] = description_object(route)
219
+ response_model = value[:model] ? expose_params_from_model(value[:model]) : @item
220
+ next unless @definitions[response_model]
221
+ next if response_model.start_with?('Swagger_doc')
222
222
 
223
+ @definitions[response_model][:description] ||= "#{response_model} model"
223
224
  memo[value[:code]][:schema] = build_reference(route, value, response_model, options)
224
225
  memo[value[:code]][:examples] = value[:examples] if value[:examples]
225
226
  end
226
227
  end
227
228
 
229
+ def codes(route)
230
+ http_codes_from_route(route).map do |x|
231
+ x.is_a?(Array) ? { code: x[0], message: x[1], model: x[2], examples: x[3], headers: x[4] } : x
232
+ end
233
+ end
234
+
228
235
  def success_code?(code)
229
236
  status = code.is_a?(Array) ? code.first : code[:code]
230
237
  status.between?(200, 299)
@@ -250,7 +257,7 @@ module Grape
250
257
 
251
258
  def tag_object(route, path)
252
259
  version = GrapeSwagger::DocMethods::Version.get(route)
253
- version = [version] unless version.is_a?(Array)
260
+ version = Array(version)
254
261
  prefix = route.prefix.to_s.split('/').reject(&:empty?)
255
262
  Array(
256
263
  path.split('{')[0].split('/').reject(&:empty?).delete_if do |i|
@@ -293,16 +300,13 @@ module Grape
293
300
  memo['schema'] = { type: 'file' }
294
301
  end
295
302
 
296
- def partition_params(route, settings)
297
- declared_params = route.settings[:declared_params] if route.settings[:declared_params].present?
303
+ def build_request_params(route, settings)
298
304
  required = merge_params(route)
299
305
  required = GrapeSwagger::DocMethods::Headers.parse(route) + required unless route.headers.nil?
300
306
 
301
307
  default_type(required)
302
308
 
303
- request_params = unless declared_params.nil? && route.headers.nil?
304
- GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings)
305
- end || {}
309
+ request_params = GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
306
310
 
307
311
  request_params.empty? ? required : request_params
308
312
  end
@@ -340,13 +344,10 @@ module Grape
340
344
  parser = GrapeSwagger.model_parsers.find(model)
341
345
  raise GrapeSwagger::Errors::UnregisteredParser, "No parser registered for #{model_name}." unless parser
342
346
 
343
- properties, required = parser.new(model, self).call
344
- unless properties&.any?
345
- raise GrapeSwagger::Errors::SwaggerSpec,
346
- "Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
347
- end
347
+ parsed_response = parser.new(model, self).call
348
348
 
349
- @definitions[model_name] = GrapeSwagger::DocMethods::BuildModelDefinition.build(model, properties, required)
349
+ @definitions[model_name] =
350
+ GrapeSwagger::DocMethods::BuildModelDefinition.parse_params_from_model(parsed_response, model, model_name)
350
351
 
351
352
  model_name
352
353
  end
@@ -363,6 +364,16 @@ module Grape
363
364
  options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
364
365
  end
365
366
 
367
+ def hidden_parameter?(value)
368
+ return false if value[:required]
369
+
370
+ if value.dig(:documentation, :hidden).is_a?(Proc)
371
+ value.dig(:documentation, :hidden).call
372
+ else
373
+ value.dig(:documentation, :hidden)
374
+ end
375
+ end
376
+
366
377
  def success_code_from_entity(route, entity)
367
378
  default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
368
379
  if entity.is_a?(Hash)