grape-swagger 2.1.1 → 2.1.3

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.
@@ -1,27 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeSwagger
4
- module Endpoint
5
- class ParamsParser
6
- attr_reader :params, :settings, :endpoint
4
+ module RequestParamParsers
5
+ class Body
6
+ attr_reader :route, :params, :settings, :endpoint
7
7
 
8
- def self.parse_request_params(params, settings, endpoint)
9
- new(params, settings, endpoint).parse_request_params
8
+ def self.parse(route, params, settings, endpoint)
9
+ new(route, params, settings, endpoint).parse
10
10
  end
11
11
 
12
- def initialize(params, settings, endpoint)
12
+ def initialize(_route, params, settings, endpoint)
13
13
  @params = params
14
14
  @settings = settings
15
15
  @endpoint = endpoint
16
16
  end
17
17
 
18
- def parse_request_params
18
+ def parse
19
19
  public_params.each_with_object({}) do |(name, options), memo|
20
20
  name = name.to_s
21
- param_type = options[:type]
22
- param_type = param_type.to_s unless param_type.nil?
21
+ param_type = options[:type]&.to_s
23
22
 
24
- if param_type_is_array?(param_type)
23
+ if array_param?(param_type)
25
24
  options[:is_array] = true
26
25
  name += '[]' if array_use_braces?
27
26
  end
@@ -36,8 +35,8 @@ module GrapeSwagger
36
35
  @array_use_braces ||= settings[:array_use_braces] && !includes_body_param?
37
36
  end
38
37
 
39
- def param_type_is_array?(param_type)
40
- return false unless param_type
38
+ def array_param?(param_type)
39
+ return false if param_type.nil?
41
40
  return true if param_type == 'Array'
42
41
 
43
42
  param_types = param_type.match(/\[(.*)\]$/)
@@ -48,21 +47,14 @@ module GrapeSwagger
48
47
  end
49
48
 
50
49
  def public_params
51
- params.select { |param| public_parameter?(param) }
50
+ params.select { |_key, param| public_parameter?(param) }
52
51
  end
53
52
 
54
- def public_parameter?(param)
55
- param_options = param.last
53
+ def public_parameter?(param_options)
56
54
  return true unless param_options.key?(:documentation) && !param_options[:required]
57
55
 
58
56
  param_hidden = param_options[:documentation].fetch(:hidden, false)
59
- if param_hidden.is_a?(Proc)
60
- param_hidden = if settings[:token_owner]
61
- param_hidden.call(endpoint.send(settings[:token_owner].to_sym))
62
- else
63
- param_hidden.call
64
- end
65
- end
57
+ param_hidden = evaluate_hidden_proc(param_hidden) if param_hidden.is_a?(Proc)
66
58
  !param_hidden
67
59
  end
68
60
 
@@ -71,6 +63,13 @@ module GrapeSwagger
71
63
  options.dig(:documentation, :param_type) == 'body' || options.dig(:documentation, :in) == 'body'
72
64
  end
73
65
  end
66
+
67
+ def evaluate_hidden_proc(hidden_proc)
68
+ return hidden_proc.call unless settings[:token_owner]
69
+
70
+ token_owner = GrapeSwagger::TokenOwnerResolver.resolve(endpoint, settings[:token_owner])
71
+ GrapeSwagger::TokenOwnerResolver.evaluate_proc(hidden_proc, token_owner)
72
+ end
74
73
  end
75
74
  end
