haveapi 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +11 -0
- data/Rakefile +1 -0
- data/haveapi.gemspec +1 -1
- data/lib/haveapi/authentication/chain.rb +4 -0
- data/lib/haveapi/hooks.rb +68 -11
- data/lib/haveapi/model_adapters/active_record.rb +1 -1
- data/lib/haveapi/{params/param.rb → parameters/typed.rb} +4 -1
- data/lib/haveapi/params.rb +2 -2
- data/lib/haveapi/spec/api_builder.rb +75 -0
- data/lib/haveapi/spec/api_response.rb +41 -0
- data/lib/haveapi/spec/helpers.rb +5 -99
- data/lib/haveapi/spec/mock_action.rb +32 -0
- data/lib/haveapi/spec/spec_methods.rb +121 -0
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/main_layout.erb +3 -1
- data/lib/haveapi.rb +1 -1
- data/spec/action/dsl_spec.rb +199 -0
- data/spec/authorization_spec.rb +113 -0
- data/spec/common_spec.rb +47 -0
- data/spec/documentation_spec.rb +29 -0
- data/spec/envelope_spec.rb +33 -0
- data/spec/hooks_spec.rb +146 -0
- data/spec/parameters/typed_spec.rb +93 -0
- data/spec/params_spec.rb +190 -0
- data/spec/resource_spec.rb +63 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/validators/acceptance_spec.rb +29 -0
- data/spec/validators/confirmation_spec.rb +46 -0
- data/spec/validators/custom_spec.rb +6 -0
- data/spec/validators/exclusion_spec.rb +32 -0
- data/spec/validators/format_spec.rb +54 -0
- data/spec/validators/inclusion_spec.rb +43 -0
- data/spec/validators/length_spec.rb +45 -0
- data/spec/validators/numericality_spec.rb +70 -0
- data/spec/validators/presence_spec.rb +47 -0
- metadata +27 -4
- /data/lib/haveapi/{params → parameters}/resource.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00a5f09608d0287b5187d60b5e5f8528b8077c33
|
4
|
+
data.tar.gz: 728e896638ef103f0d62db8aea3d0a26cae3e818
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3519a72a83f31a146da97ffb2422439231afa6a00b55eb55f3ceb1f77d8766f1afb76fe60454bc403fccd7fe2689d2c76fe2970b2557b594258c28f834f478e
|
7
|
+
data.tar.gz: 5296734002aca4221526d0bbd98ff2927bb61b6b74781f46d2d79c5bf01d29d5d0a16bfe59aa0b313b9819fe823150324f247387a7ed310d6fe747e9f8cb4a02
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
* Tue Feb 9 2016 - version 0.5.0
|
2
|
+
- Helper methods for testing APIs
|
3
|
+
- Added tests covering the basic functionality
|
4
|
+
- Fixed overflow in table of contents in online API doc
|
5
|
+
- Authentication chain can be empty
|
6
|
+
- Fixed Params.optional
|
7
|
+
- Fixed coercion of Float input parameters
|
8
|
+
- Renamed Params::Param to Parameters::Typed and Params::Resource to
|
9
|
+
Parameters::Resource
|
10
|
+
- Instance-level hooks are stored in the object itself
|
11
|
+
|
1
12
|
* Sun Jan 24 2016 - version 0.4.2
|
2
13
|
- Inclusion validator: handle hash correctly, don't overwrite given values
|
3
14
|
|
data/Rakefile
CHANGED
data/haveapi.gemspec
CHANGED
@@ -5,7 +5,7 @@ require 'haveapi/version'
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'haveapi'
|
7
7
|
s.version = HaveAPI::VERSION
|
8
|
-
s.date = '2016-
|
8
|
+
s.date = '2016-02-09'
|
9
9
|
s.summary =
|
10
10
|
s.description = 'Framework for creating self-describing APIs'
|
11
11
|
s.authors = 'Jakub Skokan'
|
@@ -37,6 +37,8 @@ module HaveAPI::Authentication
|
|
37
37
|
# Authentication provider can deny the user access by calling Base#deny.
|
38
38
|
def authenticate(v, *args)
|
39
39
|
catch(:return) do
|
40
|
+
return unless @instances[v]
|
41
|
+
|
40
42
|
@instances[v].each do |provider|
|
41
43
|
u = provider.authenticate(*args)
|
42
44
|
return u if u
|
@@ -48,6 +50,8 @@ module HaveAPI::Authentication
|
|
48
50
|
|
49
51
|
def describe(context)
|
50
52
|
ret = {}
|
53
|
+
|
54
|
+
return ret unless @instances[context.version]
|
51
55
|
|
52
56
|
@instances[context.version].each do |provider|
|
53
57
|
ret[provider.name] = provider.describe
|
data/lib/haveapi/hooks.rb
CHANGED
@@ -7,14 +7,36 @@ module HaveAPI
|
|
7
7
|
# it is possible to connect to a specific instance and not for
|
8
8
|
# all instances of a class.
|
9
9
|
#
|
10
|
+
# Hook definition contains additional information for as a documentation:
|
11
|
+
# description, context, arguments, return value.
|
12
|
+
#
|
13
|
+
# Every hook can have multiple listeners. They are invoked in the order of
|
14
|
+
# registration. Instance-level listeners first, then class-level. Hooks are
|
15
|
+
# chained using the block's first argument and return value. The first block
|
16
|
+
# to be executed gets the initial value, may make changes and returns it.
|
17
|
+
# The next block gets the return value of the previous block as its first
|
18
|
+
# argument, may make changes and returns it. Return value of the last block
|
19
|
+
# is returned to the caller of the hook.
|
20
|
+
#
|
10
21
|
# === \Usage
|
11
22
|
# ==== \Register hooks
|
12
23
|
# class MyClass
|
13
24
|
# include Hookable
|
14
25
|
#
|
15
|
-
# has_hook :myhook
|
26
|
+
# has_hook :myhook,
|
27
|
+
# desc: 'Called when I want to',
|
28
|
+
# context: 'current',
|
29
|
+
# args: {
|
30
|
+
# a: 'integer',
|
31
|
+
# b: 'integer',
|
32
|
+
# c: 'integer',
|
33
|
+
# }
|
16
34
|
# end
|
17
35
|
#
|
36
|
+
# Not that the additional information is just optional. A list of defined
|
37
|
+
# hooks and their description is a part of the reference documentation
|
38
|
+
# generated by yard.
|
39
|
+
#
|
18
40
|
# ==== \Class level hooks
|
19
41
|
# # Connect hook
|
20
42
|
# MyClass.connect_hook(:myhook) do |ret, a, b, c|
|
@@ -43,7 +65,20 @@ module HaveAPI
|
|
43
65
|
# my.call_class_hooks_for(:myhook, args: [1, 2, 3])
|
44
66
|
# # Call both instance and class hooks at once
|
45
67
|
# my.call_hooks_for(:myhook, args: [1, 2, 3])
|
68
|
+
#
|
69
|
+
# ==== \Chaining
|
70
|
+
# 5.times do |i|
|
71
|
+
# MyClass.connect_hook(:myhook) do |ret, a, b, c|
|
72
|
+
# ret[:counter] += i
|
73
|
+
# ret
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# p MyClass.call_hooks(:myhook, args: [1, 2, 3], initial: {counter: 0})
|
78
|
+
# => {:counter=>5}
|
46
79
|
module Hooks
|
80
|
+
INSTANCE_VARIABLE = '@_haveapi_hooks'
|
81
|
+
|
47
82
|
# Register a hook defined by +klass+ with +name+.
|
48
83
|
# +klass+ is an instance of Class, that is class name, not it's instance.
|
49
84
|
# +opts+ is a hash and can have following keys:
|
@@ -72,16 +107,16 @@ module HaveAPI
|
|
72
107
|
end
|
73
108
|
|
74
109
|
# Connect instance hook from instance +klass+ with +name+ to +block+.
|
75
|
-
def self.connect_instance_hook(
|
76
|
-
|
77
|
-
@hooks[klass] = {}
|
110
|
+
def self.connect_instance_hook(instance, name, &block)
|
111
|
+
hooks = instance.instance_variable_get(INSTANCE_VARIABLE)
|
78
112
|
|
79
|
-
|
80
|
-
|
81
|
-
|
113
|
+
unless hooks
|
114
|
+
hooks = {}
|
115
|
+
instance.instance_variable_set(INSTANCE_VARIABLE, hooks)
|
82
116
|
end
|
83
117
|
|
84
|
-
|
118
|
+
hooks[name] ||= {listeners: []}
|
119
|
+
hooks[name][:listeners] << block
|
85
120
|
end
|
86
121
|
|
87
122
|
# Call all blocks that are connected to hook in +klass+ with +name+.
|
@@ -98,17 +133,39 @@ module HaveAPI
|
|
98
133
|
# A block may decide that no further blocks should be executed.
|
99
134
|
# In such a case it calls Hooks.stop with the return value. It is then
|
100
135
|
# returned to the caller immediately.
|
101
|
-
|
136
|
+
#
|
137
|
+
# @param klass [Class instance, instance]
|
138
|
+
# @param name [Symbol] hook name
|
139
|
+
# @param where [Class instance] class in whose context hooks are executed
|
140
|
+
# @param args [Array] an array of arguments passed to hooks
|
141
|
+
# @param initial [Hash] initial return value
|
142
|
+
# @param instance [Boolean] call instance hooks or not; nil means auto-detect
|
143
|
+
def self.call_for(
|
144
|
+
klass,
|
145
|
+
name,
|
146
|
+
where = nil,
|
147
|
+
args: [],
|
148
|
+
initial: {},
|
149
|
+
instance: nil
|
150
|
+
)
|
102
151
|
classified = hook_classify(klass)
|
103
152
|
|
153
|
+
if (instance.nil? && !classified.is_a?(Class)) || instance
|
154
|
+
all_hooks = klass.instance_variable_get(INSTANCE_VARIABLE)
|
155
|
+
|
156
|
+
else
|
157
|
+
all_hooks = @hooks[classified]
|
158
|
+
end
|
159
|
+
|
104
160
|
catch(:stop) do
|
105
|
-
return initial unless
|
106
|
-
hooks =
|
161
|
+
return initial unless all_hooks
|
162
|
+
hooks = all_hooks[name][:listeners]
|
107
163
|
return initial unless hooks
|
108
164
|
|
109
165
|
hooks.each do |hook|
|
110
166
|
if where
|
111
167
|
ret = where.instance_exec(initial, *args, &hook)
|
168
|
+
|
112
169
|
else
|
113
170
|
ret = hook.call(initial, *args)
|
114
171
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module HaveAPI::Parameters
|
2
|
-
class
|
2
|
+
class Typed
|
3
3
|
ATTRIBUTES = %i(label desc type db_name default fill clean)
|
4
4
|
|
5
5
|
attr_reader :name, :label, :desc, :type, :default
|
@@ -76,6 +76,9 @@ module HaveAPI::Parameters
|
|
76
76
|
|
77
77
|
elsif @type == Integer
|
78
78
|
raw.to_i
|
79
|
+
|
80
|
+
elsif @type == Float
|
81
|
+
raw.to_f
|
79
82
|
|
80
83
|
elsif @type == Boolean
|
81
84
|
Boolean.to_b(raw)
|
data/lib/haveapi/params.rb
CHANGED
@@ -83,7 +83,7 @@ module HaveAPI
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def optional(*args)
|
86
|
-
add_param(*apply(args, required:
|
86
|
+
add_param(*apply(args, required: false))
|
87
87
|
end
|
88
88
|
|
89
89
|
def string(*args)
|
@@ -268,7 +268,7 @@ module HaveAPI
|
|
268
268
|
|
269
269
|
private
|
270
270
|
def add_param(*args)
|
271
|
-
p = Parameters::
|
271
|
+
p = Parameters::Typed.new(*args)
|
272
272
|
|
273
273
|
return if @include && !@include.include?(p.name)
|
274
274
|
return if @exclude && @exclude.include?(p.name)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
# Contains methods for specification of API to be used in `description` block.
|
3
|
+
module ApiBuilder
|
4
|
+
# Uses an empty module as the source for the API. It will have no resources,
|
5
|
+
# no actions. Version default to `1`.
|
6
|
+
def empty_api
|
7
|
+
api(Module.new)
|
8
|
+
default_version(1)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set an API module or create the API using DSL.
|
12
|
+
# @param mod [Module] module name or nil
|
13
|
+
# @yield block is executed in a dynamically created module
|
14
|
+
def api(mod = nil, &block)
|
15
|
+
unless mod
|
16
|
+
mod = Module.new do
|
17
|
+
def self.define_resource(name, superclass: Resource, &block)
|
18
|
+
return false if const_defined?(name)
|
19
|
+
|
20
|
+
cls = Class.new(superclass)
|
21
|
+
const_set(name, cls)
|
22
|
+
cls.class_exec(&block) if block
|
23
|
+
cls
|
24
|
+
end
|
25
|
+
|
26
|
+
module_eval(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
const_set(:ApiModule, mod)
|
30
|
+
end
|
31
|
+
|
32
|
+
opt(:api_module, mod)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set authentication chain.
|
36
|
+
def auth_chain(chain)
|
37
|
+
opt(:auth_chain, chain)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Select API versions to be used.
|
41
|
+
def use_version(v)
|
42
|
+
before(:each) do
|
43
|
+
opts(:versions, v)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set default API version.
|
48
|
+
def default_version(v)
|
49
|
+
opt(:default_version, v)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set a custom mount path.
|
53
|
+
def mount_to(path)
|
54
|
+
opts(:mount, path)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Login using HTTP basic.
|
58
|
+
def login(*credentials)
|
59
|
+
before(:each) do
|
60
|
+
basic_authorize(*credentials)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @private
|
65
|
+
def opts
|
66
|
+
@opts
|
67
|
+
end
|
68
|
+
|
69
|
+
# @private
|
70
|
+
def opt(name, v)
|
71
|
+
@opts ||= {}
|
72
|
+
@opts[name] = v
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
# This class wraps raw reply from the API and provides a more friendly
|
3
|
+
# interface.
|
4
|
+
class ApiResponse
|
5
|
+
def initialize(body)
|
6
|
+
@data = JSON.parse(body, symbolize_names: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
def envelope
|
10
|
+
@data
|
11
|
+
end
|
12
|
+
|
13
|
+
def status
|
14
|
+
@data[:status]
|
15
|
+
end
|
16
|
+
|
17
|
+
def ok?
|
18
|
+
@data[:status]
|
19
|
+
end
|
20
|
+
|
21
|
+
def failed?
|
22
|
+
!ok?
|
23
|
+
end
|
24
|
+
|
25
|
+
def response
|
26
|
+
@data[:response]
|
27
|
+
end
|
28
|
+
|
29
|
+
def message
|
30
|
+
@data[:message]
|
31
|
+
end
|
32
|
+
|
33
|
+
def errors
|
34
|
+
@data[:errors]
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](k)
|
38
|
+
@data[:response][k]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/haveapi/spec/helpers.rb
CHANGED
@@ -1,103 +1,9 @@
|
|
1
1
|
require 'rack/test'
|
2
2
|
|
3
3
|
module HaveAPI
|
4
|
-
|
5
|
-
module ApiBuilder
|
6
|
-
def auth_chain(chain)
|
7
|
-
@auth_chain = chain
|
8
|
-
end
|
9
|
-
|
10
|
-
def use_version(v)
|
11
|
-
before(:each) do
|
12
|
-
@versions = v
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def default_version(v)
|
17
|
-
@default_version = v
|
18
|
-
end
|
19
|
-
|
20
|
-
def mount_to(path)
|
21
|
-
@mount = path
|
22
|
-
end
|
23
|
-
|
24
|
-
def login(*credentials)
|
25
|
-
@username, @password = credentials
|
26
|
-
|
27
|
-
before(:each) do
|
28
|
-
basic_authorize(*credentials)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Helper methods for specs.
|
34
|
-
module SpecMethods
|
35
|
-
include Rack::Test::Methods
|
36
|
-
|
37
|
-
# This class wraps raw reply from the API and provides more friendly
|
38
|
-
# interface.
|
39
|
-
class ApiResponse
|
40
|
-
def initialize(body)
|
41
|
-
@data = JSON.parse(body, symbolize_names: true)
|
42
|
-
end
|
43
|
-
|
44
|
-
def status
|
45
|
-
@data[:status]
|
46
|
-
end
|
47
|
-
|
48
|
-
def ok?
|
49
|
-
@data[:status]
|
50
|
-
end
|
51
|
-
|
52
|
-
def failed?
|
53
|
-
!ok?
|
54
|
-
end
|
55
|
-
|
56
|
-
def response
|
57
|
-
@data[:response]
|
58
|
-
end
|
59
|
-
|
60
|
-
def message
|
61
|
-
@data[:message]
|
62
|
-
end
|
63
|
-
|
64
|
-
def errors
|
65
|
-
@data[:errors]
|
66
|
-
end
|
67
|
-
|
68
|
-
def [](k)
|
69
|
-
@data[:response][k]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def app
|
74
|
-
api = HaveAPI::Server.new
|
75
|
-
api.auth_chain << @auth_chain if @auth_chain
|
76
|
-
api.use_version(@versions || :all)
|
77
|
-
api.set_default_version(@default_version) if @default_version
|
78
|
-
api.mount(@mount || '/')
|
79
|
-
api.app
|
80
|
-
end
|
81
|
-
|
82
|
-
# Login with HTTP basic auth.
|
83
|
-
def login(*credentials)
|
84
|
-
basic_authorize(*credentials)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Make API request.
|
88
|
-
# This method is a wrapper for Rack::Test::Methods. Input parameters
|
89
|
-
# are encoded into JSON and sent with correct Content-Type.
|
90
|
-
def api(http_method, url, params={})
|
91
|
-
method(http_method).call(
|
92
|
-
url,
|
93
|
-
params.to_json,
|
94
|
-
{'Content-Type' => 'application/json'}
|
95
|
-
)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Return parsed API response.
|
99
|
-
def api_response
|
100
|
-
@api_response ||= ApiResponse.new(last_response.body)
|
101
|
-
end
|
102
|
-
end
|
4
|
+
module Spec ; end
|
103
5
|
end
|
6
|
+
|
7
|
+
require_relative 'api_builder'
|
8
|
+
require_relative 'api_response'
|
9
|
+
require_relative 'spec_methods'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
class MockAction
|
3
|
+
def initialize(test, server, action, url, v)
|
4
|
+
@test = test
|
5
|
+
@server = server
|
6
|
+
@action = action
|
7
|
+
@url = url
|
8
|
+
@v = v
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(input, user: nil, &block)
|
12
|
+
action = @action.new(nil, @v, input, nil, HaveAPI::Context.new(
|
13
|
+
@server,
|
14
|
+
version: @v,
|
15
|
+
action: @action,
|
16
|
+
url: @url,
|
17
|
+
params: input,
|
18
|
+
user: user,
|
19
|
+
endpoint: true
|
20
|
+
))
|
21
|
+
|
22
|
+
unless action.authorized?(user)
|
23
|
+
fail 'Access denied. Insufficient permissions.'
|
24
|
+
end
|
25
|
+
|
26
|
+
status, data, errors = action.safe_exec
|
27
|
+
fail (data || 'action failed') unless status
|
28
|
+
action.instance_exec(@test, &block)
|
29
|
+
data
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
# Helper methods for specs.
|
3
|
+
module SpecMethods
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
return @api.app if @api
|
8
|
+
|
9
|
+
auth = get_opt(:auth_chain)
|
10
|
+
default = get_opt(:default_version)
|
11
|
+
|
12
|
+
@api = HaveAPI::Server.new(get_opt(:api_module))
|
13
|
+
@api.auth_chain << auth if auth
|
14
|
+
@api.use_version(get_opt(:versions) || :all)
|
15
|
+
@api.default_version = default if default
|
16
|
+
@api.mount(get_opt(:mount) || '/')
|
17
|
+
@api.app
|
18
|
+
end
|
19
|
+
|
20
|
+
# Login with HTTP basic auth.
|
21
|
+
def login(*credentials)
|
22
|
+
basic_authorize(*credentials)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Make API request.
|
26
|
+
# This method is a wrapper for Rack::Test::Methods. Input parameters
|
27
|
+
# are encoded into JSON and sent with a correct Content-Type.
|
28
|
+
# Two modes:
|
29
|
+
# http_method, url, params = {}
|
30
|
+
# [resource], action, params, &block
|
31
|
+
def call_api(*args, &block)
|
32
|
+
if args[0].is_a?(::Array) || args[1].is_a?(::Symbol)
|
33
|
+
r_name, a_name, params = args
|
34
|
+
|
35
|
+
app
|
36
|
+
|
37
|
+
action, url = find_action(
|
38
|
+
(params && params[:version]) || @api.default_version,
|
39
|
+
r_name, a_name
|
40
|
+
)
|
41
|
+
|
42
|
+
method(action.http_method).call(
|
43
|
+
url,
|
44
|
+
params && params.to_json,
|
45
|
+
{'Content-Type' => 'application/json'}
|
46
|
+
)
|
47
|
+
|
48
|
+
else
|
49
|
+
http_method, url, params = args
|
50
|
+
|
51
|
+
method(http_method).call(
|
52
|
+
url,
|
53
|
+
params && params.to_json,
|
54
|
+
{'Content-Type' => 'application/json'}
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Mock action call. Note that this method does not involve rack request/response
|
60
|
+
# in any way. It simply creates an instance of specified action and executes it.
|
61
|
+
# Provided block is executed in the context of the action instance after `exec()`
|
62
|
+
# has been called.
|
63
|
+
#
|
64
|
+
# If `exec()` signals error, the block is not called at all, but `RuntimeError`
|
65
|
+
# is raised instead.
|
66
|
+
#
|
67
|
+
# Authentication does not take place. Argument `user` may be used to provide
|
68
|
+
# user object. That will signify that the user is authenticated and it will be passed
|
69
|
+
# to Action.authorize.
|
70
|
+
#
|
71
|
+
# @param r_name [Array, Symbol] path to resource in the API
|
72
|
+
# @param a_name [Symbol] name of wanted action
|
73
|
+
# @param params [Hash] a hash of parameters, must contain correct namespace
|
74
|
+
# @param version [any] API version, if not specified, the default version is used
|
75
|
+
# @param user [any] object representing authenticated user
|
76
|
+
# @yield [self] the block is executed in the action instance
|
77
|
+
def mock_action(r_name, a_name, params, version: nil, user: nil, &block)
|
78
|
+
app
|
79
|
+
v = version || @api.default_version
|
80
|
+
action, url = find_action(v, r_name, a_name)
|
81
|
+
m = MockAction.new(self, @api, action, url, v)
|
82
|
+
m.call(params, user: user, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return parsed API response.
|
86
|
+
# @return [HaveAPI::Spec::ApiResponse]
|
87
|
+
def api_response
|
88
|
+
if last_response != @last_response
|
89
|
+
@last_response = last_response
|
90
|
+
@api_response = ApiResponse.new(last_response.body)
|
91
|
+
|
92
|
+
else
|
93
|
+
@api_response ||= ApiResponse.new(last_response.body)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
def get_opt(name)
|
99
|
+
self.class.opts && self.class.opts[name]
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_action(v, r_name, a_name)
|
103
|
+
# Make sure the API is built
|
104
|
+
app
|
105
|
+
|
106
|
+
resources = r_name.is_a?(::Array) ? r_name : [r_name]
|
107
|
+
|
108
|
+
top = @api.routes[v]
|
109
|
+
|
110
|
+
resources.each do |r|
|
111
|
+
top = top[:resources].detect do |k, _|
|
112
|
+
k.resource_name.to_sym == r
|
113
|
+
end.second
|
114
|
+
end
|
115
|
+
|
116
|
+
top[:actions].detect do |k, v|
|
117
|
+
k.to_s.demodulize.underscore.to_sym == a_name
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/haveapi/version.rb
CHANGED
data/lib/haveapi.rb
CHANGED
@@ -13,7 +13,7 @@ module HaveAPI
|
|
13
13
|
end
|
14
14
|
|
15
15
|
require_relative 'haveapi/params'
|
16
|
-
require_rel 'haveapi/
|
16
|
+
require_rel 'haveapi/parameters/'
|
17
17
|
require_rel 'haveapi/*.rb'
|
18
18
|
require_rel 'haveapi/model_adapters/hash'
|
19
19
|
require_rel 'haveapi/model_adapters/active_record' if ar
|