haveapi 0.18.2 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/haveapi.gemspec +2 -1
- data/lib/haveapi/action.rb +72 -31
- data/lib/haveapi/authentication/base.rb +1 -1
- data/lib/haveapi/authentication/basic/provider.rb +2 -2
- data/lib/haveapi/authentication/chain.rb +4 -4
- data/lib/haveapi/authentication/oauth2/config.rb +52 -14
- data/lib/haveapi/authentication/oauth2/provider.rb +98 -17
- data/lib/haveapi/authentication/oauth2/revoke_endpoint.rb +36 -0
- data/lib/haveapi/authentication/token/config.rb +1 -0
- data/lib/haveapi/authorization.rb +19 -12
- data/lib/haveapi/client_examples/js_client.rb +11 -1
- data/lib/haveapi/client_examples/php_client.rb +43 -1
- data/lib/haveapi/context.rb +21 -2
- data/lib/haveapi/example.rb +9 -9
- data/lib/haveapi/hooks.rb +23 -23
- data/lib/haveapi/metadata.rb +1 -1
- data/lib/haveapi/model_adapter.rb +14 -14
- data/lib/haveapi/model_adapters/active_record.rb +20 -20
- data/lib/haveapi/output_formatter.rb +4 -4
- data/lib/haveapi/output_formatters/base.rb +1 -1
- data/lib/haveapi/parameters/resource.rb +22 -22
- data/lib/haveapi/parameters/typed.rb +7 -7
- data/lib/haveapi/params.rb +24 -22
- data/lib/haveapi/resource.rb +9 -3
- data/lib/haveapi/resources/action_state.rb +16 -16
- data/lib/haveapi/route.rb +3 -2
- data/lib/haveapi/server.rb +113 -98
- data/lib/haveapi/spec/mock_action.rb +7 -7
- data/lib/haveapi/spec/spec_methods.rb +8 -8
- data/lib/haveapi/tasks/yard.rb +2 -2
- data/lib/haveapi/validator.rb +13 -13
- data/lib/haveapi/validator_chain.rb +6 -6
- data/lib/haveapi/validators/acceptance.rb +2 -2
- data/lib/haveapi/validators/confirmation.rb +4 -4
- data/lib/haveapi/validators/exclusion.rb +4 -4
- data/lib/haveapi/validators/format.rb +4 -4
- data/lib/haveapi/validators/inclusion.rb +3 -3
- data/lib/haveapi/validators/length.rb +1 -1
- data/lib/haveapi/validators/numericality.rb +3 -3
- data/lib/haveapi/validators/presence.rb +2 -2
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/version_page/auth_body.erb +6 -4
- data/lib/haveapi/views/version_page/resource_body.erb +2 -0
- data/lib/haveapi.rb +1 -0
- data/spec/authorization_spec.rb +28 -28
- data/spec/envelope_spec.rb +4 -4
- data/spec/parameters/typed_spec.rb +3 -3
- data/spec/params_spec.rb +2 -2
- data/spec/validators/acceptance_spec.rb +2 -2
- data/spec/validators/confirmation_spec.rb +4 -4
- data/spec/validators/exclusion_spec.rb +2 -2
- data/spec/validators/format_spec.rb +5 -5
- data/spec/validators/inclusion_spec.rb +8 -8
- data/spec/validators/presence_spec.rb +1 -1
- metadata +19 -4
@@ -51,7 +51,17 @@ api.authenticate("token", {
|
|
51
51
|
END
|
52
52
|
|
53
53
|
when :oauth2
|
54
|
-
|
54
|
+
<<END
|
55
|
+
#{init}
|
56
|
+
// The JavaScript client must be configured with OAuth2 access token, it does not
|
57
|
+
// support the authorization procedure to obtain a new access token.
|
58
|
+
var accessToken = {
|
59
|
+
access_token: "the access token"
|
60
|
+
};
|
61
|
+
|
62
|
+
// The client is authenticated immediately, no need for a callback
|
63
|
+
api.authenticate("oauth2", {access_token: accessToken});
|
64
|
+
END
|
55
65
|
end
|
56
66
|
end
|
57
67
|
|
@@ -35,7 +35,49 @@ $api->authenticate("token", ["token" => $savedToken]);
|
|
35
35
|
END
|
36
36
|
|
37
37
|
when :oauth2
|
38
|
-
|
38
|
+
<<END
|
39
|
+
// OAuth2 requires session
|
40
|
+
session_start();
|
41
|
+
|
42
|
+
// Client instance
|
43
|
+
#{init}
|
44
|
+
// Check if we already have an access token
|
45
|
+
if (isset($_SESSION["access_token"])) {
|
46
|
+
// We're already authenticated, reuse the existing access token
|
47
|
+
$api->authenticate("oauth2", ["access_token" => $_SESSION["access_token"]]);
|
48
|
+
|
49
|
+
} else {
|
50
|
+
// Follow the OAuth2 authorization process to get an access token using
|
51
|
+
// authorization code
|
52
|
+
$api->authenticate("oauth2", [
|
53
|
+
// Client id and secret are given by the API server
|
54
|
+
"client_id" => "your client id",
|
55
|
+
"client_secret" => "your client secret",
|
56
|
+
|
57
|
+
// This example code should run on the URL below
|
58
|
+
"redirect_uri" => "https://your-client.tld/oauth2-callback",
|
59
|
+
|
60
|
+
// Scopes are specific to the API implementation
|
61
|
+
"scope" => "all",
|
62
|
+
]);
|
63
|
+
|
64
|
+
$provider = $api->getAuthenticationProvider();
|
65
|
+
|
66
|
+
// We don't have authorization code yet, request one
|
67
|
+
if (!isset($_GET['code'])) {
|
68
|
+
// Redirect the user to the authorization endpoint
|
69
|
+
$provider->requestAuthorizationCode();
|
70
|
+
exit;
|
71
|
+
|
72
|
+
} else {
|
73
|
+
// Request access token using the token endpoint
|
74
|
+
$provider->requestAccessToken();
|
75
|
+
|
76
|
+
// Store the access token in the session
|
77
|
+
$_SESSION['access_token'] = $provider->jsonSerialize();
|
78
|
+
}
|
79
|
+
}
|
80
|
+
END
|
39
81
|
end
|
40
82
|
end
|
41
83
|
|
data/lib/haveapi/context.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module HaveAPI
|
2
2
|
class Context
|
3
3
|
attr_accessor :server, :version, :request, :resource, :action, :path, :args,
|
4
|
-
:params, :current_user, :authorization, :endpoint,
|
4
|
+
:params, :current_user, :authorization, :endpoint, :resource_path,
|
5
5
|
:action_instance, :action_prepare, :layout
|
6
6
|
|
7
7
|
def initialize(server, version: nil, request: nil, resource: [], action: nil,
|
8
8
|
path: nil, args: nil, params: nil, user: nil,
|
9
|
-
authorization: nil, endpoint: nil)
|
9
|
+
authorization: nil, endpoint: nil, resource_path: [])
|
10
10
|
@server = server
|
11
11
|
@version = version
|
12
12
|
@request = request
|
@@ -18,6 +18,7 @@ module HaveAPI
|
|
18
18
|
@current_user = user
|
19
19
|
@authorization = authorization
|
20
20
|
@endpoint = endpoint
|
21
|
+
@resource_path = resource_path
|
21
22
|
end
|
22
23
|
|
23
24
|
def resolved_path
|
@@ -71,6 +72,24 @@ module HaveAPI
|
|
71
72
|
path_for(action, call_path_params(action, obj))
|
72
73
|
end
|
73
74
|
|
75
|
+
def path_params_from_args
|
76
|
+
ret = {}
|
77
|
+
return ret if args.nil?
|
78
|
+
|
79
|
+
my_args = args.clone
|
80
|
+
|
81
|
+
path.scan(/\{([a-zA-Z\-_]+)\}/) do |match|
|
82
|
+
path_param = match.first
|
83
|
+
ret[path_param] = my_args.shift
|
84
|
+
end
|
85
|
+
|
86
|
+
ret
|
87
|
+
end
|
88
|
+
|
89
|
+
def action_scope
|
90
|
+
resource_path.map(&:downcase).join('.') + '#' + action.action_name.underscore
|
91
|
+
end
|
92
|
+
|
74
93
|
private
|
75
94
|
def resolve_arg!(path, arg)
|
76
95
|
path.sub!(/\{[a-zA-Z\-_]+\}/, arg.to_s)
|
data/lib/haveapi/example.rb
CHANGED
@@ -58,15 +58,15 @@ module HaveAPI
|
|
58
58
|
def describe(context)
|
59
59
|
if provided?
|
60
60
|
{
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
title: @title,
|
62
|
+
comment: @comment,
|
63
|
+
path_params: @path_params,
|
64
|
+
request: filter_input_params(context, @request),
|
65
|
+
response: filter_output_params(context, @response),
|
66
|
+
status: @status.nil? ? true : @status,
|
67
|
+
message: @message,
|
68
|
+
errors: @errors,
|
69
|
+
http_status: @http_status || 200,
|
70
70
|
}
|
71
71
|
else
|
72
72
|
{}
|
data/lib/haveapi/hooks.rb
CHANGED
@@ -79,15 +79,15 @@ module HaveAPI
|
|
79
79
|
module Hooks
|
80
80
|
INSTANCE_VARIABLE = '@_haveapi_hooks'
|
81
81
|
|
82
|
-
# Register a hook defined by
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
82
|
+
# Register a hook defined by `klass` with `name`.
|
83
|
+
# @param klass [Class] an instance of Class, that is class name, not it's instance
|
84
|
+
# @param opts [Hash]
|
85
|
+
# @option opts [String] :desc why this hook exists, when it's called
|
86
|
+
# @option opts [String] :context the context in which given blocks are called
|
87
|
+
# @option opts [Hash] :args hash of block positional arguments
|
88
|
+
# @option opts [Hash] :kwargs hash of block keyword arguments
|
89
|
+
# @option opts [Hash] :initial - hash of initial values
|
90
|
+
# @option opts [Hash] :ret hash of return values
|
91
91
|
def self.register_hook(klass, name, opts = {})
|
92
92
|
classified = hook_classify(klass)
|
93
93
|
opts[:listeners] = []
|
@@ -101,13 +101,13 @@ module HaveAPI
|
|
101
101
|
@hooks
|
102
102
|
end
|
103
103
|
|
104
|
-
# Connect class hook defined in
|
105
|
-
#
|
104
|
+
# Connect class hook defined in `klass` with `name` to `block`.
|
105
|
+
# `klass` is a class name.
|
106
106
|
def self.connect_hook(klass, name, &block)
|
107
107
|
@hooks[hook_classify(klass)][name][:listeners] << block
|
108
108
|
end
|
109
109
|
|
110
|
-
# Connect instance hook from instance
|
110
|
+
# Connect instance hook from instance `klass` with `name` to `block`.
|
111
111
|
def self.connect_instance_hook(instance, name, &block)
|
112
112
|
hooks = instance.instance_variable_get(INSTANCE_VARIABLE)
|
113
113
|
|
@@ -120,11 +120,11 @@ module HaveAPI
|
|
120
120
|
hooks[name][:listeners] << block
|
121
121
|
end
|
122
122
|
|
123
|
-
# Call all blocks that are connected to hook in
|
123
|
+
# Call all blocks that are connected to hook in `klass` with `name`.
|
124
124
|
# +klass+ may be a class name or an object instance.
|
125
|
-
# If
|
126
|
-
#
|
127
|
-
# to all block is always a return value from previous block or
|
125
|
+
# If `where` is set, the blocks are executed in it with instance_exec.
|
126
|
+
# `args` is an array of arguments given to all blocks. The first argument
|
127
|
+
# to all block is always a return value from previous block or `initial`,
|
128
128
|
# which defaults to an empty hash.
|
129
129
|
#
|
130
130
|
# Blocks are executed one by one in the order they were connected.
|
@@ -193,17 +193,17 @@ module HaveAPI
|
|
193
193
|
# Classes that define hooks must include this module.
|
194
194
|
module Hookable
|
195
195
|
module ClassMethods
|
196
|
-
# Register a hook named
|
196
|
+
# Register a hook named `name`.
|
197
197
|
def has_hook(name, opts = {})
|
198
198
|
Hooks.register_hook(self.to_s, name, opts)
|
199
199
|
end
|
200
200
|
|
201
|
-
# Connect
|
201
|
+
# Connect `block` to registered hook with `name`.
|
202
202
|
def connect_hook(name, &block)
|
203
203
|
Hooks.connect_hook(self.to_s, name, &block)
|
204
204
|
end
|
205
205
|
|
206
|
-
# Call all hooks for
|
206
|
+
# Call all hooks for `name`. see {Hooks.call_for}.
|
207
207
|
def call_hooks(*args, **kwargs)
|
208
208
|
Hooks.call_for(self.to_s, *args, **kwargs)
|
209
209
|
end
|
@@ -228,7 +228,7 @@ module HaveAPI
|
|
228
228
|
Hooks.call_for(self.class, name, where, args: args, kwargs: kwargs, initial: initial)
|
229
229
|
end
|
230
230
|
|
231
|
-
# Call hooks for different
|
231
|
+
# Call hooks for different `klass`.
|
232
232
|
def call_hooks_as_for(klass, *args, **kwargs)
|
233
233
|
ret = call_instance_hooks_as_for(klass, *args, **kwargs)
|
234
234
|
|
@@ -236,17 +236,17 @@ module HaveAPI
|
|
236
236
|
call_class_hooks_as_for(klass.class, *args, **kwargs)
|
237
237
|
end
|
238
238
|
|
239
|
-
# Call only instance hooks for different
|
239
|
+
# Call only instance hooks for different `klass`.
|
240
240
|
def call_instance_hooks_as_for(klass, *args, **kwargs)
|
241
241
|
Hooks.call_for(klass, *args, **kwargs)
|
242
242
|
end
|
243
243
|
|
244
|
-
# Call only class hooks for different
|
244
|
+
# Call only class hooks for different `klass`.
|
245
245
|
def call_class_hooks_as_for(klass, *args, **kwargs)
|
246
246
|
Hooks.call_for(klass, *args, **kwargs)
|
247
247
|
end
|
248
248
|
|
249
|
-
# Connect instance level hook
|
249
|
+
# Connect instance level hook `name` to `block`.
|
250
250
|
def connect_hook(name, &block)
|
251
251
|
Hooks.connect_instance_hook(self, name, &block)
|
252
252
|
end
|
data/lib/haveapi/metadata.rb
CHANGED
@@ -2,8 +2,8 @@ module HaveAPI
|
|
2
2
|
# Model adapters are used to automate handling of action
|
3
3
|
# input/output.
|
4
4
|
#
|
5
|
-
# Adapters are chosen based on the
|
6
|
-
# If no
|
5
|
+
# Adapters are chosen based on the `model` set on a HaveAPI::Resource.
|
6
|
+
# If no `model` is specified, ModelAdapters::Hash is used as a default
|
7
7
|
# adapter.
|
8
8
|
#
|
9
9
|
# All model adapters are based on this class.
|
@@ -17,7 +17,7 @@ module HaveAPI
|
|
17
17
|
ModelAdapter.adapters << Kernel.const_get(self.to_s)
|
18
18
|
end
|
19
19
|
|
20
|
-
# Returns an adapter suitable for
|
20
|
+
# Returns an adapter suitable for `layout` and `obj`.
|
21
21
|
# Adapters are iterated over and the first to return true to handle?()
|
22
22
|
# is returned.
|
23
23
|
def for(layout, obj)
|
@@ -41,21 +41,21 @@ module HaveAPI
|
|
41
41
|
self::Output.new(*args)
|
42
42
|
end
|
43
43
|
|
44
|
-
# Override this method to load validators from
|
45
|
-
# to
|
44
|
+
# Override this method to load validators from `model`
|
45
|
+
# to `params`.
|
46
46
|
def load_validators(model, params)
|
47
47
|
|
48
48
|
end
|
49
49
|
|
50
50
|
# Called when mounting the API. Model adapters may use this method
|
51
|
-
# to add custom meta parameters to
|
52
|
-
#
|
51
|
+
# to add custom meta parameters to `action`. `direction` is one of
|
52
|
+
# `:input` and `:output`.
|
53
53
|
def used_by(direction, action)
|
54
54
|
case direction
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
when :input
|
56
|
+
self::Input.used_by(action)
|
57
|
+
when :output
|
58
|
+
self::Output.used_by(action)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -72,12 +72,12 @@ module HaveAPI
|
|
72
72
|
end
|
73
73
|
|
74
74
|
# Return true if input parameters contain parameter
|
75
|
-
# with
|
75
|
+
# with `name`.
|
76
76
|
def has_param?(name)
|
77
77
|
@input.has_key?(name)
|
78
78
|
end
|
79
79
|
|
80
|
-
# Return parameter with
|
80
|
+
# Return parameter with `name`.
|
81
81
|
def [](name)
|
82
82
|
@input[name]
|
83
83
|
end
|
@@ -101,7 +101,7 @@ module HaveAPI
|
|
101
101
|
end
|
102
102
|
|
103
103
|
# Return true if input parameters contain parameter
|
104
|
-
# with
|
104
|
+
# with `name`.
|
105
105
|
def has_param?(name)
|
106
106
|
|
107
107
|
end
|
@@ -21,7 +21,7 @@ module HaveAPI::ModelAdapters
|
|
21
21
|
module InstanceMethods
|
22
22
|
# Helper method that sets correct ActiveRecord includes
|
23
23
|
# according to the meta includes sent by the user.
|
24
|
-
#
|
24
|
+
# `q` is the model or partial AR query. If not set,
|
25
25
|
# action's model class is used instead.
|
26
26
|
def with_includes(q = nil)
|
27
27
|
q ||= self.class.model
|
@@ -179,14 +179,14 @@ END
|
|
179
179
|
end
|
180
180
|
|
181
181
|
{
|
182
|
-
|
183
|
-
|
182
|
+
path_params: params.is_a?(Array) ? params : [params],
|
183
|
+
resolved: true
|
184
184
|
}
|
185
185
|
end
|
186
186
|
|
187
187
|
protected
|
188
|
-
# Return representation of an associated resource
|
189
|
-
# with its instance in
|
188
|
+
# Return representation of an associated resource `param`
|
189
|
+
# with its instance in `val`.
|
190
190
|
#
|
191
191
|
# By default, it returns an unresolved resource, which contains
|
192
192
|
# only object id and label. Resource will be resolved
|
@@ -204,11 +204,11 @@ END
|
|
204
204
|
pass_includes = includes_pass_on_to(param.name)
|
205
205
|
|
206
206
|
show = res_show.new(
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
207
|
+
push_ins.request,
|
208
|
+
push_ins.version,
|
209
|
+
{},
|
210
|
+
nil,
|
211
|
+
@context
|
212
212
|
)
|
213
213
|
show.meta[:includes] = pass_includes
|
214
214
|
|
@@ -231,17 +231,17 @@ END
|
|
231
231
|
|
232
232
|
else
|
233
233
|
{
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
234
|
+
param.value_id => val.send(res_output[param.value_id].db_name),
|
235
|
+
param.value_label => val.send(res_output[param.value_label].db_name),
|
236
|
+
_meta: {
|
237
|
+
:path_params => args.is_a?(Array) ? args : [args],
|
238
|
+
:resolved => false
|
239
|
+
}
|
240
240
|
}
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
244
|
-
# Should an association with
|
244
|
+
# Should an association with `name` be resolved?
|
245
245
|
def includes_include?(name)
|
246
246
|
includes = @context.action_instance.meta[:includes]
|
247
247
|
return unless includes
|
@@ -324,7 +324,7 @@ END
|
|
324
324
|
|
325
325
|
handle ::ActiveModel::Validations::ExclusionValidator do |v|
|
326
326
|
opts = {
|
327
|
-
|
327
|
+
values: v.options[:in].map { |v| v }
|
328
328
|
}
|
329
329
|
opts[:message] = v.options[:message] if v.options[:message]
|
330
330
|
|
@@ -333,7 +333,7 @@ END
|
|
333
333
|
|
334
334
|
handle ::ActiveModel::Validations::FormatValidator do |v|
|
335
335
|
opts = {
|
336
|
-
|
336
|
+
rx: v.options[:with]
|
337
337
|
}
|
338
338
|
opts[:message] = v.options[:message] if v.options[:message]
|
339
339
|
|
@@ -342,7 +342,7 @@ END
|
|
342
342
|
|
343
343
|
handle ::ActiveModel::Validations::InclusionValidator do |v|
|
344
344
|
opts = {
|
345
|
-
|
345
|
+
values: v.options[:in].map { |v| v }
|
346
346
|
}
|
347
347
|
opts[:message] = v.options[:message] if v.options[:message]
|
348
348
|
|
@@ -49,10 +49,10 @@ module HaveAPI
|
|
49
49
|
ret = {}
|
50
50
|
ret[:version] = HaveAPI::PROTOCOL_VERSION if version
|
51
51
|
ret.update({
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
status: status,
|
53
|
+
response: response,
|
54
|
+
message: message,
|
55
|
+
errors: errors
|
56
56
|
})
|
57
57
|
ret
|
58
58
|
end
|
@@ -43,35 +43,35 @@ module HaveAPI::Parameters
|
|
43
43
|
|
44
44
|
def describe(context)
|
45
45
|
val_path = context.path_for(
|
46
|
-
|
47
|
-
|
46
|
+
@resource::Show,
|
47
|
+
context.endpoint && context.action_prepare && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
|
48
48
|
)
|
49
49
|
val_method = @resource::Index.http_method.to_s.upcase
|
50
50
|
|
51
51
|
choices_path = context.path_for(
|
52
|
-
|
53
|
-
|
52
|
+
@choices,
|
53
|
+
context.endpoint && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
|
54
54
|
)
|
55
55
|
choices_method = @choices.http_method.to_s.upcase
|
56
56
|
|
57
57
|
{
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
58
|
+
required: required?,
|
59
|
+
label: @label,
|
60
|
+
description: @desc,
|
61
|
+
type: 'Resource',
|
62
|
+
resource: @resource_path,
|
63
|
+
value_id: @value_id,
|
64
|
+
value_label: @value_label,
|
65
|
+
value: context.action_prepare && {
|
66
|
+
path: val_path,
|
67
|
+
method: val_method,
|
68
|
+
help: "#{val_path}?method=#{val_method}",
|
69
|
+
},
|
70
|
+
choices: {
|
71
|
+
path: choices_path,
|
72
|
+
method: choices_method,
|
73
|
+
help: "#{choices_path}?method=#{choices_method}"
|
74
|
+
}
|
75
75
|
}
|
76
76
|
end
|
77
77
|
|
@@ -93,7 +93,7 @@ module HaveAPI::Parameters
|
|
93
93
|
|
94
94
|
def clean(raw)
|
95
95
|
::HaveAPI::ModelAdapter.for(
|
96
|
-
|
96
|
+
show_action.input.layout, @resource.model
|
97
97
|
).input_clean(@resource.model, raw, @extra)
|
98
98
|
end
|
99
99
|
|
@@ -46,13 +46,13 @@ module HaveAPI::Parameters
|
|
46
46
|
|
47
47
|
def describe(context)
|
48
48
|
{
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
49
|
+
required: required?,
|
50
|
+
label: @label,
|
51
|
+
description: @desc,
|
52
|
+
type: @type ? @type.to_s : String.to_s,
|
53
|
+
validators: @validators ? @validators.describe : {},
|
54
|
+
default: @default,
|
55
|
+
protected: @protected || false,
|
56
56
|
}
|
57
57
|
end
|
58
58
|
|
data/lib/haveapi/params.rb
CHANGED
@@ -178,12 +178,14 @@ module HaveAPI
|
|
178
178
|
|
179
179
|
if @direction == :input
|
180
180
|
ret[:parameters] = context.authorization.filter_input(
|
181
|
-
|
182
|
-
|
181
|
+
@params,
|
182
|
+
ModelAdapters::Hash.output(context, ret[:parameters])
|
183
|
+
)
|
183
184
|
else
|
184
185
|
ret[:parameters] = context.authorization.filter_output(
|
185
|
-
|
186
|
-
|
186
|
+
@params,
|
187
|
+
ModelAdapters::Hash.output(context, ret[:parameters])
|
188
|
+
)
|
187
189
|
end
|
188
190
|
|
189
191
|
ret
|
@@ -205,11 +207,11 @@ module HaveAPI
|
|
205
207
|
end
|
206
208
|
|
207
209
|
case layout
|
208
|
-
|
209
|
-
|
210
|
+
when :object, :hash
|
211
|
+
params[namespace] ||= {}
|
210
212
|
|
211
|
-
|
212
|
-
|
213
|
+
when :object_list, :hash_list
|
214
|
+
params[namespace] ||= []
|
213
215
|
end
|
214
216
|
end
|
215
217
|
|
@@ -298,14 +300,14 @@ module HaveAPI
|
|
298
300
|
|
299
301
|
def valid_layout?(params)
|
300
302
|
case layout
|
301
|
-
|
302
|
-
|
303
|
+
when :object, :hash
|
304
|
+
params[namespace].is_a?(Hash)
|
303
305
|
|
304
|
-
|
305
|
-
|
306
|
+
when :object_list, :hash_list
|
307
|
+
params[namespace].is_a?(Array)
|
306
308
|
|
307
|
-
|
308
|
-
|
309
|
+
else
|
310
|
+
false
|
309
311
|
end
|
310
312
|
end
|
311
313
|
|
@@ -313,16 +315,16 @@ module HaveAPI
|
|
313
315
|
ns = namespace
|
314
316
|
|
315
317
|
case layout
|
316
|
-
|
317
|
-
|
318
|
+
when :object, :hash
|
319
|
+
yield(ns ? params[namespace] : params)
|
318
320
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
321
|
+
when :object_list, :hash_list
|
322
|
+
(ns ? params[namespace] : params).each do |object|
|
323
|
+
yield(object)
|
324
|
+
end
|
323
325
|
|
324
|
-
|
325
|
-
|
326
|
+
else
|
327
|
+
false
|
326
328
|
end
|
327
329
|
end
|
328
330
|
|
data/lib/haveapi/resource.rb
CHANGED
@@ -59,20 +59,21 @@ module HaveAPI
|
|
59
59
|
singular ? resource_name.singularize.underscore : resource_name.tableize
|
60
60
|
end
|
61
61
|
|
62
|
-
def self.routes(prefix='/')
|
62
|
+
def self.routes(prefix='/', resource_path: [])
|
63
63
|
ret = []
|
64
64
|
prefix = "#{prefix}#{@route || rest_name}/"
|
65
|
+
new_resource_path = resource_path + [resource_name.underscore]
|
65
66
|
|
66
67
|
actions do |a|
|
67
68
|
# Call used_by for selected model adapters. It is safe to do
|
68
69
|
# only when all classes are loaded.
|
69
70
|
a.initialize
|
70
71
|
|
71
|
-
ret << Route.new(a.build_route(prefix).chomp('/'), a)
|
72
|
+
ret << Route.new(a.build_route(prefix).chomp('/'), a, new_resource_path)
|
72
73
|
end
|
73
74
|
|
74
75
|
resources do |r|
|
75
|
-
ret << {r => r.routes(prefix)}
|
76
|
+
ret << {r => r.routes(prefix, resource_path: new_resource_path)}
|
76
77
|
end
|
77
78
|
|
78
79
|
ret
|
@@ -83,6 +84,9 @@ module HaveAPI
|
|
83
84
|
|
84
85
|
context.resource = self
|
85
86
|
|
87
|
+
orig_resource_path = context.resource_path
|
88
|
+
context.resource_path = context.resource_path + [resource_name.underscore]
|
89
|
+
|
86
90
|
hash[:actions].each do |action, path|
|
87
91
|
context.action = action
|
88
92
|
context.path = path
|
@@ -97,6 +101,8 @@ module HaveAPI
|
|
97
101
|
ret[:resources][resource.resource_name.underscore] = resource.describe(children, context)
|
98
102
|
end
|
99
103
|
|
104
|
+
context.resource_path = orig_resource_path
|
105
|
+
|
100
106
|
ret
|
101
107
|
end
|
102
108
|
|