trailblazer-endpoint 0.0.1 → 0.0.6
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/.gitignore +16 -0
- data/Appraisals +5 -0
- data/CHANGES.md +26 -0
- data/README.md +40 -5
- data/Rakefile +7 -1
- data/gemfiles/rails_app.gemfile +12 -0
- data/lib/trailblazer/endpoint.rb +29 -14
- data/lib/trailblazer/endpoint/adapter.rb +30 -121
- data/lib/trailblazer/endpoint/builder.rb +1 -1
- data/lib/trailblazer/endpoint/controller.rb +223 -0
- data/lib/trailblazer/endpoint/dsl.rb +29 -0
- data/lib/trailblazer/endpoint/options.rb +92 -0
- data/lib/trailblazer/endpoint/protocol.rb +5 -8
- data/lib/trailblazer/endpoint/version.rb +1 -1
- data/test/adapter/api_test.rb +6 -11
- data/test/adapter/web_test.rb +2 -5
- data/test/config_test.rb +128 -0
- data/test/docs/controller_test.rb +339 -58
- data/test/endpoint_test.rb +1 -1
- data/test/rails-app/.gitignore +8 -2
- data/test/rails-app/.ruby-version +1 -0
- data/test/rails-app/Gemfile +15 -9
- data/test/rails-app/Gemfile.lock +162 -121
- data/test/rails-app/app/concepts/app/api/v1/representer/errors.rb +16 -0
- data/test/rails-app/app/concepts/auth/jwt.rb +35 -0
- data/test/rails-app/app/concepts/auth/operation/authenticate.rb +32 -0
- data/test/rails-app/app/concepts/auth/operation/policy.rb +9 -0
- data/test/rails-app/app/concepts/song/cell/create.rb +4 -0
- data/test/rails-app/app/concepts/song/cell/new.rb +4 -0
- data/test/rails-app/app/concepts/song/operation/create.rb +17 -0
- data/test/rails-app/app/concepts/song/operation/show.rb +10 -0
- data/test/rails-app/app/concepts/song/representer.rb +5 -0
- data/test/rails-app/app/concepts/song/view/create.erb +1 -0
- data/test/rails-app/app/concepts/song/view/new.erb +1 -0
- data/test/rails-app/app/controllers/api/v1/songs_controller.rb +41 -0
- data/test/rails-app/app/controllers/application_controller.rb +8 -1
- data/test/rails-app/app/controllers/application_controller/api.rb +107 -0
- data/test/rails-app/app/controllers/application_controller/web.rb +44 -0
- data/test/rails-app/app/controllers/auth_controller.rb +44 -0
- data/test/rails-app/app/controllers/home_controller.rb +5 -0
- data/test/rails-app/app/controllers/songs_controller.rb +71 -13
- data/test/rails-app/app/models/song.rb +3 -0
- data/test/rails-app/app/models/user.rb +7 -0
- data/test/rails-app/bin/bundle +114 -0
- data/test/rails-app/bin/rails +4 -0
- data/test/rails-app/bin/rake +4 -0
- data/test/rails-app/bin/setup +33 -0
- data/test/rails-app/config/application.rb +26 -3
- data/test/rails-app/config/credentials.yml.enc +1 -0
- data/test/rails-app/config/database.yml +2 -2
- data/test/rails-app/config/environments/development.rb +7 -17
- data/test/rails-app/config/environments/production.rb +28 -23
- data/test/rails-app/config/environments/test.rb +8 -12
- data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -4
- data/test/rails-app/config/initializers/cors.rb +16 -0
- data/test/rails-app/config/initializers/trailblazer.rb +2 -0
- data/test/rails-app/config/locales/en.yml +11 -1
- data/test/rails-app/config/master.key +1 -0
- data/test/rails-app/config/routes.rb +16 -4
- data/test/rails-app/db/schema.rb +15 -0
- data/test/rails-app/test/controllers/api_songs_controller_test.rb +87 -0
- data/test/rails-app/test/controllers/songs_controller_test.rb +80 -147
- data/test/rails-app/test/test_helper.rb +7 -1
- data/test/test_helper.rb +0 -2
- data/trailblazer-endpoint.gemspec +2 -1
- metadata +70 -22
- data/test/rails-app/config/initializers/cookies_serializer.rb +0 -5
- data/test/rails-app/config/initializers/new_framework_defaults.rb +0 -24
- data/test/rails-app/config/initializers/session_store.rb +0 -3
- data/test/rails-app/config/secrets.yml +0 -22
- data/test/rails-app/test/helpers/.keep +0 -0
- data/test/rails-app/test/integration/.keep +0 -0
- data/test/rails-app/test/mailers/.keep +0 -0
- data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
- data/test/rails-app/vendor/assets/stylesheets/.keep +0 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Endpoint
|
3
|
+
module Controller
|
4
|
+
def self.extended(extended)
|
5
|
+
extended.extend Trailblazer::Endpoint::Options::DSL # ::directive
|
6
|
+
extended.extend Trailblazer::Endpoint::Options::DSL::Inherit
|
7
|
+
extended.extend Trailblazer::Endpoint::Options # ::options_for
|
8
|
+
extended.extend DSL::Endpoint
|
9
|
+
|
10
|
+
extended.include InstanceMethods # {#endpoint_for}
|
11
|
+
|
12
|
+
# DISCUSS: hmm
|
13
|
+
extended.directive :generic_options, ->(*) { Hash.new } # for Controller::endpoint
|
14
|
+
extended.directive :options_for_flow_options, ->(*) { Hash.new }
|
15
|
+
extended.directive :options_for_endpoint, ->(*) { Hash.new }
|
16
|
+
extended.directive :options_for_domain_ctx, ->(*) { Hash.new }
|
17
|
+
end
|
18
|
+
|
19
|
+
# @experimental
|
20
|
+
# TODO: test application_controller with and without dsl/api
|
21
|
+
|
22
|
+
def self.module(framework: :rails, api: false, dsl: false, application_controller: false)
|
23
|
+
if application_controller && !api && !dsl # FIXME: not tested! this is useful for an actual AppController with block_options or flow_options settings, "globally"
|
24
|
+
Module.new do
|
25
|
+
def self.included(includer)
|
26
|
+
includer.extend(Controller) # only ::directive and friends.
|
27
|
+
end
|
28
|
+
end
|
29
|
+
elsif api
|
30
|
+
Module.new do
|
31
|
+
@application_controller = application_controller
|
32
|
+
def self.included(includer)
|
33
|
+
if @application_controller
|
34
|
+
includer.extend Controller
|
35
|
+
end
|
36
|
+
includer.include(InstanceMethods::API)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
elsif dsl
|
40
|
+
Module.new do
|
41
|
+
@application_controller = application_controller
|
42
|
+
def self.included(includer)
|
43
|
+
if @application_controller
|
44
|
+
includer.extend Controller
|
45
|
+
end
|
46
|
+
includer.include Trailblazer::Endpoint::Controller::InstanceMethods::DSL
|
47
|
+
includer.include Trailblazer::Endpoint::Controller::Rails
|
48
|
+
includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultBlocks
|
49
|
+
includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultParams
|
50
|
+
includer.include Trailblazer::Endpoint::Controller::Rails::Process
|
51
|
+
end
|
52
|
+
end # Module
|
53
|
+
else
|
54
|
+
raise
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module Rails
|
59
|
+
module Process
|
60
|
+
def send_action(action_name)
|
61
|
+
puts "@@@@@>>>>>>> #{action_name.inspect}"
|
62
|
+
|
63
|
+
dsl = send(action_name) # call the actual controller action.
|
64
|
+
|
65
|
+
options, block_options = dsl.to_args(self.class.options_for(:options_for_block_options, controller: self)) # {success_block:, failure_block:, protocol_failure_block:}
|
66
|
+
# now we know the authorative blocks
|
67
|
+
|
68
|
+
Controller.advance_endpoint_for_controller(**options, block_options: block_options, config_source: self.class, controller: self)
|
69
|
+
end
|
70
|
+
|
71
|
+
end # Process
|
72
|
+
|
73
|
+
# The three default handlers for {Endpoint::with_or_etc}
|
74
|
+
# @experimental
|
75
|
+
module DefaultBlocks
|
76
|
+
def self.extended(extended)
|
77
|
+
extended.directive :options_for_block_options, Controller.method(:options_for_block_options)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
# @experimental
|
81
|
+
module DefaultParams
|
82
|
+
def self.extended(extended)
|
83
|
+
extended.directive :options_for_domain_ctx, ->(ctx, controller:, **) { {params: controller.params} }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end # Rails
|
88
|
+
|
89
|
+
module DSL
|
90
|
+
module Endpoint
|
91
|
+
def self.extended(extended)
|
92
|
+
extended.directive(:endpoints, ->(*) { {} })
|
93
|
+
end
|
94
|
+
|
95
|
+
def endpoint(name, **options, &block)
|
96
|
+
options = options.merge(protocol_block: block) if block_given?
|
97
|
+
|
98
|
+
return generic_endpoint_config(**name, **options) if name.is_a?(Hash)
|
99
|
+
endpoint_config(name, **options)
|
100
|
+
end
|
101
|
+
|
102
|
+
def generic_endpoint_config(protocol:, adapter:, **options)
|
103
|
+
self.singleton_class.define_method :generic_options do |ctx,**|
|
104
|
+
{
|
105
|
+
protocol: protocol,
|
106
|
+
adapter: adapter,
|
107
|
+
**options
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
directive :generic_options, method(:generic_options) # FIXME: do we need this?
|
112
|
+
end
|
113
|
+
|
114
|
+
def endpoint_config(name, domain_activity: name, **options)
|
115
|
+
build_options = options_for(:generic_options, {}).merge(domain_activity: domain_activity, **options) # DISCUSS: why don't we add this as another directive option/step?
|
116
|
+
|
117
|
+
endpoint = Trailblazer::Endpoint.build(build_options)
|
118
|
+
|
119
|
+
directive :endpoints, ->(*) { {name.to_s => endpoint} }
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module InstanceMethods
|
126
|
+
|
127
|
+
def endpoint_for(name, config_source: self.class)
|
128
|
+
config_source.options_for(:endpoints, {}).fetch(name.to_s) # TODO: test non-existant endpoint
|
129
|
+
end
|
130
|
+
|
131
|
+
module DSL
|
132
|
+
def endpoint(name, **action_options, &block)
|
133
|
+
action_options = {controller: self}.merge(action_options) # FIXME: redundant with {API#endpoint}
|
134
|
+
|
135
|
+
endpoint = endpoint_for(name)
|
136
|
+
|
137
|
+
invoke_endpoint_with_dsl(endpoint: endpoint, **action_options, &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
def invoke_endpoint_with_dsl(options, &block)
|
141
|
+
_dsl = Trailblazer::Endpoint::DSL::Runtime.new(options, block) # provides #Or etc, is returned to {Controller#call}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
module API
|
146
|
+
def endpoint(name, config_source: self.class, **action_options)
|
147
|
+
endpoint = endpoint_for(name, config_source: config_source)
|
148
|
+
|
149
|
+
action_options = {controller: self}.merge(action_options) # FIXME: redundant with {InstanceMethods#endpoint}
|
150
|
+
|
151
|
+
block_options = config_source.options_for(:options_for_block_options, **action_options)
|
152
|
+
block_options = Trailblazer::Endpoint::Options.merge_with(action_options, block_options)
|
153
|
+
|
154
|
+
signal, (ctx, _) = Trailblazer::Endpoint::Controller.advance_endpoint_for_controller(
|
155
|
+
endpoint: endpoint,
|
156
|
+
block_options: block_options,
|
157
|
+
config_source: config_source,
|
158
|
+
**action_options
|
159
|
+
)
|
160
|
+
|
161
|
+
ctx
|
162
|
+
end
|
163
|
+
end # API
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def self.advance_endpoint_for_controller(endpoint:, block_options:, **action_options)
|
168
|
+
domain_ctx, endpoint_options, flow_options = compile_options_for_controller(**action_options) # controller-specific, get from directives.
|
169
|
+
|
170
|
+
endpoint_options = endpoint_options.merge(action_options) # DISCUSS
|
171
|
+
|
172
|
+
Endpoint::Controller.advance_endpoint(
|
173
|
+
endpoint: endpoint,
|
174
|
+
block_options: block_options,
|
175
|
+
|
176
|
+
domain_ctx: domain_ctx,
|
177
|
+
endpoint_options: endpoint_options,
|
178
|
+
flow_options: flow_options,
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.compile_options_for_controller(options_for_domain_ctx: nil, config_source:, **action_options)
|
183
|
+
flow_options = config_source.options_for(:options_for_flow_options, **action_options)
|
184
|
+
endpoint_options = config_source.options_for(:options_for_endpoint, **action_options) # "class level"
|
185
|
+
domain_ctx = options_for_domain_ctx || config_source.options_for(:options_for_domain_ctx, **action_options)
|
186
|
+
|
187
|
+
return domain_ctx, endpoint_options, flow_options
|
188
|
+
end
|
189
|
+
|
190
|
+
# Ultimate low-level entry point.
|
191
|
+
# Remember that you don't _have_ to use Endpoint.with_or_etc to invoke an endpoint.
|
192
|
+
def self.advance_endpoint(endpoint:, block_options:, domain_ctx:, endpoint_options:, flow_options:)
|
193
|
+
|
194
|
+
# build Context(ctx),
|
195
|
+
args, _ = Trailblazer::Endpoint.arguments_for(
|
196
|
+
domain_ctx: domain_ctx,
|
197
|
+
flow_options: flow_options,
|
198
|
+
**endpoint_options,
|
199
|
+
)
|
200
|
+
|
201
|
+
signal, (ctx, _ ) = Trailblazer::Endpoint.with_or_etc(
|
202
|
+
endpoint,
|
203
|
+
args, # [ctx, flow_options]
|
204
|
+
|
205
|
+
**block_options,
|
206
|
+
# success_block: success_block,
|
207
|
+
# failure_block: failure_block,
|
208
|
+
# protocol_failure_block: protocol_failure_block,
|
209
|
+
)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Default blocks for the {Adapter}.
|
213
|
+
def self.options_for_block_options(ctx, controller:, **)
|
214
|
+
{
|
215
|
+
success_block: ->(ctx, endpoint_ctx:, **) { controller.head 200 },
|
216
|
+
failure_block: ->(ctx, **) { controller.head 422 },
|
217
|
+
protocol_failure_block: ->(ctx, endpoint_ctx:, **) { controller.head endpoint_ctx[:status] }
|
218
|
+
}
|
219
|
+
end
|
220
|
+
|
221
|
+
end # Controller
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Endpoint
|
3
|
+
module DSL
|
4
|
+
# Run before the endpoint is invoked. This collects the blocks from the controller.
|
5
|
+
class Runtime < Struct.new(:options, :success_block, :failure_block, :protocol_failure_block)
|
6
|
+
|
7
|
+
def failure(&block)
|
8
|
+
self.failure_block = block
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
alias_method :Or, :failure
|
13
|
+
|
14
|
+
def protocol_failure(&block)
|
15
|
+
self.protocol_failure_block = block
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# #call
|
20
|
+
def to_args(default_block_options)
|
21
|
+
return options,
|
22
|
+
success_block: success_block || default_block_options[:success_block],
|
23
|
+
failure_block: failure_block || default_block_options[:failure_block],
|
24
|
+
protocol_failure_block: protocol_failure_block || default_block_options[:protocol_failure_block]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# DISCUSS: the generic inheritance/Options logic might be extracted to trailblazer-config or something.
|
2
|
+
# it is completely independent and could be helpful for many other configurations.
|
3
|
+
module Trailblazer
|
4
|
+
class Endpoint
|
5
|
+
module Options
|
6
|
+
module DSL
|
7
|
+
def directive(directive_name, *callables, inherit: superclass)
|
8
|
+
options = {}
|
9
|
+
|
10
|
+
if inherit
|
11
|
+
options[:base_class] = instance_variable_get(:@normalizers)[directive_name] || Trailblazer::Activity::Path # FIXME
|
12
|
+
end
|
13
|
+
|
14
|
+
@normalizers[directive_name] = Trailblazer::Endpoint::Normalizer.Options(directive_name, *callables, **options) # DISCUSS: allow multiple calls?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Called in {Endpoint::Controller}.
|
18
|
+
def self.extended(extended) # TODO: let's hope this is only called once per hierachy :)
|
19
|
+
extended.instance_variable_set(:@normalizers, {})
|
20
|
+
end
|
21
|
+
|
22
|
+
module Inherit
|
23
|
+
def inherited(subclass)
|
24
|
+
super
|
25
|
+
|
26
|
+
subclass.instance_variable_set(:@normalizers, @normalizers.dup)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def options_for(directive_name, runtime_options)
|
32
|
+
normalizer = @normalizers.fetch(directive_name)
|
33
|
+
|
34
|
+
ctx = Trailblazer::Context(runtime_options, {})
|
35
|
+
|
36
|
+
# signal, (ctx, ) = Trailblazer::Developer.wtf?(normalizer, [ctx])
|
37
|
+
signal, (ctx, ) = Trailblazer::Activity::TaskWrap.invoke(normalizer, [ctx])
|
38
|
+
|
39
|
+
_, options = ctx.decompose
|
40
|
+
options
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Merge {merged} into {hash}, but only keys that exist in {hash}.
|
45
|
+
def self.merge_with(merged, hash)
|
46
|
+
keys = hash.keys
|
47
|
+
merged = keys.collect { |key| merged.key?(key) ? [key, merged[key]] : nil }.compact.to_h
|
48
|
+
hash.merge(merged)
|
49
|
+
end
|
50
|
+
end # Options
|
51
|
+
|
52
|
+
module Normalizer
|
53
|
+
def self.Options(directive_name, *callables, base_class: Trailblazer::Activity::Path)
|
54
|
+
normalizer = Class.new(base_class) do
|
55
|
+
end
|
56
|
+
|
57
|
+
Normalizer.add(normalizer, directive_name, callables)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.DefaultToEmptyHash(config_name)
|
61
|
+
-> (ctx, **) { ctx[config_name] ||= {} }
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.add_normalizer!(target, normalizer, config)
|
65
|
+
normalizer = Normalizer.add(normalizer, target, config) # add configure steps for {subclass} to the _new_ normalizer.
|
66
|
+
target.instance_variable_set(:@normalizer, normalizer)
|
67
|
+
target.instance_variable_set(:@config, config)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.add(normalizer, directive_name, options)
|
71
|
+
Class.new(normalizer) do
|
72
|
+
options.collect do |callable|
|
73
|
+
step task: Normalizer.CallDirective(callable, directive_name), id: "#{directive_name}=>#{callable}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.CallDirective(callable, option_name)
|
79
|
+
->((ctx, flow_options), *) {
|
80
|
+
config = callable.(ctx, **ctx) # e.g. ApplicationController.options_for_endpoint
|
81
|
+
|
82
|
+
# ctx[option_name] = ctx[option_name].merge(config)
|
83
|
+
config.each do |k, v|
|
84
|
+
ctx[k] = v
|
85
|
+
end
|
86
|
+
|
87
|
+
return Trailblazer::Activity::Right, [ctx, flow_options]
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end # Normalizer
|
91
|
+
end
|
92
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "trailblazer/activity/dsl/linear"
|
2
|
+
|
1
3
|
module Trailblazer
|
2
4
|
class Endpoint
|
3
5
|
# The {Protocol} implements auth*, and calls the domain OP/WF.
|
@@ -16,13 +18,8 @@ module Trailblazer
|
|
16
18
|
class Noop < Trailblazer::Activity::Railway
|
17
19
|
end
|
18
20
|
|
19
|
-
class Failure < Trailblazer::Activity::End # DISCUSS: move to Act::Railway?
|
20
|
-
# class Authentication < Failure
|
21
|
-
# end
|
22
|
-
end
|
23
|
-
|
24
21
|
def self._Path(semantic:, &block) # DISCUSS: the problem with Path currently is https://github.com/trailblazer/trailblazer-activity-dsl-linear/issues/27
|
25
|
-
Path(track_color: semantic, end_id: "End.#{semantic}", end_task:
|
22
|
+
Path(track_color: semantic, end_id: "End.#{semantic}", end_task: Activity::End.new(semantic: semantic), &block)
|
26
23
|
end
|
27
24
|
|
28
25
|
step :authenticate, Output(:failure) => _Path(semantic: :not_authenticated) do
|
@@ -43,8 +40,8 @@ module Trailblazer
|
|
43
40
|
# termini for the Adapter this is the only way to get it working right now.
|
44
41
|
# FIXME: is this really the only way to add an {End} to all this?
|
45
42
|
@state.update_sequence do |sequence:, **|
|
46
|
-
sequence = Activity::Path::DSL.append_end(sequence, task:
|
47
|
-
sequence = Activity::Path::DSL.append_end(sequence, task:
|
43
|
+
sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :not_found), magnetic_to: :not_found, id: "End.not_found")
|
44
|
+
sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :invalid_data), magnetic_to: :invalid_data, id: "End.invalid_data")
|
48
45
|
|
49
46
|
recompile_activity!(sequence)
|
50
47
|
|
data/test/adapter/api_test.rb
CHANGED
@@ -12,14 +12,13 @@ class AdapterAPITest < Minitest::Spec
|
|
12
12
|
protocol: protocol, # do we cover all usual routes?
|
13
13
|
adapter: Trailblazer::Endpoint::Adapter::API,
|
14
14
|
scope_domain_ctx: false,
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
{Output(:not_found) => Track(:not_found)}
|
19
|
-
end
|
15
|
+
protocol_block: -> { {Output(:not_found) => Track(:not_found)} }
|
16
|
+
)
|
20
17
|
|
21
18
|
# success
|
22
19
|
assert_route endpoint, {}, :authenticate, :policy, :model, :validate, :success, status: 200
|
20
|
+
# created, when passing 201
|
21
|
+
assert_route endpoint, {success_status: 201}, :authenticate, :policy, :model, :validate, :success, status: 201
|
23
22
|
# authentication error
|
24
23
|
assert_route endpoint, {authenticate: false}, :authenticate, :fail_fast, status: 401 # fail_fast == protocol error
|
25
24
|
# policy error
|
@@ -47,12 +46,8 @@ class AdapterAPITest < Minitest::Spec
|
|
47
46
|
protocol: protocol, # do we cover all usual routes?
|
48
47
|
adapter: adapter,
|
49
48
|
scope_domain_ctx: false,
|
50
|
-
|
51
|
-
)
|
52
|
-
|
53
|
-
|
54
|
-
{Output(:not_found) => Track(:not_found)}
|
55
|
-
end
|
49
|
+
protocol_block: -> { {Output(:not_found) => Track(:not_found)} }
|
50
|
+
)
|
56
51
|
|
57
52
|
class TestErrors < Struct.new(:message)
|
58
53
|
def ==(b)
|
data/test/adapter/web_test.rb
CHANGED
@@ -19,11 +19,8 @@ class AdapterWebTest < Minitest::Spec
|
|
19
19
|
protocol: protocol, # do we cover all usual routes?
|
20
20
|
adapter: Trailblazer::Endpoint::Adapter::Web,
|
21
21
|
scope_domain_ctx: false,
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
{Output(:not_found) => Track(:not_found)}
|
26
|
-
end
|
22
|
+
protocol_block: -> { {Output(:not_found) => Track(:not_found)} }
|
23
|
+
)
|
27
24
|
|
28
25
|
# success
|
29
26
|
assert_route(endpoint, {}, :authenticate, :policy, :model, :validate, :success)
|
data/test/config_test.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "trailblazer/endpoint/options"
|
4
|
+
|
5
|
+
class ConfigTest < Minitest::Spec
|
6
|
+
Controller = Struct.new(:params)
|
7
|
+
|
8
|
+
it "what" do
|
9
|
+
ApplicationController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
|
10
|
+
|
11
|
+
# inherits endpoint options from ApplicationController
|
12
|
+
ApeController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
|
13
|
+
# defines its own domain options, none in ApplicationController
|
14
|
+
ApeController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
|
15
|
+
|
16
|
+
# 3-rd level, inherit everything from 2-nd level
|
17
|
+
ApeBabeController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
|
18
|
+
ApeBabeController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
|
19
|
+
|
20
|
+
# 4t-h level, also inherit everything from 2-nd level
|
21
|
+
ApeBabeKidController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
|
22
|
+
ApeBabeKidController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
|
23
|
+
|
24
|
+
BoringController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true, :xml=>"<XML"}}
|
25
|
+
BoringController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:policy=>\"Ehm\"}}
|
26
|
+
|
27
|
+
OverridingController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:redis=>\"Arrr\"}}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises helpful exception with unknown directive" do
|
31
|
+
assert_raises KeyError do
|
32
|
+
ApplicationController.options_for(:unknown_options, {})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ApplicationController
|
37
|
+
def self.options_for_endpoint(ctx, **)
|
38
|
+
{
|
39
|
+
find_process_model: true,
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.request_options(ctx, **)
|
44
|
+
{
|
45
|
+
request: true,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
extend Trailblazer::Endpoint::Controller
|
50
|
+
directive :options_for_endpoint, method(:options_for_endpoint), method(:request_options)
|
51
|
+
end
|
52
|
+
|
53
|
+
class ApeController < ApplicationController
|
54
|
+
def self.options_for_domain_ctx(ctx, **)
|
55
|
+
{
|
56
|
+
current_user: "Yo",
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
directive :options_for_domain_ctx, method(:options_for_domain_ctx)
|
61
|
+
end
|
62
|
+
|
63
|
+
class ApeBabeController < ApeController
|
64
|
+
# def self.options_for_domain_ctx(ctx, **)
|
65
|
+
# {policy: "Ehm"}
|
66
|
+
# end
|
67
|
+
|
68
|
+
# directive :options_for_domain_ctx, method(:options_for_domain_ctx)
|
69
|
+
end
|
70
|
+
|
71
|
+
class ApeBabeKidController < ApeController
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class BoringController < ApplicationController
|
76
|
+
def self.options_for_domain_ctx(ctx, **) {policy: "Ehm",} end
|
77
|
+
def self.options_for_endpoint(ctx, **) {xml: "<XML",} end
|
78
|
+
|
79
|
+
directive :options_for_endpoint, method(:options_for_endpoint) #, inherit: ApplicationController
|
80
|
+
directive :options_for_domain_ctx, method(:options_for_domain_ctx)
|
81
|
+
end
|
82
|
+
|
83
|
+
class OverridingController < BoringController
|
84
|
+
def self.options_for_domain_ctx(ctx, **)
|
85
|
+
{
|
86
|
+
redis: "Arrr",
|
87
|
+
}
|
88
|
+
end
|
89
|
+
directive :options_for_domain_ctx, method(:options_for_domain_ctx), inherit: false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class RuntimeOptionsTest < Minitest::Spec
|
94
|
+
class ApplicationController
|
95
|
+
def self.options_for_endpoint(ctx, controller:, **)
|
96
|
+
{
|
97
|
+
option: true,
|
98
|
+
params: controller[:params],
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
# You can access variables set prior to this options directive.
|
103
|
+
def self.request_options(ctx, controller:, params:, **)
|
104
|
+
{
|
105
|
+
my_params: params.inspect,
|
106
|
+
option: nil,
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
extend Trailblazer::Endpoint::Controller
|
111
|
+
directive :options_for_endpoint, method(:options_for_endpoint), method(:request_options)
|
112
|
+
end
|
113
|
+
|
114
|
+
it do
|
115
|
+
ApplicationController.options_for(:options_for_endpoint, controller: {params: {id: 1}}).inspect.must_equal %{{:option=>nil, :params=>{:id=>1}, :my_params=>\"{:id=>1}\"}}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class OptionsTest < Minitest::Spec
|
120
|
+
# Options#merge_with
|
121
|
+
it "works with empty {merged}" do
|
122
|
+
Trailblazer::Endpoint::Options.merge_with({}, {a: 1, b: 2}).inspect.must_equal %{{:a=>1, :b=>2}}
|
123
|
+
end
|
124
|
+
|
125
|
+
it "keys in merged have precedence, but unknown {merged} keys are discarded" do
|
126
|
+
Trailblazer::Endpoint::Options.merge_with({a: 3, d: 4}, {a: 1, b: 2}).inspect.must_equal %{{:a=>3, :b=>2}}
|
127
|
+
end
|
128
|
+
end
|