haveapi 0.28.0 → 0.28.2
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/haveapi.gemspec +1 -1
- data/lib/haveapi/action.rb +67 -51
- data/lib/haveapi/authentication/token/provider.rb +12 -0
- data/lib/haveapi/authorization.rb +21 -0
- data/lib/haveapi/context.rb +17 -2
- data/lib/haveapi/example.rb +1 -0
- data/lib/haveapi/extensions/exception_mailer.rb +6 -2
- data/lib/haveapi/metadata.rb +1 -1
- data/lib/haveapi/model_adapters/active_record.rb +9 -4
- data/lib/haveapi/parameters/resource.rb +5 -4
- data/lib/haveapi/parameters/typed.rb +34 -2
- data/lib/haveapi/params.rb +16 -14
- data/lib/haveapi/resources/action_state.rb +3 -3
- data/lib/haveapi/server.rb +43 -8
- data/lib/haveapi/spec/api_builder.rb +10 -0
- data/lib/haveapi/spec/mock_action.rb +10 -9
- data/lib/haveapi/spec/spec_methods.rb +4 -0
- data/lib/haveapi/version.rb +1 -1
- data/spec/action/runtime_spec.rb +278 -16
- data/spec/action/validation_http_status_spec.rb +76 -0
- data/spec/action_state_spec.rb +28 -5
- data/spec/documentation/auth_filtering_spec.rb +14 -2
- data/spec/model_adapters/active_record_spec.rb +179 -11
- data/spec/parameters/typed_spec.rb +54 -0
- data/spec/server/integration_spec.rb +1 -1
- data/test_support/client_test_api.rb +8 -8
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c0e9ead0f7cf99be75dc598c843487b2609b0cdc902694b800b9fcdb7df7ef9
|
|
4
|
+
data.tar.gz: 1689ebabcc73b7c9f31c53d328abf133f3ab6b3feb6c7892ddbfa1f32f7e426d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 44f5728075305342ec0e01a35d80c9ec85a07552b15b44acb1a5ee8139017dd5a0c669ee415f2ecd9b8a66a4eff65a8968fde169d5cbec38e767a036e81cc97a
|
|
7
|
+
data.tar.gz: 40acf5edb198ccb57b8f195342c9aed5d8badbfb87f6e65fda2725f641d21ee1cd8f348d5393e99d12441f56015e0679c371a53f4ab25999fdb6ba592d322789
|
data/haveapi.gemspec
CHANGED
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
s.required_ruby_version = ">= #{File.read('../../.ruby-version').strip}"
|
|
16
16
|
|
|
17
17
|
s.add_dependency 'activesupport', '>= 7.1'
|
|
18
|
-
s.add_dependency 'haveapi-client', '~> 0.28.
|
|
18
|
+
s.add_dependency 'haveapi-client', '~> 0.28.2'
|
|
19
19
|
s.add_dependency 'json'
|
|
20
20
|
s.add_dependency 'mail'
|
|
21
21
|
s.add_dependency 'nesty', '~> 1.0'
|
data/lib/haveapi/action.rb
CHANGED
|
@@ -259,9 +259,9 @@ module HaveAPI
|
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
def from_context(c)
|
|
262
|
-
ret = new(nil, c.version, c.
|
|
262
|
+
ret = new(nil, c.version, c.path_params || c.path_params_from_args, c.input || c.params || {}, c)
|
|
263
263
|
ret.instance_exec do
|
|
264
|
-
@
|
|
264
|
+
@safe_input = self.class.input && input_from_params(raw_input_params(self.class.input))
|
|
265
265
|
@authorization = c.authorization
|
|
266
266
|
@current_user = c.current_user
|
|
267
267
|
end
|
|
@@ -287,7 +287,9 @@ module HaveAPI
|
|
|
287
287
|
params = {}
|
|
288
288
|
|
|
289
289
|
path_param_names(path).each do |name|
|
|
290
|
-
|
|
290
|
+
value = values.shift.to_s
|
|
291
|
+
params[name] = value
|
|
292
|
+
params[name.to_sym] = value
|
|
291
293
|
end
|
|
292
294
|
|
|
293
295
|
params
|
|
@@ -306,14 +308,12 @@ module HaveAPI
|
|
|
306
308
|
end
|
|
307
309
|
end
|
|
308
310
|
|
|
309
|
-
def initialize(request, version, params,
|
|
311
|
+
def initialize(request, version, params, input_params, context)
|
|
310
312
|
super()
|
|
311
313
|
@request = request
|
|
312
314
|
@version = version
|
|
313
315
|
@route_params = params.dup
|
|
314
|
-
@
|
|
315
|
-
@params = params
|
|
316
|
-
@params.update(@body)
|
|
316
|
+
@raw_input = input_params || {}
|
|
317
317
|
@context = context
|
|
318
318
|
@context.action = self.class
|
|
319
319
|
@context.action_instance = self
|
|
@@ -333,25 +333,28 @@ module HaveAPI
|
|
|
333
333
|
end
|
|
334
334
|
|
|
335
335
|
def validate!
|
|
336
|
-
|
|
336
|
+
validate
|
|
337
337
|
rescue ValidationError => e
|
|
338
|
-
|
|
338
|
+
opts = {}
|
|
339
|
+
status = @context.server.validation_error_http_status
|
|
340
|
+
opts[:http_status] = status if status
|
|
341
|
+
|
|
342
|
+
error!(e.message, e.to_hash, opts)
|
|
339
343
|
end
|
|
340
344
|
|
|
341
345
|
def authorized?(user)
|
|
342
346
|
@current_user = user
|
|
343
|
-
@authorization.authorized?(user,
|
|
347
|
+
@authorization.authorized?(user, path_params)
|
|
344
348
|
end
|
|
345
349
|
|
|
346
|
-
def
|
|
347
|
-
@
|
|
350
|
+
def path_params
|
|
351
|
+
@path_params ||= extract_path_params
|
|
348
352
|
end
|
|
349
353
|
|
|
350
354
|
def input
|
|
351
355
|
return unless self.class.input
|
|
352
356
|
|
|
353
|
-
|
|
354
|
-
ns ? @safe_params[ns] : @safe_params
|
|
357
|
+
@safe_input
|
|
355
358
|
end
|
|
356
359
|
|
|
357
360
|
def meta
|
|
@@ -593,51 +596,37 @@ module HaveAPI
|
|
|
593
596
|
|
|
594
597
|
def validate
|
|
595
598
|
# Validate standard input
|
|
596
|
-
@
|
|
599
|
+
@safe_input = nil
|
|
597
600
|
input = self.class.input
|
|
598
601
|
|
|
599
602
|
if input
|
|
600
|
-
raw_params =
|
|
603
|
+
raw_params = raw_input_params(input)
|
|
601
604
|
|
|
602
605
|
# First check layout
|
|
603
606
|
input.check_layout(raw_params)
|
|
604
607
|
|
|
605
608
|
# Then filter allowed params
|
|
606
|
-
case input.layout
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
self.class.model_adapter(self.class.input.layout).input(
|
|
624
|
-
input.namespace ? raw_params[input.namespace] : raw_params
|
|
625
|
-
)
|
|
626
|
-
)
|
|
627
|
-
if input.namespace
|
|
628
|
-
@safe_params[input.namespace] = filtered
|
|
629
|
-
else
|
|
630
|
-
self.class.input.params.each do |p|
|
|
631
|
-
@safe_params.delete(p.name)
|
|
632
|
-
@safe_params.delete(p.name.to_s)
|
|
633
|
-
end
|
|
634
|
-
@safe_params.update(filtered)
|
|
635
|
-
end
|
|
636
|
-
end
|
|
609
|
+
@safe_input = case input.layout
|
|
610
|
+
when :object_list, :hash_list
|
|
611
|
+
input_from_params(raw_params).map do |obj|
|
|
612
|
+
@authorization.filter_input(
|
|
613
|
+
self.class.input.params,
|
|
614
|
+
self.class.model_adapter(self.class.input.layout).input(obj)
|
|
615
|
+
)
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
else
|
|
619
|
+
@authorization.filter_input(
|
|
620
|
+
self.class.input.params,
|
|
621
|
+
self.class.model_adapter(self.class.input.layout).input(
|
|
622
|
+
input_from_params(raw_params)
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
end
|
|
637
626
|
|
|
638
627
|
# Now check required params, convert types and set defaults
|
|
639
628
|
input.validate(
|
|
640
|
-
|
|
629
|
+
validated_input_params(input),
|
|
641
630
|
context: @context,
|
|
642
631
|
only: @authorization.permitted_input_names(self.class.input.params)
|
|
643
632
|
)
|
|
@@ -678,15 +667,42 @@ module HaveAPI
|
|
|
678
667
|
def metadata_params(type, input)
|
|
679
668
|
case type
|
|
680
669
|
when :global
|
|
681
|
-
fetch_metadata_from(@
|
|
670
|
+
fetch_metadata_from(@raw_input)
|
|
682
671
|
when :object
|
|
683
672
|
return unless input && input.namespace
|
|
684
673
|
|
|
685
|
-
obj_params = @
|
|
674
|
+
obj_params = fetch_param(@raw_input, input.namespace)
|
|
686
675
|
fetch_metadata_from(obj_params) if obj_params.is_a?(Hash)
|
|
687
676
|
end
|
|
688
677
|
end
|
|
689
678
|
|
|
679
|
+
def raw_input_params(input)
|
|
680
|
+
return @raw_input.dup unless input.namespace
|
|
681
|
+
|
|
682
|
+
{ input.namespace => fetch_param(@raw_input, input.namespace) }
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def validated_input_params(input)
|
|
686
|
+
input.namespace ? { input.namespace => @safe_input } : @safe_input
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def input_from_params(params)
|
|
690
|
+
input = self.class.input
|
|
691
|
+
return unless input
|
|
692
|
+
|
|
693
|
+
input.namespace ? fetch_param(params, input.namespace) : params
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
def fetch_param(params, name)
|
|
697
|
+
return unless params
|
|
698
|
+
return params[name] if params.has_key?(name)
|
|
699
|
+
|
|
700
|
+
string_name = name.to_s
|
|
701
|
+
return params[string_name] if params.has_key?(string_name)
|
|
702
|
+
|
|
703
|
+
nil
|
|
704
|
+
end
|
|
705
|
+
|
|
690
706
|
def fetch_metadata_from(params)
|
|
691
707
|
[Metadata.namespace, Metadata.namespace.to_s].each do |ns|
|
|
692
708
|
return params[ns] if params && params.has_key?(ns)
|
|
@@ -714,7 +730,7 @@ module HaveAPI
|
|
|
714
730
|
global_meta = self.class.meta(:global)
|
|
715
731
|
return @reply_meta[:global] unless global_meta && global_meta.output
|
|
716
732
|
|
|
717
|
-
@authorization.
|
|
733
|
+
@authorization.filter_meta_output(
|
|
718
734
|
global_meta.output.params,
|
|
719
735
|
self.class.model_adapter(global_meta.output.layout).output(@context, @reply_meta[:global]),
|
|
720
736
|
true
|
|
@@ -242,6 +242,12 @@ module HaveAPI::Authentication
|
|
|
242
242
|
allow
|
|
243
243
|
end
|
|
244
244
|
|
|
245
|
+
def validate!
|
|
246
|
+
validate
|
|
247
|
+
rescue HaveAPI::ValidationError => e
|
|
248
|
+
error!(e.message, e.to_hash, http_status: 400)
|
|
249
|
+
end
|
|
250
|
+
|
|
245
251
|
def exec
|
|
246
252
|
config = self.class.resource.token_instance.config
|
|
247
253
|
|
|
@@ -378,6 +384,12 @@ module HaveAPI::Authentication
|
|
|
378
384
|
allow
|
|
379
385
|
end
|
|
380
386
|
|
|
387
|
+
def validate!
|
|
388
|
+
validate
|
|
389
|
+
rescue HaveAPI::ValidationError => e
|
|
390
|
+
error!(e.message, e.to_hash, http_status: 400)
|
|
391
|
+
end
|
|
392
|
+
|
|
381
393
|
define_method(:exec) do
|
|
382
394
|
begin
|
|
383
395
|
result = config.handle.call(ActionRequest.new(
|
|
@@ -61,6 +61,13 @@ module HaveAPI
|
|
|
61
61
|
}
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def meta_output(whitelist: nil, blacklist: nil)
|
|
65
|
+
@meta_output = {
|
|
66
|
+
whitelist:,
|
|
67
|
+
blacklist:
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
64
71
|
def allow
|
|
65
72
|
throw(:rule, true)
|
|
66
73
|
end
|
|
@@ -91,6 +98,10 @@ module HaveAPI
|
|
|
91
98
|
filter_inner(output, @output, params, format)
|
|
92
99
|
end
|
|
93
100
|
|
|
101
|
+
def filter_meta_output(output, params, format = false)
|
|
102
|
+
filter_inner(output, meta_output_filter, params, format)
|
|
103
|
+
end
|
|
104
|
+
|
|
94
105
|
def permitted_input_names(params)
|
|
95
106
|
permitted_params(params, @input).map(&:name)
|
|
96
107
|
end
|
|
@@ -128,6 +139,16 @@ module HaveAPI
|
|
|
128
139
|
end
|
|
129
140
|
end
|
|
130
141
|
|
|
142
|
+
def meta_output_filter
|
|
143
|
+
return @meta_output if @meta_output
|
|
144
|
+
return unless @output && @output[:blacklist]
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
whitelist: nil,
|
|
148
|
+
blacklist: @output[:blacklist]
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
|
|
131
152
|
def normalize_names(names)
|
|
132
153
|
names.map { |name| normalize_key(name) }
|
|
133
154
|
end
|
data/lib/haveapi/context.rb
CHANGED
|
@@ -2,13 +2,13 @@ module HaveAPI
|
|
|
2
2
|
class Context
|
|
3
3
|
attr_accessor :server, :version, :request, :resource, :action, :path, :args,
|
|
4
4
|
:params, :current_user, :authorization, :endpoint, :resource_path,
|
|
5
|
-
:action_instance, :action_prepare, :layout, :doc,
|
|
5
|
+
:path_params, :input, :action_instance, :action_prepare, :layout, :doc,
|
|
6
6
|
:auth_users_by_version
|
|
7
7
|
|
|
8
8
|
def initialize(server, version: nil, request: nil, resource: [], action: nil,
|
|
9
9
|
path: nil, args: nil, params: nil, user: nil,
|
|
10
10
|
authorization: nil, endpoint: nil, resource_path: [], doc: false,
|
|
11
|
-
auth_users_by_version: nil)
|
|
11
|
+
auth_users_by_version: nil, path_params: nil, input: nil)
|
|
12
12
|
@server = server
|
|
13
13
|
@version = version
|
|
14
14
|
@request = request
|
|
@@ -17,6 +17,8 @@ module HaveAPI
|
|
|
17
17
|
@path = path
|
|
18
18
|
@args = args
|
|
19
19
|
@params = params
|
|
20
|
+
@path_params = path_params
|
|
21
|
+
@input = input
|
|
20
22
|
@current_user = user
|
|
21
23
|
@authorization = authorization
|
|
22
24
|
@endpoint = endpoint
|
|
@@ -64,6 +66,19 @@ module HaveAPI
|
|
|
64
66
|
ret
|
|
65
67
|
end
|
|
66
68
|
|
|
69
|
+
def action_path_for(action, args = nil)
|
|
70
|
+
ret = @server.path_for_action(@version, action) || path_for(action)
|
|
71
|
+
|
|
72
|
+
ret = ret.dup
|
|
73
|
+
args.each { |arg| resolve_arg!(ret, arg) } if args
|
|
74
|
+
|
|
75
|
+
ret
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def path_params_for(action, args)
|
|
79
|
+
action.path_params(action_path_for(action), args)
|
|
80
|
+
end
|
|
81
|
+
|
|
67
82
|
def call_path_params(action, obj)
|
|
68
83
|
ret = params && action.resolve_path_params(obj)
|
|
69
84
|
|
data/lib/haveapi/example.rb
CHANGED
|
@@ -227,8 +227,12 @@ module HaveAPI::Extensions
|
|
|
227
227
|
<td><%=h context.args %></td>
|
|
228
228
|
</tr>
|
|
229
229
|
<tr>
|
|
230
|
-
<th>
|
|
231
|
-
<td><%=h context.
|
|
230
|
+
<th>Path parameters</th>
|
|
231
|
+
<td><%=h context.path_params %></td>
|
|
232
|
+
</tr>
|
|
233
|
+
<tr>
|
|
234
|
+
<th>Input</th>
|
|
235
|
+
<td><%=h context.input %></td>
|
|
232
236
|
</tr>
|
|
233
237
|
<tr>
|
|
234
238
|
<th>User</th>
|
data/lib/haveapi/metadata.rb
CHANGED
|
@@ -383,24 +383,29 @@ module HaveAPI::ModelAdapters
|
|
|
383
383
|
push_cls = @context.action
|
|
384
384
|
push_ins = @context.action_instance
|
|
385
385
|
push_path = @context.path
|
|
386
|
-
|
|
386
|
+
push_path_params = @context.path_params
|
|
387
|
+
path = @context.action_path_for(res_show)
|
|
388
|
+
path_params = @context.path_params_for(res_show, args)
|
|
389
|
+
@context.path = path
|
|
390
|
+
@context.path_params = path_params
|
|
387
391
|
|
|
388
392
|
res_show.new(
|
|
389
393
|
push_ins.request,
|
|
390
394
|
push_ins.version,
|
|
391
|
-
|
|
395
|
+
path_params,
|
|
392
396
|
nil,
|
|
393
397
|
@context
|
|
394
398
|
)
|
|
395
399
|
yield @context.action_instance
|
|
396
400
|
ensure
|
|
397
|
-
restore_context(push_cls, push_ins, push_path)
|
|
401
|
+
restore_context(push_cls, push_ins, push_path, push_path_params)
|
|
398
402
|
end
|
|
399
403
|
|
|
400
|
-
def restore_context(action, action_instance, path)
|
|
404
|
+
def restore_context(action, action_instance, path, path_params)
|
|
401
405
|
@context.action = action
|
|
402
406
|
@context.action_instance = action_instance
|
|
403
407
|
@context.path = path
|
|
408
|
+
@context.path_params = path_params
|
|
404
409
|
end
|
|
405
410
|
|
|
406
411
|
def show_prepared?(show)
|
|
@@ -159,19 +159,20 @@ module HaveAPI::Parameters
|
|
|
159
159
|
return if record.nil? || context.nil?
|
|
160
160
|
return unless show_action.authorization
|
|
161
161
|
|
|
162
|
-
path =
|
|
163
|
-
path_params =
|
|
162
|
+
path = context.action_path_for(show_action)
|
|
163
|
+
path_params = context.path_params_for(show_action, show_action.resolve_path_params(record))
|
|
164
164
|
child_context = HaveAPI::Context.new(
|
|
165
165
|
context.server,
|
|
166
166
|
version: context.version,
|
|
167
167
|
request: context.request,
|
|
168
168
|
action: show_action,
|
|
169
169
|
path:,
|
|
170
|
-
|
|
170
|
+
path_params:,
|
|
171
|
+
input: {},
|
|
171
172
|
user: context.current_user,
|
|
172
173
|
endpoint: context.endpoint
|
|
173
174
|
)
|
|
174
|
-
action = show_action.new(context.request, context.version, path_params,
|
|
175
|
+
action = show_action.new(context.request, context.version, path_params, {}, child_context)
|
|
175
176
|
return if action.authorized?(context.current_user)
|
|
176
177
|
|
|
177
178
|
raise HaveAPI::ValidationError, 'resource not found'
|
|
@@ -3,7 +3,10 @@ require 'time'
|
|
|
3
3
|
|
|
4
4
|
module HaveAPI::Parameters
|
|
5
5
|
class Typed
|
|
6
|
-
ATTRIBUTES = %i[
|
|
6
|
+
ATTRIBUTES = %i[
|
|
7
|
+
label desc type db_name default fill clean protected load_validators
|
|
8
|
+
nullable symbolize_keys
|
|
9
|
+
].freeze
|
|
7
10
|
|
|
8
11
|
attr_reader :name, :label, :desc, :type, :default
|
|
9
12
|
|
|
@@ -79,7 +82,8 @@ module HaveAPI::Parameters
|
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
def clean(raw)
|
|
82
|
-
|
|
85
|
+
clean_raw = custom? ? normalize_custom_keys(raw) : raw
|
|
86
|
+
return validate_cleaned_value(instance_exec(clean_raw, &@clean)) if @clean
|
|
83
87
|
|
|
84
88
|
if raw.nil?
|
|
85
89
|
return nil if nullable?
|
|
@@ -110,6 +114,9 @@ module HaveAPI::Parameters
|
|
|
110
114
|
elsif @type == String || @type == Text
|
|
111
115
|
coerce_string(raw)
|
|
112
116
|
|
|
117
|
+
elsif custom?
|
|
118
|
+
clean_raw
|
|
119
|
+
|
|
113
120
|
else
|
|
114
121
|
raw
|
|
115
122
|
end
|
|
@@ -153,6 +160,31 @@ module HaveAPI::Parameters
|
|
|
153
160
|
value
|
|
154
161
|
end
|
|
155
162
|
|
|
163
|
+
def custom?
|
|
164
|
+
@type == Custom
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def normalize_custom_keys(value)
|
|
168
|
+
case value
|
|
169
|
+
when ::Hash
|
|
170
|
+
value.each_with_object({}) do |(key, inner), ret|
|
|
171
|
+
ret[normalize_custom_key(key)] = normalize_custom_keys(inner)
|
|
172
|
+
end
|
|
173
|
+
when ::Array
|
|
174
|
+
value.map { |inner| normalize_custom_keys(inner) }
|
|
175
|
+
else
|
|
176
|
+
value
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def normalize_custom_key(key)
|
|
181
|
+
if @symbolize_keys
|
|
182
|
+
key.respond_to?(:to_sym) ? key.to_sym : key.to_s.to_sym
|
|
183
|
+
else
|
|
184
|
+
key.to_s
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
156
188
|
def strip_string(value)
|
|
157
189
|
value.strip
|
|
158
190
|
rescue ArgumentError, Encoding::CompatibilityError
|
data/lib/haveapi/params.rb
CHANGED
|
@@ -167,11 +167,11 @@ module HaveAPI
|
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
# Action returns custom data.
|
|
170
|
-
def custom(name, **kwargs, &block)
|
|
171
|
-
add_param(name, apply(kwargs, type: Custom, clean: block))
|
|
170
|
+
def custom(name, symbolize_keys: false, **kwargs, &block)
|
|
171
|
+
add_param(name, apply(kwargs, type: Custom, clean: block, symbolize_keys:))
|
|
172
172
|
end
|
|
173
173
|
|
|
174
|
-
def describe(context)
|
|
174
|
+
def describe(context, metadata: false)
|
|
175
175
|
context.layout = layout
|
|
176
176
|
|
|
177
177
|
ret = { parameters: {} }
|
|
@@ -183,17 +183,7 @@ module HaveAPI
|
|
|
183
183
|
ret[:parameters][p.name] = p.describe(context)
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
-
ret[:parameters] =
|
|
187
|
-
context.authorization.filter_input(
|
|
188
|
-
@params,
|
|
189
|
-
ModelAdapters::Hash.output(context, ret[:parameters])
|
|
190
|
-
)
|
|
191
|
-
else
|
|
192
|
-
context.authorization.filter_output(
|
|
193
|
-
@params,
|
|
194
|
-
ModelAdapters::Hash.output(context, ret[:parameters])
|
|
195
|
-
)
|
|
196
|
-
end
|
|
186
|
+
ret[:parameters] = filtered_description_parameters(context, ret, metadata)
|
|
197
187
|
|
|
198
188
|
ret
|
|
199
189
|
end
|
|
@@ -293,6 +283,18 @@ module HaveAPI
|
|
|
293
283
|
|
|
294
284
|
private
|
|
295
285
|
|
|
286
|
+
def filtered_description_parameters(context, ret, metadata)
|
|
287
|
+
params = ModelAdapters::Hash.output(context, ret[:parameters])
|
|
288
|
+
|
|
289
|
+
if @direction == :input
|
|
290
|
+
context.authorization.filter_input(@params, params)
|
|
291
|
+
elsif metadata
|
|
292
|
+
context.authorization.filter_meta_output(@params, params)
|
|
293
|
+
else
|
|
294
|
+
context.authorization.filter_output(@params, params)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
296
298
|
def add_param(name, kwargs)
|
|
297
299
|
p = Parameters::Typed.new(name, kwargs)
|
|
298
300
|
|
|
@@ -111,7 +111,7 @@ module HaveAPI::Resources
|
|
|
111
111
|
loop do
|
|
112
112
|
state = @context.server.action_state.new(
|
|
113
113
|
current_user,
|
|
114
|
-
id:
|
|
114
|
+
id: path_params['action_state_id']
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
error!('action state not found') unless state.valid?
|
|
@@ -148,7 +148,7 @@ module HaveAPI::Resources
|
|
|
148
148
|
def exec
|
|
149
149
|
state = @context.server.action_state.new(
|
|
150
150
|
current_user,
|
|
151
|
-
id:
|
|
151
|
+
id: path_params['action_state_id']
|
|
152
152
|
)
|
|
153
153
|
|
|
154
154
|
return state_to_hash(state) if state.valid?
|
|
@@ -169,7 +169,7 @@ module HaveAPI::Resources
|
|
|
169
169
|
def exec
|
|
170
170
|
state = @context.server.action_state.new(
|
|
171
171
|
current_user,
|
|
172
|
-
id:
|
|
172
|
+
id: path_params['action_state_id']
|
|
173
173
|
)
|
|
174
174
|
|
|
175
175
|
error!('action state not found') unless state.valid?
|
data/lib/haveapi/server.rb
CHANGED
|
@@ -6,8 +6,9 @@ require 'haveapi/hooks'
|
|
|
6
6
|
|
|
7
7
|
module HaveAPI
|
|
8
8
|
class Server
|
|
9
|
-
attr_accessor :default_version, :action_state
|
|
10
|
-
attr_reader :root, :routes, :module_name, :auth_chain, :versions, :extensions
|
|
9
|
+
attr_accessor :default_version, :action_state, :validation_error_http_status
|
|
10
|
+
attr_reader :root, :routes, :module_name, :auth_chain, :versions, :extensions,
|
|
11
|
+
:action_state_auth
|
|
11
12
|
|
|
12
13
|
include Hookable
|
|
13
14
|
|
|
@@ -220,6 +221,19 @@ module HaveAPI
|
|
|
220
221
|
@allowed_headers = ['Content-Type']
|
|
221
222
|
@auth_chain = HaveAPI::Authentication::Chain.new(self)
|
|
222
223
|
@extensions = []
|
|
224
|
+
@action_state_auth = :backend
|
|
225
|
+
@validation_error_http_status = nil
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def action_state_auth=(mode)
|
|
229
|
+
@action_state_auth = case mode
|
|
230
|
+
when :backend, false, nil
|
|
231
|
+
:backend
|
|
232
|
+
when :required, true
|
|
233
|
+
:required
|
|
234
|
+
else
|
|
235
|
+
raise ArgumentError, "unsupported action_state_auth #{mode.inspect}"
|
|
236
|
+
end
|
|
223
237
|
end
|
|
224
238
|
|
|
225
239
|
# Include specific version `v` of API.
|
|
@@ -530,7 +544,7 @@ module HaveAPI
|
|
|
530
544
|
end
|
|
531
545
|
|
|
532
546
|
begin
|
|
533
|
-
body = raw_body.empty? ? nil : JSON.parse(raw_body
|
|
547
|
+
body = raw_body.empty? ? nil : JSON.parse(raw_body)
|
|
534
548
|
rescue JSON::ParserError
|
|
535
549
|
report_error(400, {}, 'Bad JSON syntax')
|
|
536
550
|
end
|
|
@@ -539,8 +553,8 @@ module HaveAPI
|
|
|
539
553
|
report_error(400, {}, 'JSON body must be an object')
|
|
540
554
|
end
|
|
541
555
|
|
|
542
|
-
action_params =
|
|
543
|
-
|
|
556
|
+
action_params = settings.api_server.send(:path_params, route, params)
|
|
557
|
+
action_input = body_method ? (body || {}) : request.GET
|
|
544
558
|
|
|
545
559
|
context = Context.new(
|
|
546
560
|
settings.api_server,
|
|
@@ -548,13 +562,14 @@ module HaveAPI
|
|
|
548
562
|
request: self,
|
|
549
563
|
action: route.action,
|
|
550
564
|
path: route.path,
|
|
551
|
-
|
|
565
|
+
path_params: action_params,
|
|
566
|
+
input: action_input,
|
|
552
567
|
user: current_user,
|
|
553
568
|
endpoint: true,
|
|
554
569
|
resource_path: route.resource_path
|
|
555
570
|
)
|
|
556
571
|
|
|
557
|
-
action = route.action.new(request, v, action_params,
|
|
572
|
+
action = route.action.new(request, v, action_params, action_input, context)
|
|
558
573
|
|
|
559
574
|
unless action.authorized?(current_user)
|
|
560
575
|
report_error(403, {}, 'Access denied. Insufficient permissions.')
|
|
@@ -674,10 +689,18 @@ module HaveAPI
|
|
|
674
689
|
r.describe(hash, context)
|
|
675
690
|
end
|
|
676
691
|
|
|
692
|
+
def path_for_action(version, action)
|
|
693
|
+
routes = @routes && @routes[version]
|
|
694
|
+
return unless routes
|
|
695
|
+
|
|
696
|
+
find_action_path(routes[:resources], action)
|
|
697
|
+
end
|
|
698
|
+
|
|
677
699
|
def action_state_auth_required?(route)
|
|
700
|
+
return false unless route.action.resource == HaveAPI::Resources::ActionState
|
|
678
701
|
return false if @auth_chain.empty?
|
|
679
702
|
|
|
680
|
-
|
|
703
|
+
@action_state_auth == :required
|
|
681
704
|
end
|
|
682
705
|
|
|
683
706
|
def version_prefix(v)
|
|
@@ -753,5 +776,17 @@ module HaveAPI
|
|
|
753
776
|
def do_authenticate(v, request)
|
|
754
777
|
@auth_chain.authenticate(v, request)
|
|
755
778
|
end
|
|
779
|
+
|
|
780
|
+
def find_action_path(resources, action)
|
|
781
|
+
resources.each_value do |node|
|
|
782
|
+
path = node[:actions][action]
|
|
783
|
+
return path if path
|
|
784
|
+
|
|
785
|
+
path = find_action_path(node[:resources], action)
|
|
786
|
+
return path if path
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
nil
|
|
790
|
+
end
|
|
756
791
|
end
|
|
757
792
|
end
|