76
75
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module RequestParamParsers
5
+ class Headers
6
+ attr_reader :route
7
+
8
+ def self.parse(route, params, settings, endpoint)
9
+ new(route, params, settings, endpoint).parse
10
+ end
11
+
12
+ def initialize(route, _params, _settings, _endpoint)
13
+ @route = route
14
+ end
15
+
16
+ def parse
17
+ return {} unless route.headers
18
+
19
+ route.headers.each_with_object({}) do |(name, definition), accum|
20
+ # Extract the description from any key type (string or symbol)
21
+ description = definition[:description] || definition['description']
22
+ doc = { desc: description, in: 'header' }
23
+
24
+ header_attrs = definition.symbolize_keys.except(:description, 'description')
25
+ header_attrs[:type] = definition[:type].titleize if definition[:type]
26
+ header_attrs[:documentation] = doc
27
+
28
+ accum[name] = header_attrs
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module RequestParamParsers
5
+ class Route
6
+ DEFAULT_PARAM_TYPE = { required: true, type: 'Integer' }.freeze
7
+
8
+ attr_reader :route
9
+
10
+ def self.parse(route, params, settings, endpoint)
11
+ new(route, params, settings, endpoint).parse
12
+ end
13
+
14
+ def initialize(route, _params, _settings, _endpoint)
15
+ @route = route
16
+ end
17
+
18
+ def parse
19
+ stackable_values = route.app&.inheritable_setting&.namespace_stackable
20
+
21
+ path_params = build_path_params(stackable_values)
22
+
23
+ fulfill_params(path_params)
24
+ end
25
+
26
+ private
27
+
28
+ def build_path_params(stackable_values)
29
+ params = {}
30
+
31
+ while stackable_values.is_a?(Grape::Util::StackableValues)
32
+ params.merge!(fetch_inherited_params(stackable_values))
33
+ stackable_values = stackable_values.inherited_values
34
+ end
35
+
36
+ params
37
+ end
38
+
39
+ def fetch_inherited_params(stackable_values)
40
+ return {} unless stackable_values.new_values
41
+
42
+ namespaces = stackable_values.new_values[:namespace] || []
43
+
44
+ namespaces.each_with_object({}) do |namespace, params|
45
+ space = namespace.space.to_s.gsub(':', '')
46
+ params[space] = namespace.options || {}
47
+ end
48
+ end
49
+
50
+ def fulfill_params(path_params)
51
+ # Merge path params options into route params
52
+ route.params.each_with_object({}) do |(param, definition), accum|
53
+ # The route.params hash includes both parametrized params (with a string as a key)
54
+ # and well-defined params from body/query (with a symbol as a key).
55
+ # We avoid overriding well-defined params with parametrized ones.
56
+ key = param.is_a?(String) ? param.to_sym : param
57
+ next if param.is_a?(String) && accum.key?(key)
58
+
59
+ defined_options = definition.is_a?(Hash) ? definition : {}
60
+ value = (path_params[param] || {}).merge(defined_options)
61
+ accum[key] = value.empty? ? DEFAULT_PARAM_TYPE : value
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ class TokenOwnerResolver
5
+ class << self
6
+ SUPPORTED_ARITY_TYPES = %i[req opt rest keyreq key keyrest].freeze
7
+ UNRESOLVED = Object.new.freeze
8
+ private_constant :UNRESOLVED
9
+
10
+ def resolve(endpoint, method_name)
11
+ return if method_name.nil?
12
+
13
+ method_name = method_name.to_sym
14
+ return endpoint.public_send(method_name) if endpoint.respond_to?(method_name, true)
15
+
16
+ helper_value = resolve_from_helpers(endpoint, method_name)
17
+ return helper_value unless helper_value.equal?(UNRESOLVED)
18
+
19
+ raise Errors::TokenOwnerNotFound, "undefined method `#{method_name}` for #{endpoint.class}"
20
+ end
21
+
22
+ def evaluate_proc(callable, token_owner)
23
+ return callable.call unless accepts_argument?(callable)
24
+
25
+ callable.call(token_owner)
26
+ end
27
+
28
+ private
29
+
30
+ def resolve_from_helpers(endpoint, method_name)
31
+ helpers = gather_helpers(endpoint)
32
+ return UNRESOLVED if helpers.empty?
33
+
34
+ helpers.each do |helper|
35
+ resolved = resolve_from_helper(endpoint, helper, method_name)
36
+ return resolved unless resolved.equal?(UNRESOLVED)
37
+ end
38
+
39
+ UNRESOLVED
40
+ end
41
+
42
+ def gather_helpers(endpoint)
43
+ return [] if endpoint.nil?
44
+
45
+ stackable_helpers = fetch_stackable_helpers(endpoint)
46
+ normalize_helpers(stackable_helpers)
47
+ end
48
+
49
+ def fetch_stackable_helpers(endpoint)
50
+ return unless endpoint.respond_to?(:inheritable_setting)
51
+
52
+ setting = endpoint.inheritable_setting
53
+ return unless setting.respond_to?(:namespace_stackable)
54
+
55
+ namespace_stackable = setting.namespace_stackable
56
+ return unless namespace_stackable.respond_to?(:[])
57
+
58
+ namespace_stackable[:helpers]
59
+ rescue NameError
60
+ nil
61
+ end
62
+
63
+ def normalize_helpers(helpers)
64
+ case helpers
65
+ when nil, false
66
+ []
67
+ when Module
68
+ [helpers]
69
+ when Array
70
+ helpers.compact
71
+ else
72
+ if helpers.respond_to?(:key?) && helpers.respond_to?(:[]) && helpers.key?(:helpers)
73
+ normalize_helpers(helpers[:helpers])
74
+ elsif helpers.respond_to?(:to_a)
75
+ Array(helpers.to_a).flatten.compact
76
+ else
77
+ Array(helpers).compact
78
+ end
79
+ end
80
+ end
81
+
82
+ def resolve_from_helper(endpoint, helper, method_name)
83
+ return UNRESOLVED unless helper_method_defined?(helper, method_name)
84
+
85
+ helper.instance_method(method_name).bind(endpoint).call
86
+ rescue NameError
87
+ UNRESOLVED
88
+ end
89
+
90
+ def helper_method_defined?(helper, method_name)
91
+ helper.method_defined?(method_name) || helper.private_method_defined?(method_name)
92
+ end
93
+
94
+ def accepts_argument?(callable)
95
+ return false unless callable.respond_to?(:parameters)
96
+
97
+ callable.parameters.any? { |type, _| SUPPORTED_ARITY_TYPES.include?(type) }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeSwagger
4
- VERSION = '2.1.1'
4
+ VERSION = '2.1.3'
5
5
  end
