Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ jobs:
- { ruby: '3.3', grape: '2.2.0' }
- { ruby: '3.4', grape: '2.2.0' }
- { ruby: 'head', grape: '2.2.0' }
# - { ruby: '3.1', grape: 'HEAD' }
# - { ruby: '3.2', grape: 'HEAD' }
# - { ruby: '3.3', grape: 'HEAD' }
# - { ruby: '3.4', grape: 'HEAD' }
- { ruby: '3.2', grape: 'HEAD' }
- { ruby: '3.3', grape: 'HEAD' }
- { ruby: '3.4', grape: 'HEAD' }
name: test (ruby=${{ matrix.entry.ruby }}, grape=${{ matrix.entry.grape }})
runs-on: ubuntu-latest
needs: ['rubocop']
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#### Fixes

* [#966](https://github.com/ruby-grape/grape-swagger/pull/966): Grape 3.0 compatibility - [@numbata](https://github.com/numbata).
* [#954](https://github.com/ruby-grape/grape-swagger/pull/954): Ruby 3.5 compatibility: add cgi gem, drop runtime `rack‑test` - [@numbata](https://github.com/numbata).
* [#948](https://github.com/ruby-grape/grape-swagger/pull/948): Grape 2.3.0 and Ruby 3.5 compatibility - [@numbata](https://github.com/numbata).
* Your contribution here.
Expand Down
5 changes: 3 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ source 'https://rubygems.org'
gemspec

# gem 'grape', git: 'https://github.com/ruby-grape/grape'
gem 'grape', case version = ENV.fetch('GRAPE_VERSION', '< 3.0')
gem 'grape', case version = ENV.fetch('GRAPE_VERSION', '< 4.0')
when 'HEAD'
{ git: 'https://github.com/ruby-grape/grape' }
else
Expand All @@ -20,10 +20,11 @@ group :development, :test do
gem 'pry', platforms: [:mri]
gem 'pry-byebug', platforms: [:mri]

grape_version = ENV.fetch('GRAPE_VERSION', '2.2.0')
grape_version = ENV.fetch('GRAPE_VERSION', '2.4.0')
if grape_version == 'HEAD' || Gem::Version.new(grape_version) >= Gem::Version.new('2.0.0')
gem 'rack', '>= 3.0'
else
gem 'activesupport', '< 7.2'
gem 'rack', '< 3.0'
end

Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,19 @@ This screenshot is based on the [Hussars](https://github.com/LeFnord/hussars) sa

The following versions of grape, grape-entity and grape-swagger can currently be used together.

| grape-swagger | swagger spec | grape | grape-entity | representable |
| ------------------ | ------------ | ----------------------- | ------------ | ------------- |
| 0.10.5 | 1.2 | >= 0.10.0 ... <= 0.14.0 | < 0.5.0 | n/a |
| 0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0 | n/a |
| 0.25.2 | 2.0 | >= 0.14.0 ... <= 0.18.0 | <= 0.6.0 | >= 2.4.1 |
| 0.26.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | <= 0.6.1 | >= 2.4.1 |
| 0.27.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | >= 0.5.0 | >= 2.4.1 |
| 0.32.0 | 2.0 | >= 0.16.2 | >= 0.5.0 | >= 2.4.1 |
| 0.34.0 | 2.0 | >= 0.16.2 ... < 1.3.0 | >= 0.5.0 | >= 2.4.1 |
| >= 1.0.0 | 2.0 | >= 1.3.0 | >= 0.5.0 | >= 2.4.1 |
| >= 2.0.0 | 2.0 | >= 1.7.0 | >= 0.5.0 | >= 2.4.1 |
| >= 2.0.0 ... < 2.2 | 2.0 | >= 1.8.0 ... < 2.3.0 | >= 0.5.0 | >= 2.4.1 |
| grape-swagger | swagger spec | grape | grape-entity | representable |
| --------------------- | ------------ | ----------------------- | ------------ | ------------- |
| 0.10.5 | 1.2 | >= 0.10.0 ... <= 0.14.0 | < 0.5.0 | n/a |
| 0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0 | n/a |
| 0.25.2 | 2.0 | >= 0.14.0 ... <= 0.18.0 | <= 0.6.0 | >= 2.4.1 |
| 0.26.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | <= 0.6.1 | >= 2.4.1 |
| 0.27.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | >= 0.5.0 | >= 2.4.1 |
| 0.32.0 | 2.0 | >= 0.16.2 | >= 0.5.0 | >= 2.4.1 |
| 0.34.0 | 2.0 | >= 0.16.2 ... < 1.3.0 | >= 0.5.0 | >= 2.4.1 |
| >= 1.0.0 | 2.0 | >= 1.3.0 | >= 0.5.0 | >= 2.4.1 |
| >= 2.0.0 | 2.0 | >= 1.7.0 | >= 0.5.0 | >= 2.4.1 |
| >= 2.0.0 ... <= 2.1.2 | 2.0 | >= 1.8.0 ... < 2.3.0 | >= 0.5.0 | >= 2.4.1 |
| > 2.1.2 | 2.0 | >= 1.8.0 ... < 4.0 | >= 0.5.0 | >= 2.4.1 |


## Swagger-Spec <a name="swagger-spec"></a>
Expand Down
2 changes: 1 addition & 1 deletion grape-swagger.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.metadata['rubygems_mfa_required'] = 'true'

s.required_ruby_version = '>= 3.1'
s.add_dependency 'grape', '>= 1.7', '< 3.0'
s.add_dependency 'grape', '>= 1.7', '< 4.0'

s.files = Dir['lib/**/*', '*.md', 'LICENSE.txt', 'grape-swagger.gemspec']
s.require_paths = ['lib']
Expand Down
6 changes: 4 additions & 2 deletions lib/grape-swagger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'grape-swagger/doc_methods'
require 'grape-swagger/model_parsers'
require 'grape-swagger/request_param_parser_registry'
require 'grape-swagger/token_owner_resolver'

module GrapeSwagger
class << self
Expand Down Expand Up @@ -184,12 +185,13 @@ def combine_namespaces(app)
endpoint = endpoints.shift

endpoints.push(*endpoint.options[:app].endpoints) if endpoint.options[:app]
ns = endpoint.namespace_stackable(:namespace).last
namespace_stackable = endpoint.inheritable_setting.namespace_stackable
ns = (namespace_stackable[:namespace] || []).last
next unless ns

# use the full namespace here (not the latest level only)
# and strip leading slash
mount_path = (endpoint.namespace_stackable(:mount_path) || []).join('/')
mount_path = (namespace_stackable[:mount_path] || []).join('/')
full_namespace = (mount_path + endpoint.namespace).sub(/\/{2,}/, '/').sub(/^\//, '')
combined_namespaces[full_namespace] = ns
end
Expand Down
6 changes: 5 additions & 1 deletion lib/grape-swagger/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require_relative 'request_param_parsers/headers'
require_relative 'request_param_parsers/route'
require_relative 'request_param_parsers/body'
require_relative 'token_owner_resolver'

module Grape
class Endpoint # rubocop:disable Metrics/ClassLength
Expand Down Expand Up @@ -439,7 +440,10 @@ def hidden?(route, options)
route_hidden = route.options[:hidden] if route.options.key?(:hidden)
return route_hidden unless route_hidden.is_a?(Proc)

options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
return route_hidden.call unless options[:token_owner]

token_owner = GrapeSwagger::TokenOwnerResolver.resolve(self, options[:token_owner])
GrapeSwagger::TokenOwnerResolver.evaluate_proc(route_hidden, token_owner)
end

def hidden_parameter?(value)
Expand Down
2 changes: 2 additions & 0 deletions lib/grape-swagger/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ def tell!(what)
end
end
end

class TokenOwnerNotFound < NoMethodError; end
end
end
15 changes: 8 additions & 7 deletions lib/grape-swagger/request_param_parsers/body.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,7 @@ def public_parameter?(param_options)
return true unless param_options.key?(:documentation) && !param_options[:required]

param_hidden = param_options[:documentation].fetch(:hidden, false)
if param_hidden.is_a?(Proc)
param_hidden = if settings[:token_owner]
param_hidden.call(endpoint.send(settings[:token_owner].to_sym))
else
param_hidden.call
end
end
param_hidden = evaluate_hidden_proc(param_hidden) if param_hidden.is_a?(Proc)
!param_hidden
end

Expand All @@ -69,6 +63,13 @@ def includes_body_param?
options.dig(:documentation, :param_type) == 'body' || options.dig(:documentation, :in) == 'body'
end
end

def evaluate_hidden_proc(hidden_proc)
return hidden_proc.call unless settings[:token_owner]

token_owner = GrapeSwagger::TokenOwnerResolver.resolve(endpoint, settings[:token_owner])
GrapeSwagger::TokenOwnerResolver.evaluate_proc(hidden_proc, token_owner)
end
end
end
end
101 changes: 101 additions & 0 deletions lib/grape-swagger/token_owner_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# frozen_string_literal: true

module GrapeSwagger
class TokenOwnerResolver
class << self
SUPPORTED_ARITY_TYPES = %i[req opt rest keyreq key keyrest].freeze
UNRESOLVED = Object.new.freeze
private_constant :UNRESOLVED

def resolve(endpoint, method_name)
return if method_name.nil?

method_name = method_name.to_sym
return endpoint.public_send(method_name) if endpoint.respond_to?(method_name, true)

helper_value = resolve_from_helpers(endpoint, method_name)
return helper_value unless helper_value.equal?(UNRESOLVED)

raise Errors::TokenOwnerNotFound, "undefined method `#{method_name}` for #{endpoint.class}"
end

def evaluate_proc(callable, token_owner)
return callable.call unless accepts_argument?(callable)

callable.call(token_owner)
end

private

def resolve_from_helpers(endpoint, method_name)
helpers = gather_helpers(endpoint)
return UNRESOLVED if helpers.empty?

helpers.each do |helper|
resolved = resolve_from_helper(endpoint, helper, method_name)
return resolved unless resolved.equal?(UNRESOLVED)
end

UNRESOLVED
end

def gather_helpers(endpoint)
return [] if endpoint.nil?

stackable_helpers = fetch_stackable_helpers(endpoint)
normalize_helpers(stackable_helpers)
end

def fetch_stackable_helpers(endpoint)
return unless endpoint.respond_to?(:inheritable_setting)

setting = endpoint.inheritable_setting
return unless setting.respond_to?(:namespace_stackable)

namespace_stackable = setting.namespace_stackable
return unless namespace_stackable.respond_to?(:[])

namespace_stackable[:helpers]
rescue NameError
nil
end

def normalize_helpers(helpers)
case helpers
when nil, false
[]
when Module
[helpers]
when Array
helpers.compact
else
if helpers.respond_to?(:key?) && helpers.respond_to?(:[]) && helpers.key?(:helpers)
normalize_helpers(helpers[:helpers])
elsif helpers.respond_to?(:to_a)
Array(helpers.to_a).flatten.compact
else
Array(helpers).compact
end
end
end

def resolve_from_helper(endpoint, helper, method_name)
return UNRESOLVED unless helper_method_defined?(helper, method_name)

helper.instance_method(method_name).bind(endpoint).call
rescue NameError
UNRESOLVED
end

def helper_method_defined?(helper, method_name)
helper.method_defined?(method_name) || helper.private_method_defined?(method_name)
end

def accepts_argument?(callable)
return false unless callable.respond_to?(:parameters)

callable.parameters.any? { |type, _| SUPPORTED_ARITY_TYPES.include?(type) }
end
end
end
end
Loading