jsapi 1.4.1 → 2.0
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/lib/jsapi/controller/actions/class_methods.rb +61 -0
- data/lib/jsapi/controller/actions.rb +13 -0
- data/lib/jsapi/controller/authentication/class_methods.rb +65 -0
- data/lib/jsapi/controller/authentication/credentials/api_key.rb +24 -0
- data/lib/jsapi/controller/authentication/credentials/http/base.rb +25 -0
- data/lib/jsapi/controller/authentication/credentials/http/basic.rb +34 -0
- data/lib/jsapi/controller/authentication/credentials/http/bearer.rb +30 -0
- data/lib/jsapi/controller/authentication/credentials/http.rb +5 -0
- data/lib/jsapi/controller/authentication/credentials.rb +38 -0
- data/lib/jsapi/controller/authentication.rb +70 -0
- data/lib/jsapi/controller/base.rb +5 -4
- data/lib/jsapi/controller/methods/callbacks/callback.rb +80 -0
- data/lib/jsapi/controller/methods/callbacks/class_methods.rb +62 -0
- data/lib/jsapi/controller/methods/callbacks.rb +54 -0
- data/lib/jsapi/controller/methods.rb +188 -71
- data/lib/jsapi/controller/parameters.rb +24 -20
- data/lib/jsapi/controller/response.rb +71 -39
- data/lib/jsapi/controller.rb +2 -1
- data/lib/jsapi/dsl/base.rb +38 -5
- data/lib/jsapi/dsl/class_methods.rb +2 -2
- data/lib/jsapi/dsl/definitions.rb +41 -27
- data/lib/jsapi/dsl/operation.rb +10 -109
- data/lib/jsapi/dsl/parameter.rb +1 -1
- data/lib/jsapi/dsl/path.rb +41 -18
- data/lib/jsapi/dsl/request_body.rb +1 -1
- data/lib/jsapi/dsl/response.rb +9 -6
- data/lib/jsapi/dsl/schema.rb +11 -5
- data/lib/jsapi/dsl/shared_operation_methods.rb +140 -0
- data/lib/jsapi/dsl.rb +1 -2
- data/lib/jsapi/json/array.rb +2 -2
- data/lib/jsapi/json/object.rb +6 -6
- data/lib/jsapi/json.rb +4 -6
- data/lib/jsapi/media/range.rb +39 -6
- data/lib/jsapi/media/type.rb +8 -4
- data/lib/jsapi/media.rb +5 -0
- data/lib/jsapi/messages.rb +19 -0
- data/lib/jsapi/meta/callback/base.rb +63 -8
- data/lib/jsapi/meta/content.rb +59 -0
- data/lib/jsapi/meta/definitions.rb +291 -141
- data/lib/jsapi/meta/example/base.rb +41 -8
- data/lib/jsapi/meta/existence.rb +1 -1
- data/lib/jsapi/meta/header/base.rb +4 -2
- data/lib/jsapi/meta/info.rb +3 -1
- data/lib/jsapi/meta/license.rb +11 -5
- data/lib/jsapi/meta/model/attributes/class_methods.rb +150 -0
- data/lib/jsapi/meta/model/attributes/frozen_error.rb +16 -0
- data/lib/jsapi/meta/model/attributes/type_caster.rb +56 -0
- data/lib/jsapi/meta/model/attributes.rb +24 -118
- data/lib/jsapi/meta/model/base.rb +2 -5
- data/lib/jsapi/meta/model/reference.rb +46 -10
- data/lib/jsapi/meta/model/wrappable.rb +23 -0
- data/lib/jsapi/meta/model/wrapper.rb +26 -0
- data/lib/jsapi/meta/model.rb +2 -1
- data/lib/jsapi/meta/oauth_flow.rb +1 -1
- data/lib/jsapi/meta/openapi/extensions.rb +5 -6
- data/lib/jsapi/meta/openapi/version.rb +2 -2
- data/lib/jsapi/meta/operation.rb +177 -71
- data/lib/jsapi/meta/parameter/base.rb +9 -5
- data/lib/jsapi/meta/parameter/wrapper.rb +13 -0
- data/lib/jsapi/meta/parameter.rb +3 -0
- data/lib/jsapi/meta/path.rb +59 -13
- data/lib/jsapi/meta/pathname.rb +3 -0
- data/lib/jsapi/meta/property.rb +10 -0
- data/lib/jsapi/meta/request_body/base.rb +69 -32
- data/lib/jsapi/meta/request_body/wrapper.rb +13 -0
- data/lib/jsapi/meta/request_body.rb +3 -0
- data/lib/jsapi/meta/rescue_handler.rb +18 -17
- data/lib/jsapi/meta/response/base.rb +86 -49
- data/lib/jsapi/meta/response/reference.rb +11 -1
- data/lib/jsapi/meta/response/wrapper.rb +26 -0
- data/lib/jsapi/meta/response.rb +3 -0
- data/lib/jsapi/meta/schema/additional_properties.rb +8 -0
- data/lib/jsapi/meta/schema/array.rb +20 -8
- data/lib/jsapi/meta/schema/base.rb +10 -9
- data/lib/jsapi/meta/schema/numeric.rb +26 -20
- data/lib/jsapi/meta/schema/object.rb +60 -44
- data/lib/jsapi/meta/schema/reference.rb +1 -8
- data/lib/jsapi/meta/schema/string.rb +12 -6
- data/lib/jsapi/meta/schema/wrapper.rb +31 -0
- data/lib/jsapi/meta/schema.rb +22 -9
- data/lib/jsapi/meta/security_requirement.rb +2 -2
- data/lib/jsapi/meta/security_scheme/api_key.rb +5 -2
- data/lib/jsapi/meta/security_scheme/base.rb +7 -5
- data/lib/jsapi/meta/security_scheme/http/basic.rb +5 -7
- data/lib/jsapi/meta/security_scheme/http/bearer.rb +5 -5
- data/lib/jsapi/meta/security_scheme/http/other.rb +1 -3
- data/lib/jsapi/meta/security_scheme/mutual_tls.rb +1 -3
- data/lib/jsapi/meta/security_scheme/oauth2.rb +18 -13
- data/lib/jsapi/meta/security_scheme/open_id_connect.rb +4 -4
- data/lib/jsapi/meta/security_scheme.rb +4 -4
- data/lib/jsapi/meta/server.rb +4 -2
- data/lib/jsapi/meta/tag.rb +9 -3
- data/lib/jsapi/meta.rb +2 -1
- data/lib/jsapi/model/base.rb +1 -1
- data/lib/jsapi/status/base.rb +35 -0
- data/lib/jsapi/status/code.rb +113 -0
- data/lib/jsapi/status/default.rb +16 -0
- data/lib/jsapi/status/range.rb +35 -0
- data/lib/jsapi/status.rb +37 -0
- data/lib/jsapi/version.rb +1 -1
- data/lib/jsapi.rb +2 -3
- metadata +32 -10
- data/lib/jsapi/controller/parameters_invalid.rb +0 -27
- data/lib/jsapi/dsl/callback.rb +0 -21
- data/lib/jsapi/dsl/error.rb +0 -36
- data/lib/jsapi/invalid_argument_error.rb +0 -12
- data/lib/jsapi/invalid_value_error.rb +0 -12
- data/lib/jsapi/invalid_value_helper.rb +0 -17
- data/lib/jsapi/meta/model/type_caster.rb +0 -50
- data/lib/jsapi/meta/schema/delegator.rb +0 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bbb6b0f27ef5b970e527367f2e918502d935765e4201ddbf5376fa4566ed5fde
|
|
4
|
+
data.tar.gz: d4764ceb8c1e32d0368a14d2d09370696f0329eb69f745ccdb9da67e5dcd0b1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d996c4830b339e80c9de07bf379d239734de9f0df4d9f1cd0072ae358c755a07aeee5a54bbbbe6cb2dae4452e3fba0c8691d6a641a74ac8ef7014b00b64d51dd
|
|
7
|
+
data.tar.gz: ea2bf03ff17664bcb434f160e13e17e5d2eb341fb7c17ceddf6667d4fbcd6db297eee0c93c66a8d530a2da1032bf33b983385c05e88bd9a431a68b0062a4b08e
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Controller
|
|
5
|
+
module Actions
|
|
6
|
+
module ClassMethods
|
|
7
|
+
##
|
|
8
|
+
# :method: api_action
|
|
9
|
+
# :call-seq:
|
|
10
|
+
# api_action(name, operation_name = nil, action: :index, **options)
|
|
11
|
+
# api_action(operation_name = nil, action: :index, **options, &block)
|
|
12
|
+
#
|
|
13
|
+
# Defines a controller action that performs an API operation by wrapping
|
|
14
|
+
# the given method or block by +api_operation+.
|
|
15
|
+
#
|
|
16
|
+
# # Invoke :foo to perform :bar
|
|
17
|
+
# api_action :foo, :bar
|
|
18
|
+
#
|
|
19
|
+
# # Call the given block to perform :bar
|
|
20
|
+
# api_action :bar do |api_params|
|
|
21
|
+
# # ...
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# Raises an +ArgumentError+ when neither +name+ nor +block+ is given.
|
|
25
|
+
#
|
|
26
|
+
# +:action+ specifies the name of the controller action to be defined.
|
|
27
|
+
# The default is +:index+.
|
|
28
|
+
#
|
|
29
|
+
# All other options are passed to +api_operation+.
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# :method: api_action!
|
|
33
|
+
# :call-seq:
|
|
34
|
+
# api_action!(name, operation_name = nil, action: :index, **options)
|
|
35
|
+
# api_action!(operation_name = nil, action: :index, **options, &block)
|
|
36
|
+
#
|
|
37
|
+
# Like +api_action+, except that +api_operation!+ is used instead of
|
|
38
|
+
# +api_operation+.
|
|
39
|
+
|
|
40
|
+
['', '!'].each do |suffix|
|
|
41
|
+
method = :"api_operation#{suffix}"
|
|
42
|
+
|
|
43
|
+
define_method(:"api_action#{suffix}") \
|
|
44
|
+
do |name = nil, operation_name = nil, action: nil, **options, &block|
|
|
45
|
+
raise ArgumentError, 'neither name nor block is given' if !name && !block
|
|
46
|
+
|
|
47
|
+
operation_name = name if operation_name.nil?
|
|
48
|
+
|
|
49
|
+
define_method(action || :index, &(
|
|
50
|
+
if block
|
|
51
|
+
-> { send(method, operation_name, **options, &block) }
|
|
52
|
+
else
|
|
53
|
+
-> { send(method, operation_name, **options) { |p| send(name, p) } }
|
|
54
|
+
end
|
|
55
|
+
))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Controller
|
|
5
|
+
module Authentication
|
|
6
|
+
module ClassMethods
|
|
7
|
+
# :call-seq:
|
|
8
|
+
# api_after_authentication(method_or_proc, **options)
|
|
9
|
+
# api_after_authentication(**options, &block)
|
|
10
|
+
#
|
|
11
|
+
# Registers a callback triggered after a request has been authenticated
|
|
12
|
+
# successfully.
|
|
13
|
+
def api_after_authentication(method_or_proc = nil, **options, &block)
|
|
14
|
+
_api_add_callback(:after_authentication, method_or_proc, **options, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# :call-seq:
|
|
18
|
+
# api_authenticate(*scheme_names, with:)
|
|
19
|
+
# api_authenticate(*scheme_names, &block)
|
|
20
|
+
#
|
|
21
|
+
# Registers a handler to authenticate requests according to the specified
|
|
22
|
+
# security schemes.
|
|
23
|
+
#
|
|
24
|
+
# api_authenticate 'basic_auth', with: :authenticate
|
|
25
|
+
#
|
|
26
|
+
# If no scheme names are specified, the handler is used as fallback for all
|
|
27
|
+
# security schemes for which no handler is registered.
|
|
28
|
+
#
|
|
29
|
+
# The +:with+ option specifies the method to be called to authenticate
|
|
30
|
+
# requests. Alternatively, a block can be given as handler.
|
|
31
|
+
#
|
|
32
|
+
# If the handler returns a truthy value, the request is assumed to be
|
|
33
|
+
# authenticated successfully.
|
|
34
|
+
#
|
|
35
|
+
# def authenticate(credentials)
|
|
36
|
+
# credentials.username == 'api_user' &&
|
|
37
|
+
# credentials.password == 'secret'
|
|
38
|
+
# end
|
|
39
|
+
def api_authenticate(*scheme_names, with: nil, &block)
|
|
40
|
+
handler = with || block
|
|
41
|
+
raise ArgumentError, 'either the :with keyword argument or a block ' \
|
|
42
|
+
'must be specified' unless handler
|
|
43
|
+
|
|
44
|
+
(scheme_names.presence || [nil]).each do |scheme_name|
|
|
45
|
+
_api_authentication_handlers[scheme_name&.to_s] = handler
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def _api_authentication_handler(scheme_name) # :nodoc:
|
|
50
|
+
scheme_name = scheme_name&.to_s
|
|
51
|
+
|
|
52
|
+
_api_authentication_handlers[scheme_name] ||
|
|
53
|
+
superclass.try(:_api_authentication_handler, scheme_name) ||
|
|
54
|
+
(_api_authentication_handler(nil) unless scheme_name.nil?)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def _api_authentication_handlers
|
|
60
|
+
@_api_authentication_handlers ||= {}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Controller
|
|
5
|
+
module Authentication
|
|
6
|
+
module Credentials
|
|
7
|
+
# Holds an API key.
|
|
8
|
+
class APIKey
|
|
9
|
+
# The API key as it was passed.
|
|
10
|
+
attr_reader :api_key
|
|
11
|
+
|
|
12
|
+
def initialize(api_key)
|
|
13
|
+
@api_key = api_key
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns true if the API Key is not nil.
|
|
17
|
+
def well_formed?
|
|
18
|
+
!api_key.nil?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Controller
|
|
5
|
+
module Authentication
|
|
6
|
+
module Credentials
|
|
7
|
+
module HTTP
|
|
8
|
+
# Holds credentials passed according to \HTTP \Authentication.
|
|
9
|
+
class Base
|
|
10
|
+
attr_reader :auth_scheme, :auth_param
|
|
11
|
+
|
|
12
|
+
def initialize(authorization)
|
|
13
|
+
@auth_scheme, @auth_param = authorization.to_s.split(' ', 2)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns true if the credentials are syntactically correct.
|
|
17
|
+
def well_formed?
|
|
18
|
+
auth_scheme.present? && auth_param.present?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Jsapi
|
|
6
|
+
module Controller
|
|
7
|
+
module Authentication
|
|
8
|
+
module Credentials
|
|
9
|
+
module HTTP
|
|
10
|
+
# Holds a username and password passed according to \HTTP
|
|
11
|
+
# \Basic \Authentication.
|
|
12
|
+
class Basic < Base
|
|
13
|
+
# The decoded password.
|
|
14
|
+
attr_reader :password
|
|
15
|
+
|
|
16
|
+
# The decoded username.
|
|
17
|
+
attr_reader :username
|
|
18
|
+
|
|
19
|
+
def initialize(authorization)
|
|
20
|
+
super
|
|
21
|
+
@username, @password =
|
|
22
|
+
Base64.decode64(auth_param).split(':', 2) \
|
|
23
|
+
if auth_scheme == 'Basic' && auth_param.present?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def well_formed? # :nodoc:
|
|
27
|
+
!username.nil? && !password.nil?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Jsapi
|
|
6
|
+
module Controller
|
|
7
|
+
module Authentication
|
|
8
|
+
module Credentials
|
|
9
|
+
module HTTP
|
|
10
|
+
# Holds a token passed according to \HTTP \Bearer \Authentication.
|
|
11
|
+
class Bearer < Base
|
|
12
|
+
# The decoded token.
|
|
13
|
+
attr_reader :token
|
|
14
|
+
|
|
15
|
+
def initialize(authorization)
|
|
16
|
+
super
|
|
17
|
+
@token =
|
|
18
|
+
Base64.decode64(auth_param) \
|
|
19
|
+
if auth_scheme == 'Bearer' && auth_param.present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def well_formed? # :nodoc:
|
|
23
|
+
!token.nil?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'credentials/api_key'
|
|
4
|
+
require_relative 'credentials/http'
|
|
5
|
+
|
|
6
|
+
module Jsapi
|
|
7
|
+
module Controller
|
|
8
|
+
module Authentication
|
|
9
|
+
# Provides classes to pass credentials to an authentication handler.
|
|
10
|
+
module Credentials
|
|
11
|
+
class << self
|
|
12
|
+
# Creates credentials from +request+ according to the specified
|
|
13
|
+
# security scheme.
|
|
14
|
+
def create(request, security_scheme)
|
|
15
|
+
case security_scheme
|
|
16
|
+
when Meta::SecurityScheme::APIKey
|
|
17
|
+
name = security_scheme.name
|
|
18
|
+
APIKey.new(
|
|
19
|
+
case security_scheme.in
|
|
20
|
+
when 'header'
|
|
21
|
+
request.headers[name]
|
|
22
|
+
when 'query'
|
|
23
|
+
request.query_parameters[name]
|
|
24
|
+
end
|
|
25
|
+
)
|
|
26
|
+
when Meta::SecurityScheme::HTTP::Basic
|
|
27
|
+
HTTP::Basic.new(request.authorization)
|
|
28
|
+
when Meta::SecurityScheme::HTTP::Bearer
|
|
29
|
+
HTTP::Bearer.new(request.authorization)
|
|
30
|
+
when Meta::SecurityScheme::HTTP::Other
|
|
31
|
+
HTTP::Base.new(request.authorization)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'authentication/credentials'
|
|
4
|
+
require_relative 'authentication/class_methods'
|
|
5
|
+
|
|
6
|
+
module Jsapi
|
|
7
|
+
module Controller
|
|
8
|
+
# The \Authentication add-on.
|
|
9
|
+
#
|
|
10
|
+
# class FooController < Jsapi::Controller::Base
|
|
11
|
+
# include Jsapi::Controller::Authentication
|
|
12
|
+
#
|
|
13
|
+
# api_authenticate 'basic_auth' do |credentials|
|
|
14
|
+
# # Implement authentication handler here
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# api_security_scheme 'basic_auth', type: 'http', scheme: 'basic'
|
|
18
|
+
#
|
|
19
|
+
# api_security_requirement do
|
|
20
|
+
# scheme 'basic_auth'
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
module Authentication
|
|
24
|
+
def self.included(base) # :nodoc:
|
|
25
|
+
base.extend(ClassMethods)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns true if and only if the current request satisfies at least one of
|
|
29
|
+
# the security requirements applied to the specified operation. A security
|
|
30
|
+
# requirement is satisfied if it is empty ({}) or all of the referred
|
|
31
|
+
# security schemes are satisfied.
|
|
32
|
+
#
|
|
33
|
+
# +operation_name+ can be omitted if the controller handles one operation only.
|
|
34
|
+
# If the operation isn't defined, an OperationNotDefined exception is raised.
|
|
35
|
+
def api_authenticated?(operation_name = nil)
|
|
36
|
+
_api_authenticated?(_api_operation(operation_name))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def _api_authenticated?(operation)
|
|
42
|
+
security_requirements = operation.security_requirements
|
|
43
|
+
return true if security_requirements.blank?
|
|
44
|
+
|
|
45
|
+
security_requirements.any? do |security_requirement|
|
|
46
|
+
security_requirement.schemes.keys.all? do |scheme_name|
|
|
47
|
+
# Find the most appropriate authentication handler
|
|
48
|
+
authentication_handler = self.class._api_authentication_handler(scheme_name)
|
|
49
|
+
next false unless authentication_handler
|
|
50
|
+
|
|
51
|
+
# Find the appropriate security scheme
|
|
52
|
+
security_scheme = api_definitions.find_security_scheme(scheme_name)
|
|
53
|
+
next false unless security_scheme
|
|
54
|
+
|
|
55
|
+
# Create the credentials to be passed to the authenticator
|
|
56
|
+
credentials = Credentials.create(request, security_scheme)
|
|
57
|
+
next false unless credentials&.well_formed?
|
|
58
|
+
|
|
59
|
+
# Call authentication handler
|
|
60
|
+
if authentication_handler.is_a?(Proc)
|
|
61
|
+
instance_exec(credentials, &authentication_handler)
|
|
62
|
+
else
|
|
63
|
+
send(authentication_handler, credentials)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -4,16 +4,17 @@ module Jsapi
|
|
|
4
4
|
module Controller
|
|
5
5
|
# The base API controller class.
|
|
6
6
|
#
|
|
7
|
+
# A minimal API controller responding with "Hello world" looks like:
|
|
8
|
+
#
|
|
7
9
|
# class FooController < Jsapi::Controller::Base
|
|
8
10
|
# api_operation do
|
|
9
11
|
# response type: 'string'
|
|
10
12
|
# end
|
|
11
13
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# end
|
|
15
|
-
# end
|
|
14
|
+
# api_action { 'Hello world' }
|
|
15
|
+
#
|
|
16
16
|
class Base < ActionController::API
|
|
17
|
+
include Actions
|
|
17
18
|
include DSL
|
|
18
19
|
include Methods
|
|
19
20
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Controller
|
|
5
|
+
module Methods
|
|
6
|
+
module Callbacks
|
|
7
|
+
# Represents a callback to be triggered.
|
|
8
|
+
class Callback
|
|
9
|
+
# The method or +Proc+ to be called.
|
|
10
|
+
attr_reader :method_or_proc
|
|
11
|
+
|
|
12
|
+
# Creates a callback that executes +method_or_proc+.
|
|
13
|
+
#
|
|
14
|
+
# The following options can be specified:
|
|
15
|
+
#
|
|
16
|
+
# - +:if+ - The conditions under which the callback is triggered only.
|
|
17
|
+
# - +:unless+ - The conditions under which the callback isn't triggered.
|
|
18
|
+
# - +:only+ - The operations on which the callback is triggered only.
|
|
19
|
+
# - +:except+ - The operations on which the callback isn't triggered.
|
|
20
|
+
#
|
|
21
|
+
# +:if+ and +:unless+ can be a symbol, a +Proc+ or an array of symbols
|
|
22
|
+
# and +Proc+s.
|
|
23
|
+
def initialize(method_or_proc, **options)
|
|
24
|
+
@method_or_proc = method_or_proc
|
|
25
|
+
|
|
26
|
+
@if, @unless =
|
|
27
|
+
%i[if unless].map do |key|
|
|
28
|
+
value = options[key]
|
|
29
|
+
Array.wrap(value) if value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@only, @except =
|
|
33
|
+
%i[only except].map do |key|
|
|
34
|
+
value = options[key]
|
|
35
|
+
Array.wrap(value).map(&:to_s) if value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def inspect # :nodoc:
|
|
40
|
+
"#<#{self.class} #{@method_or_proc.inspect}#{
|
|
41
|
+
{
|
|
42
|
+
if: @if,
|
|
43
|
+
unless: @unless,
|
|
44
|
+
only: @only,
|
|
45
|
+
except: @except
|
|
46
|
+
}.filter_map do |key, value|
|
|
47
|
+
next if value.nil?
|
|
48
|
+
|
|
49
|
+
value = value.sole if value.one?
|
|
50
|
+
", #{key}: #{value.inspect}"
|
|
51
|
+
end.join
|
|
52
|
+
}>"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns true if and only if the callback must not be triggered on
|
|
56
|
+
# +controller+ and +operation_name+.
|
|
57
|
+
def skip_on?(controller, operation_name)
|
|
58
|
+
operation_name = operation_name.to_s
|
|
59
|
+
|
|
60
|
+
@only&.exclude?(operation_name) ||
|
|
61
|
+
@except&.include?(operation_name) ||
|
|
62
|
+
@if&.any? { |condition| !evaluate(condition, controller) } ||
|
|
63
|
+
@unless&.any? { |condition| evaluate(condition, controller) } ||
|
|
64
|
+
false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def evaluate(condition, context)
|
|
70
|
+
if condition.is_a?(Proc)
|
|
71
|
+
context.instance_exec(&condition)
|
|
72
|
+
else
|
|
73
|
+
context.send(condition)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Controller
|
|
5
|
+
module Methods
|
|
6
|
+
module Callbacks
|
|
7
|
+
module ClassMethods
|
|
8
|
+
# :call-seq:
|
|
9
|
+
# api_after_validation(method_or_proc, **options)
|
|
10
|
+
# api_after_validation(**options, &block)
|
|
11
|
+
#
|
|
12
|
+
# Registers a callback that is triggered by +api_operation!+ after the
|
|
13
|
+
# parameters have been validated successfully.
|
|
14
|
+
#
|
|
15
|
+
# api_after_validation do |api_params|
|
|
16
|
+
# # ...
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# When calling an +api_after_validation+ callback, the parameters are
|
|
20
|
+
# passed.
|
|
21
|
+
def api_after_validation(method_or_proc = nil, **options, &block)
|
|
22
|
+
_api_add_callback(:after_validation, method_or_proc, **options, &block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# :call-seq:
|
|
26
|
+
# api_before_rendering(method_or_proc, **options)
|
|
27
|
+
# api_before_rendering(**options, &block)
|
|
28
|
+
#
|
|
29
|
+
# Registers a callback that is triggered by +api_operation+ and
|
|
30
|
+
# +api_operation!+ before the response body is rendered.
|
|
31
|
+
#
|
|
32
|
+
# api_before_rendering do |result, api_params|
|
|
33
|
+
# { request_id: api_params.request_id, payload: result }
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# When calling an +api_before_rendering+ callback, the result and
|
|
37
|
+
# parameters are passed. The value returned by the callback replaces
|
|
38
|
+
# the result to be rendered.
|
|
39
|
+
def api_before_rendering(method_or_proc = nil, **options, &block)
|
|
40
|
+
_api_add_callback(:before_rendering, method_or_proc, **options, &block)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def _api_add_callback(kind, method_or_proc = nil, **options, &block) # :nodoc:
|
|
44
|
+
method_or_proc ||= block
|
|
45
|
+
raise ArgumentError, 'either a method or a block must be ' \
|
|
46
|
+
'specified' unless method_or_proc
|
|
47
|
+
|
|
48
|
+
((@_api_callbacks ||= {})[kind] ||= []) <<
|
|
49
|
+
Callback.new(method_or_proc, **options)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def _api_callbacks(kind) # :nodoc:
|
|
53
|
+
callbacks = @_api_callbacks&.fetch(kind, nil) || []
|
|
54
|
+
return callbacks unless superclass.respond_to?(:_api_callbacks)
|
|
55
|
+
|
|
56
|
+
superclass._api_callbacks(kind) + callbacks
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'callbacks/callback'
|
|
4
|
+
require_relative 'callbacks/class_methods'
|
|
5
|
+
|
|
6
|
+
module Jsapi
|
|
7
|
+
module Controller
|
|
8
|
+
module Methods
|
|
9
|
+
module Callbacks
|
|
10
|
+
def self.included(base) # :nodoc:
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def _api_callback(name, operation_name, *args)
|
|
17
|
+
operation_name = operation_name.to_s
|
|
18
|
+
|
|
19
|
+
self.class._api_callbacks(name).each do |callback|
|
|
20
|
+
next if callback.skip_on?(self, operation_name)
|
|
21
|
+
|
|
22
|
+
_api_exec(callback.method_or_proc, operation_name, *args)
|
|
23
|
+
end
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def _api_before_rendering(operation_name, result, *args)
|
|
28
|
+
operation_name = operation_name.to_s
|
|
29
|
+
args = [args, operation_name].flatten
|
|
30
|
+
|
|
31
|
+
self.class._api_callbacks(:before_rendering).reduce(result) do |memo, callback|
|
|
32
|
+
next memo if callback.skip_on?(self, operation_name)
|
|
33
|
+
|
|
34
|
+
_api_exec(callback.method_or_proc, memo, operation_name, *args)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def _api_exec(method_or_proc, *args)
|
|
39
|
+
if method_or_proc.is_a?(Proc)
|
|
40
|
+
arity = method_or_proc.arity
|
|
41
|
+
args = args.take(arity) if arity < args.count
|
|
42
|
+
|
|
43
|
+
instance_exec(*args, &method_or_proc)
|
|
44
|
+
else
|
|
45
|
+
arity = self.class.instance_method(method_or_proc).arity
|
|
46
|
+
args = args.take(arity) if arity < args.count
|
|
47
|
+
|
|
48
|
+
send(method_or_proc, *args)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|