data/lib/grape-swagger.rb CHANGED
@@ -10,22 +10,28 @@ require 'grape-swagger/errors'
10
10
 
11
11
  require 'grape-swagger/doc_methods'
12
12
  require 'grape-swagger/model_parsers'
13
+ require 'grape-swagger/request_param_parser_registry'
14
+ require 'grape-swagger/token_owner_resolver'
13
15
 
14
16
  module GrapeSwagger
15
17
  class << self
16
18
  def model_parsers
17
19
  @model_parsers ||= GrapeSwagger::ModelParsers.new
18
20
  end
21
+
22
+ def request_param_parsers
23
+ @request_param_parsers ||= GrapeSwagger::RequestParamParserRegistry.new
24
+ end
19
25
  end
20
26
  autoload :Rake, 'grape-swagger/rake/oapi_tasks'
21
27
 
22
28
  # Copied from https://github.com/ruby-grape/grape/blob/v2.2.0/lib/grape/formatter.rb
23
29
  FORMATTER_DEFAULTS = {
30
+ xml: Grape::Formatter::Xml,
31
+ serializable_hash: Grape::Formatter::SerializableHash,
24
32
  json: Grape::Formatter::Json,
25
33
  jsonapi: Grape::Formatter::Json,
26
- serializable_hash: Grape::Formatter::SerializableHash,
27
- txt: Grape::Formatter::Txt,
28
- xml: Grape::Formatter::Xml
34
+ txt: Grape::Formatter::Txt
29
35
  }.freeze
30
36
 
31
37
  # Copied from https://github.com/ruby-grape/grape/blob/v2.2.0/lib/grape/content_types.rb
@@ -179,12 +185,13 @@ module SwaggerDocumentationAdder
179
185
  endpoint = endpoints.shift
180
186
 
181
187
  endpoints.push(*endpoint.options[:app].endpoints) if endpoint.options[:app]
182
- ns = endpoint.namespace_stackable(:namespace).last
188
+ namespace_stackable = endpoint.inheritable_setting.namespace_stackable
189
+ ns = (namespace_stackable[:namespace] || []).last
183
190
  next unless ns
184
191
 
185
192
  # use the full namespace here (not the latest level only)
186
193
  # and strip leading slash
