grape-swagger 2.1.2 → 2.1.4
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/CHANGELOG.md +121 -106
- data/CLAUDE.md +88 -0
- data/README.md +150 -29
- data/RELEASING.md +8 -8
- data/grape-swagger.gemspec +1 -2
- data/lib/grape-swagger/doc_methods/build_model_definition.rb +1 -1
- data/lib/grape-swagger/doc_methods/format_data.rb +1 -1
- data/lib/grape-swagger/doc_methods/move_params.rb +1 -1
- data/lib/grape-swagger/doc_methods/parse_params.rb +4 -2
- data/lib/grape-swagger/doc_methods.rb +0 -1
- data/lib/grape-swagger/endpoint.rb +19 -45
- data/lib/grape-swagger/errors.rb +2 -0
- data/lib/grape-swagger/request_param_parser_registry.rb +48 -0
- data/lib/grape-swagger/{endpoint/params_parser.rb → request_param_parsers/body.rb} +21 -22
- data/lib/grape-swagger/request_param_parsers/headers.rb +33 -0
- data/lib/grape-swagger/request_param_parsers/route.rb +66 -0
- data/lib/grape-swagger/token_owner_resolver.rb +101 -0
- data/lib/grape-swagger/version.rb +1 -1
- data/lib/grape-swagger.rb +12 -5
- metadata +15 -21
- data/lib/grape-swagger/doc_methods/headers.rb +0 -20
|
@@ -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
|
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
|
-
|
|
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
|
-
|
|
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 = (
|
|
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,14 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: grape-swagger
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LeFnord
|
|
8
8
|
- Tim Vandecasteele
|
|
9
|
+
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
+
date: 2026-02-03 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: grape
|
|
@@ -19,7 +20,7 @@ dependencies:
|
|
|
19
20
|
version: '1.7'
|
|
20
21
|
- - "<"
|
|
21
22
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: '
|
|
23
|
+
version: '4.0'
|
|
23
24
|
type: :runtime
|
|
24
25
|
prerelease: false
|
|
25
26
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -29,21 +30,8 @@ dependencies:
|
|
|
29
30
|
version: '1.7'
|
|
30
31
|
- - "<"
|
|
31
32
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
33
|
-
|
|
34
|
-
name: rack-test
|
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '2'
|
|
40
|
-
type: :runtime
|
|
41
|
-
prerelease: false
|
|
42
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '2'
|
|
33
|
+
version: '4.0'
|
|
34
|
+
description:
|
|
47
35
|
email:
|
|
48
36
|
- pscholz.le@gmail.com
|
|
49
37
|
- tim.vandecasteele@gmail.com
|
|
@@ -52,6 +40,7 @@ extensions: []
|
|
|
52
40
|
extra_rdoc_files: []
|
|
53
41
|
files:
|
|
54
42
|
- CHANGELOG.md
|
|
43
|
+
- CLAUDE.md
|
|
55
44
|
- CONTRIBUTING.md
|
|
56
45
|
- LICENSE.txt
|
|
57
46
|
- README.md
|
|
@@ -64,7 +53,6 @@ files:
|
|
|
64
53
|
- lib/grape-swagger/doc_methods/data_type.rb
|
|
65
54
|
- lib/grape-swagger/doc_methods/extensions.rb
|
|
66
55
|
- lib/grape-swagger/doc_methods/format_data.rb
|
|
67
|
-
- lib/grape-swagger/doc_methods/headers.rb
|
|
68
56
|
- lib/grape-swagger/doc_methods/move_params.rb
|
|
69
57
|
- lib/grape-swagger/doc_methods/operation_id.rb
|
|
70
58
|
- lib/grape-swagger/doc_methods/optional_object.rb
|
|
@@ -75,17 +63,22 @@ files:
|
|
|
75
63
|
- lib/grape-swagger/doc_methods/tag_name_description.rb
|
|
76
64
|
- lib/grape-swagger/doc_methods/version.rb
|
|
77
65
|
- lib/grape-swagger/endpoint.rb
|
|
78
|
-
- lib/grape-swagger/endpoint/params_parser.rb
|
|
79
66
|
- lib/grape-swagger/errors.rb
|
|
80
67
|
- lib/grape-swagger/instance.rb
|
|
81
68
|
- lib/grape-swagger/model_parsers.rb
|
|
82
69
|
- lib/grape-swagger/rake/oapi_tasks.rb
|
|
70
|
+
- lib/grape-swagger/request_param_parser_registry.rb
|
|
71
|
+
- lib/grape-swagger/request_param_parsers/body.rb
|
|
72
|
+
- lib/grape-swagger/request_param_parsers/headers.rb
|
|
73
|
+
- lib/grape-swagger/request_param_parsers/route.rb
|
|
74
|
+
- lib/grape-swagger/token_owner_resolver.rb
|
|
83
75
|
- lib/grape-swagger/version.rb
|
|
84
76
|
homepage: https://github.com/ruby-grape/grape-swagger
|
|
85
77
|
licenses:
|
|
86
78
|
- MIT
|
|
87
79
|
metadata:
|
|
88
80
|
rubygems_mfa_required: 'true'
|
|
81
|
+
post_install_message:
|
|
89
82
|
rdoc_options: []
|
|
90
83
|
require_paths:
|
|
91
84
|
- lib
|
|
@@ -100,7 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
93
|
- !ruby/object:Gem::Version
|
|
101
94
|
version: '0'
|
|
102
95
|
requirements: []
|
|
103
|
-
rubygems_version: 3.
|
|
96
|
+
rubygems_version: 3.5.22
|
|
97
|
+
signing_key:
|
|
104
98
|
specification_version: 4
|
|
105
99
|
summary: Add auto generated documentation to your Grape API that can be displayed
|
|
106
100
|
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
|