187
- mount_path = (endpoint.namespace_stackable(:mount_path) || []).join('/')
194
+ mount_path = (namespace_stackable[:mount_path] || []).join('/')
188
195
  full_namespace = (mount_path + endpoint.namespace).sub(/\/{2,}/, '/').sub(/^\//, '')
189
196
  combined_namespaces[full_namespace] = ns
190
197
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-swagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - LeFnord
8
8
  - Tim Vandecasteele
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2024-09-21 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: grape
@@ -20,7 +19,7 @@ dependencies:
20
19
  version: '1.7'
21
20
  - - "<"
22
21
  - !ruby/object:Gem::Version
23
- version: '3.0'
22
+ version: '4.0'
24
23
  type: :runtime
25
24
  prerelease: false
26
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,22 +29,7 @@ dependencies:
30
29
  version: '1.7'
31
30
  - - "<"
32
31
  - !ruby/object:Gem::Version
33
- version: '3.0'
34
- - !ruby/object:Gem::Dependency
35
- name: rack-test
36
- requirement: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '2'
41
- type: :runtime
42
- prerelease: false
43
- version_requirements: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2'
48
- description:
32
+ version: '4.0'
49
33
  email:
50
34
  - pscholz.le@gmail.com
51
35
  - tim.vandecasteele@gmail.com
@@ -66,7 +50,6 @@ files:
66
50
  - lib/grape-swagger/doc_methods/data_type.rb
67
51
  - lib/grape-swagger/doc_methods/extensions.rb
68
52
  - lib/grape-swagger/doc_methods/format_data.rb
69
- - lib/grape-swagger/doc_methods/headers.rb
70
53
  - lib/grape-swagger/doc_methods/move_params.rb
71
54
  - lib/grape-swagger/doc_methods/operation_id.rb
72
55
  - lib/grape-swagger/doc_methods/optional_object.rb
@@ -77,18 +60,21 @@ files:
77
60
  - lib/grape-swagger/doc_methods/tag_name_description.rb
78
61
  - lib/grape-swagger/doc_methods/version.rb
79
62
  - lib/grape-swagger/endpoint.rb
80
- - lib/grape-swagger/endpoint/params_parser.rb
81
63
  - lib/grape-swagger/errors.rb
82
64
  - lib/grape-swagger/instance.rb
83
65
  - lib/grape-swagger/model_parsers.rb
84
66
  - lib/grape-swagger/rake/oapi_tasks.rb
67
+ - lib/grape-swagger/request_param_parser_registry.rb
68
+ - lib/grape-swagger/request_param_parsers/body.rb
69
+ - lib/grape-swagger/request_param_parsers/headers.rb
70
+ - lib/grape-swagger/request_param_parsers/route.rb
71
+ - lib/grape-swagger/token_owner_resolver.rb
85
72
  - lib/grape-swagger/version.rb
86
73
  homepage: https://github.com/ruby-grape/grape-swagger
87
74
  licenses:
88
75
  - MIT
89
76
  metadata:
90
77
  rubygems_mfa_required: 'true'
91
- post_install_message:
92
78
  rdoc_options: []
93
79
  require_paths:
94
80
  - lib
@@ -96,15 +82,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
82
  requirements:
97
83
  - - ">="
98
84
  - !ruby/object:Gem::Version
99
- version: '3.0'
85
+ version: '3.1'
100
86
  required_rubygems_version: !ruby/object:Gem::Requirement
101
87
  requirements:
102
88
  - - ">="
103
89
  - !ruby/object:Gem::Version
104
90
  version: '0'
105
91
  requirements: []
106
- rubygems_version: 3.3.7
107
- signing_key:
92
+ rubygems_version: 3.6.8
108
93
  specification_version: 4
109
94
  summary: Add auto generated documentation to your Grape API that can be displayed
110
95
  with Swagger.
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GrapeSwagger
4
- module DocMethods
5
- class Headers
6
- class << self
7
- def parse(route)
8
- route.headers.to_a.map do |route_header|
9
- route_header.tap do |header|
10
- hash = header[1]
11
- description = hash.delete('description')
12
- hash[:documentation] = { desc: description, in: 'header' }
13
- hash[:type] = hash['type'].titleize if hash['type']
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end