trailblazer-endpoint 0.0.4 → 0.0.5
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 +6 -0
- data/Rakefile +7 -1
- data/gemfiles/rails_app.gemfile +12 -0
- data/lib/trailblazer/endpoint.rb +20 -5
- data/lib/trailblazer/endpoint/adapter.rb +30 -121
- data/lib/trailblazer/endpoint/builder.rb +1 -1
- data/lib/trailblazer/endpoint/controller.rb +203 -1
- data/lib/trailblazer/endpoint/dsl.rb +6 -3
- data/lib/trailblazer/endpoint/options.rb +13 -44
- 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 +25 -0
- data/test/docs/controller_test.rb +160 -73
- 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 +11 -9
- data/test/rails-app/Gemfile.lock +137 -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/operation/create.rb +13 -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/controllers/api/v1/songs_controller.rb +35 -0
- data/test/rails-app/app/controllers/application_controller.rb +6 -1
- data/test/rails-app/app/controllers/application_controller/api.rb +105 -0
- data/test/rails-app/app/controllers/application_controller/web.rb +30 -0
- data/test/rails-app/app/controllers/songs_controller.rb +15 -17
- data/test/rails-app/app/models/song.rb +3 -0
- data/test/rails-app/app/models/user.rb +5 -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 +8 -3
- data/test/rails-app/db/schema.rb +15 -0
- data/test/rails-app/test/controllers/api_songs_controller_test.rb +83 -0
- data/test/rails-app/test/controllers/songs_controller_test.rb +36 -144
- data/test/rails-app/test/test_helper.rb +7 -1
- data/test/test_helper.rb +0 -2
- data/trailblazer-endpoint.gemspec +1 -0
- metadata +52 -21
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5856f0724650869732af67c5b45eee354e68a606b97d86153dffa3271adc9ee1
|
4
|
+
data.tar.gz: 01504dc413db4e2dce833908d1687ba0492f157cea3a3bca58853d9615d597d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c6322150c0dad7b568a77c8dc8b4af4a48c40e6358d18b16cf57e50f3f9515bae3e38a1875f3eb48e420192dbe978a5f7631b5b35f7c1e532e58e52d3f2043a
|
7
|
+
data.tar.gz: 3ff1847a6e2e59fba1d8b3713b7069d58ea7e89be250ab79d5d87b795a5141c6a2c5dd1cc653257191d07906c66c4700fd12408b03200bdc89d87b312451cf96
|
data/.gitignore
ADDED
data/Appraisals
ADDED
data/CHANGES.md
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
# 0.0.5
|
2
|
+
|
3
|
+
* Removed `Protocol::Failure`. Until we have `Railway::End::Failure`, use a normal `Activity::End` everywhere instead of introducing our own.
|
4
|
+
* Default `with_or_etc:invoke` is `TaskWrap.invoke`.
|
5
|
+
|
1
6
|
# 0.0.4
|
2
7
|
|
3
8
|
* Use new `context-0.3.1`.
|
4
9
|
* Don't use `wtf?`.
|
10
|
+
* Don't create a `Context` anymore in `Endpoint.arguments_for`.
|
5
11
|
|
6
12
|
# 0.0.3
|
7
13
|
|
data/Rakefile
CHANGED
@@ -5,6 +5,12 @@ task :default => [:test]
|
|
5
5
|
|
6
6
|
Rake::TestTask.new(:test) do |test|
|
7
7
|
test.libs << 'test'
|
8
|
-
test.test_files = FileList['test/endpoint_test.rb', 'test/docs/*_test.rb', "test/adapter/*_test.rb"]
|
8
|
+
test.test_files = FileList['test/endpoint_test.rb', 'test/docs/*_test.rb', "test/adapter/*_test.rb", "test/config_test.rb"]
|
9
|
+
test.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new('test-rails-app') do |test|
|
13
|
+
test.libs << 'test'
|
14
|
+
test.test_files = FileList['test/rails-app/test/test_helper.rb', 'test/rails-app/test/**/*.rb']
|
9
15
|
test.verbose = true
|
10
16
|
end
|
data/lib/trailblazer/endpoint.rb
CHANGED
@@ -2,8 +2,7 @@ module Trailblazer
|
|
2
2
|
class Endpoint
|
3
3
|
# Create an {Endpoint} class with the provided adapter and protocol.
|
4
4
|
# This builder also sets up taskWrap filters around the {domain_activity} execution.
|
5
|
-
def self.build(protocol:, adapter:, domain_activity:, scope_domain_ctx: true, domain_ctx_filter: nil,
|
6
|
-
|
5
|
+
def self.build(protocol:, adapter:, domain_activity:, scope_domain_ctx: true, domain_ctx_filter: nil, protocol_block: ->(*) { Hash.new })
|
7
6
|
# special considerations around the {domain_activity} and its taskWrap:
|
8
7
|
#
|
9
8
|
# 1. domain_ctx_filter (e.g. to filter {current_user})
|
@@ -24,16 +23,21 @@ module Trailblazer
|
|
24
23
|
domain_ctx_filter_callable = [[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["endpoint.domain_ctx_filter", domain_ctx_filter]]]
|
25
24
|
extensions_options[:extensions] << Trailblazer::Activity::TaskWrap::Extension(merge: domain_ctx_filter_callable) if domain_ctx_filter
|
26
25
|
|
26
|
+
# puts Trailblazer::Developer.render(protocol)
|
27
|
+
# puts
|
28
|
+
|
27
29
|
app_protocol = Class.new(protocol) do
|
28
30
|
step(Subprocess(domain_activity), {inherit: true, id: :domain_activity, replace: :domain_activity,
|
29
31
|
|
30
32
|
# FIXME: where does this go?
|
31
33
|
}.
|
32
34
|
merge(extensions_options).
|
33
|
-
merge(instance_exec(&
|
35
|
+
merge(instance_exec(&protocol_block)) # the block is evaluated in the {Protocol} context.
|
34
36
|
)
|
35
37
|
end
|
36
38
|
|
39
|
+
# puts Trailblazer::Developer.render(app_protocol)
|
40
|
+
|
37
41
|
Class.new(adapter) do
|
38
42
|
step(Subprocess(app_protocol), {inherit: true, id: :protocol, replace: :protocol})
|
39
43
|
end # app_adapter
|
@@ -47,9 +51,19 @@ module Trailblazer
|
|
47
51
|
}
|
48
52
|
end
|
49
53
|
|
54
|
+
def self.domain_ctx_filter(variables)
|
55
|
+
->(_ctx, ((ctx, a), b)) do # taskWrap interface
|
56
|
+
variables.each do |variable|
|
57
|
+
ctx[:domain_ctx][variable] = ctx[variable]
|
58
|
+
end
|
59
|
+
|
60
|
+
[_ctx, [[ctx, a], b]]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
50
64
|
# Runtime
|
51
65
|
# Invokes the endpoint for you and runs one of the three outcome blocks.
|
52
|
-
def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:, invoke: Trailblazer::Developer.method(:wtf?)
|
66
|
+
def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:, invoke: Trailblazer::Activity::TaskWrap.method(:invoke)) # invoke: Trailblazer::Developer.method(:wtf?)
|
53
67
|
# args[1] = args[1].merge(focus_on: { variables: [:returned], steps: :invoke_workflow })
|
54
68
|
|
55
69
|
# signal, (endpoint_ctx, _ ) = Trailblazer::Developer.wtf?(activity, args)
|
@@ -78,7 +92,8 @@ module Trailblazer
|
|
78
92
|
|
79
93
|
#@ For WORKFLOW and operations. not sure this method will stay here.
|
80
94
|
def self.arguments_for(domain_ctx:, flow_options:, circuit_options: {}, **endpoint_options)
|
81
|
-
|
95
|
+
# we don't have to create the Ctx wrapping explicitly here. this is done via `:input`.
|
96
|
+
# domain_ctx = Trailblazer::Context::IndifferentAccess.build(domain_ctx, {}, [domain_ctx, flow_options], circuit_options)
|
82
97
|
|
83
98
|
[
|
84
99
|
[
|
@@ -9,20 +9,32 @@ module Trailblazer
|
|
9
9
|
|
10
10
|
|
11
11
|
module Adapter
|
12
|
-
class Web <Trailblazer::Activity::
|
12
|
+
class Web < Trailblazer::Activity::Path
|
13
13
|
_404_path = ->(*) { step :_404_status }
|
14
14
|
_401_path = ->(*) { step :_401_status }
|
15
15
|
_403_path = ->(*) { step :_403_status }
|
16
16
|
# _422_path = ->(*) { step :_422_status } # TODO: this is currently represented by the {failure} track.
|
17
17
|
|
18
|
+
# FIXME: is this really the only way to add an {End} to all this?
|
19
|
+
@state.update_sequence do |sequence:, **|
|
20
|
+
sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :fail_fast), magnetic_to: :fail_fast, id: "End.fail_fast") # TODO: rename to {protocol_failure}
|
21
|
+
sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :failure), magnetic_to: :failure, id: "End.failure")
|
22
|
+
|
23
|
+
recompile_activity!(sequence)
|
24
|
+
|
25
|
+
sequence
|
26
|
+
end
|
27
|
+
|
18
28
|
step Subprocess(Protocol), # this will get replaced
|
19
29
|
id: :protocol,
|
20
30
|
Output(:not_authorized) => Path(track_color: :not_authorized, connect_to: Id(:protocol_failure), &_403_path),
|
21
31
|
Output(:not_found) => Path(track_color: :not_found, connect_to: Id(:protocol_failure), &_404_path),
|
22
32
|
Output(:not_authenticated) => Path(track_color: :not_authenticated, connect_to: Id(:protocol_failure), &_401_path),
|
23
|
-
Output(:invalid_data) => Track(:failure) # application error, since it's usually a failed validation.
|
33
|
+
Output(:invalid_data) => Track(:failure), # application error, since it's usually a failed validation.
|
34
|
+
Output(:failure) => Track(:failure) # application error, since it's usually a failed validation.
|
35
|
+
|
36
|
+
step :protocol_failure, magnetic_to: nil, Output(:success) => Track(:fail_fast)#, Output(:failure) => Track(:fail_fast)
|
24
37
|
|
25
|
-
step :protocol_failure, magnetic_to: nil, Output(:success) => Track(:fail_fast), Output(:failure) => Track(:fail_fast)
|
26
38
|
|
27
39
|
def protocol_failure(ctx, **)
|
28
40
|
true
|
@@ -41,28 +53,34 @@ module Trailblazer
|
|
41
53
|
def _403_status(ctx, **)
|
42
54
|
ctx[:status] = 403
|
43
55
|
end
|
44
|
-
end
|
56
|
+
end # Web
|
45
57
|
|
46
58
|
class API < Web
|
47
59
|
step :_200_status, after: :protocol
|
48
60
|
|
49
|
-
def _200_status(ctx, **)
|
50
|
-
ctx[:status] =
|
61
|
+
def _200_status(ctx, success_status: 200, **)
|
62
|
+
ctx[:status] = success_status
|
51
63
|
end
|
52
64
|
|
53
|
-
|
65
|
+
step :_422_status, before: "End.failure", magnetic_to: :failure, Output(:success) => Track(:failure)
|
54
66
|
|
55
67
|
def _422_status(ctx, **)
|
56
68
|
ctx[:status] = 422
|
57
69
|
end
|
58
70
|
|
59
71
|
|
60
|
-
def self.insert_error_handler_steps(adapter)
|
72
|
+
def self.insert_error_handler_steps(adapter) # TODO: evaluate if needed?
|
61
73
|
adapter = Class.new(adapter) do
|
62
|
-
|
63
|
-
|
74
|
+
API.insert_error_handler_steps!(self)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.insert_error_handler_steps!(adapter)
|
79
|
+
adapter.instance_exec do
|
80
|
+
step :handle_not_authenticated, magnetic_to: :not_authenticated, Output(:success) => Track(:not_authenticated), before: :_401_status
|
81
|
+
step :handle_not_authorized, magnetic_to: :not_authorized, Output(:success) => Track(:not_authorized), before: :_403_status
|
64
82
|
# step :handle_not_found, magnetic_to: :not_found, Output(:success) => Track(:not_found), Output(:failure) => Track(:not_found)
|
65
|
-
|
83
|
+
step :handle_invalid_data, before: :_422_status, magnetic_to: :failure, Output(:success) => Track(:failure)
|
66
84
|
end
|
67
85
|
end
|
68
86
|
|
@@ -81,117 +99,8 @@ module Trailblazer
|
|
81
99
|
end
|
82
100
|
end
|
83
101
|
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Basic endpoint adapter for a HTTP document API.
|
87
|
-
# As always: "work in progress" ;)
|
88
|
-
#
|
89
|
-
# {End.fail_fast} currently implies a 4xx-able error.
|
90
|
-
class API_ < Trailblazer::Activity::FastTrack
|
91
|
-
_404_path = ->(*) { step :_404_status }
|
92
|
-
_401_path = ->(*) { step :_401_status; step :_401_error_message }
|
93
|
-
_403_path = ->(*) { step :_403_status }
|
94
|
-
# _422_path = ->(*) { step :_422_status } # TODO: this is currently represented by the {failure} track.
|
95
|
-
|
96
|
-
# The API Adapter automatically wires well-defined outputs for you to well-defined paths. :)
|
97
|
-
# FIXME
|
98
|
-
|
99
|
-
step Subprocess(Protocol), # this will get replaced
|
100
|
-
id: :protocol,
|
101
|
-
Output(:not_authorized) => Path(track_color: :_403, connect_to: Id(:render_protocol_failure_config), &_403_path),
|
102
|
-
Output(:not_found) => Path(track_color: :_404, connect_to: Id(:protocol_failure), &_404_path),
|
103
|
-
Output(:not_authenticated) => Path(track_color: :_401, connect_to: Id(:render_protocol_failure_config), &_401_path), # head(401), representer: Representer::Error, message: no token
|
104
|
-
Output(:invalid_data) => Track(:failure) # application error, since it's usually a failed validation.
|
105
|
-
|
106
|
-
# extensions: [Trailblazer::Activity::TaskWrap::Extension(merge: TERMINUS_HANDLER)]
|
107
|
-
# failure is automatically wired to failure, being an "application error" vs. a "protocol error (auth, etc)"
|
108
|
-
|
109
|
-
|
110
|
-
fail :failure_render_config
|
111
|
-
fail :failure_config_status
|
112
|
-
fail :render_failure
|
113
|
-
|
114
|
-
step :success_render_config
|
115
|
-
step :success_render_status
|
116
|
-
step :render_success
|
117
|
-
|
118
|
-
|
119
|
-
# DISCUSS: "protocol failure" and "application failure" should be the same path, probably?
|
120
|
-
step :render_protocol_failure_config, magnetic_to: nil, Output(:success) => Path(connect_to: Id("End.fail_fast")) do
|
121
|
-
step :render_protocol_failure
|
122
|
-
step :protocol_failure
|
123
|
-
end
|
124
|
-
|
125
|
-
=begin
|
126
|
-
render_protocol_failure_config # representer
|
127
|
-
render_protocol_failure # Representer.new
|
128
|
-
protocol_failure # true
|
129
|
-
=end
|
130
|
-
|
131
|
-
def success_render_status(ctx, **)
|
132
|
-
ctx[:status] = 200
|
133
|
-
end
|
134
|
-
|
135
|
-
def success_render_config(ctx, representer:, **)
|
136
|
-
true
|
137
|
-
end
|
138
|
-
|
139
|
-
def render_protocol_failure_config(*args)
|
140
|
-
failure_render_config(*args)
|
141
|
-
end
|
142
|
-
|
143
|
-
# ROAR
|
144
|
-
def render_success(ctx, representer:, domain_ctx:, **)
|
145
|
-
model = domain_ctx[:model]
|
146
|
-
ctx[:json] = representer.new(model).to_json # FIXME: use the same as render_failure.
|
147
|
-
end
|
148
|
-
|
149
|
-
def failure_render_config(ctx, error_representer:, **)
|
150
|
-
ctx[:representer] = error_representer
|
151
|
-
end
|
152
|
-
|
153
|
-
def failure_config_status(ctx, **)
|
154
|
-
ctx[:status] = 422
|
155
|
-
end
|
156
|
-
|
157
|
-
def protocol_failure(*args)
|
158
|
-
#failure_config(*args)
|
159
|
-
true
|
160
|
-
end
|
161
|
-
def render_protocol_failure(*args)
|
162
|
-
render_failure(*args)
|
163
|
-
end
|
164
|
-
|
165
|
-
# ROAR
|
166
|
-
def render_failure(ctx, error_representer:, errors:, **)
|
167
|
-
# render_success(*args)
|
168
|
-
ctx[:json] = error_representer.new(errors).to_json
|
169
|
-
end
|
170
|
-
# how/where would we configure each endpoint? (per action)
|
171
|
-
# class Endpoint
|
172
|
-
# representer ...
|
173
|
-
# message ...
|
174
|
-
|
175
|
-
def _401_status(ctx, **)
|
176
|
-
ctx[:status] = 401
|
177
|
-
end
|
178
|
-
|
179
|
-
def _404_status(ctx, **)
|
180
|
-
ctx[:status] = 404
|
181
|
-
end
|
182
|
-
|
183
|
-
def _403_status(ctx, **)
|
184
|
-
ctx[:status] = 403
|
185
|
-
end
|
186
|
-
|
187
|
-
def _401_error_message(ctx, **)
|
188
|
-
ctx[:error_message] = "Authentication credentials were not provided or invalid."
|
189
|
-
end
|
102
|
+
end # API
|
190
103
|
|
191
|
-
# def exec_success(ctx, success_block:, **)
|
192
|
-
# success_block.call(ctx, **ctx.to_hash) # DISCUSS: use Nested(dynamic) ?
|
193
|
-
# end
|
194
|
-
end
|
195
104
|
end
|
196
105
|
end
|
197
106
|
end
|
@@ -35,7 +35,7 @@ module Trailblazer
|
|
35
35
|
def endpoint_for(id:, builder:, default_options:, **config)
|
36
36
|
options = build_options_for(builder: builder, **config)
|
37
37
|
|
38
|
-
return id, Trailblazer::Endpoint.build(default_options.merge(options[:options_for_build])
|
38
|
+
return id, Trailblazer::Endpoint.build(default_options.merge(options[:options_for_build]).merge(protocol_block: options[:protocol_block]))
|
39
39
|
end
|
40
40
|
|
41
41
|
# {dsl_options} being something like
|
@@ -1,7 +1,209 @@
|
|
1
1
|
module Trailblazer
|
2
2
|
class Endpoint
|
3
3
|
module Controller
|
4
|
+
def self.extended(extended)
|
5
|
+
extended.extend Trailblazer::Endpoint::Options::DSL
|
6
|
+
extended.extend Trailblazer::Endpoint::Options::DSL::Inherit
|
7
|
+
extended.extend Trailblazer::Endpoint::Options
|
8
|
+
extended.extend DSL::Endpoint
|
4
9
|
|
5
|
-
|
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
|
+
def self.module(framework: :rails, api: false, dsl: false, application_controller: false)
|
21
|
+
if api
|
22
|
+
Module.new do
|
23
|
+
def self.included(includer)
|
24
|
+
includer.extend(Controller)
|
25
|
+
includer.include(InstanceMethods)
|
26
|
+
includer.include(InstanceMethods::API)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
elsif dsl and !application_controller
|
30
|
+
Module.new do
|
31
|
+
def self.included(includer)
|
32
|
+
# includer.extend Trailblazer::Endpoint::Controller
|
33
|
+
includer.include Trailblazer::Endpoint::Controller::InstanceMethods::DSL
|
34
|
+
includer.include Trailblazer::Endpoint::Controller::Rails
|
35
|
+
includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultBlocks
|
36
|
+
includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultParams
|
37
|
+
includer.include Trailblazer::Endpoint::Controller::Rails::Process
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Rails
|
44
|
+
module Process
|
45
|
+
def send_action(action_name)
|
46
|
+
puts "@@@@@>>>>>>> #{action_name.inspect}"
|
47
|
+
|
48
|
+
dsl = send(action_name) # call the actual controller action.
|
49
|
+
|
50
|
+
options, block_options = dsl.to_args(self.class.options_for(:options_for_block_options, controller: self)) # {success_block:, failure_block:, protocol_failure_block:}
|
51
|
+
# now we know the authorative blocks
|
52
|
+
|
53
|
+
Controller.advance_endpoint_for_controller(**options, block_options: block_options, config_source: self.class, controller: self)
|
54
|
+
end
|
55
|
+
|
56
|
+
end # Process
|
57
|
+
|
58
|
+
# The three default handlers for {Endpoint::with_or_etc}
|
59
|
+
# @experimental
|
60
|
+
module DefaultBlocks
|
61
|
+
def self.extended(extended)
|
62
|
+
extended.directive :options_for_block_options, Controller.method(:options_for_block_options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# @experimental
|
66
|
+
module DefaultParams
|
67
|
+
def self.extended(extended)
|
68
|
+
extended.directive :options_for_domain_ctx, ->(ctx, controller:, **) { {params: controller.params} }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end # Rails
|
73
|
+
|
74
|
+
module DSL
|
75
|
+
module Endpoint
|
76
|
+
def self.extended(extended)
|
77
|
+
extended.directive(:endpoints, ->(*) { {} })
|
78
|
+
end
|
79
|
+
|
80
|
+
def endpoint(name, **options, &block)
|
81
|
+
options = options.merge(protocol_block: block) if block_given?
|
82
|
+
|
83
|
+
return generic_endpoint_config(**name, **options) if name.is_a?(Hash)
|
84
|
+
endpoint_config(name, **options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def generic_endpoint_config(protocol:, adapter:, **options)
|
88
|
+
self.singleton_class.define_method :generic_options do |ctx,**|
|
89
|
+
{
|
90
|
+
protocol: protocol,
|
91
|
+
adapter: adapter,
|
92
|
+
**options
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
directive :generic_options, method(:generic_options) # FIXME: do we need this?
|
97
|
+
end
|
98
|
+
|
99
|
+
def endpoint_config(name, **options)
|
100
|
+
puts "~~~~~~~~~~~~~~~config"
|
101
|
+
build_options = options_for(:generic_options, {}).merge(options) # DISCUSS: why don't we add this as another directive option/step?
|
102
|
+
|
103
|
+
endpoint = Trailblazer::Endpoint.build(build_options)
|
104
|
+
|
105
|
+
directive :endpoints, ->(*) { {name => endpoint} }
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
module InstanceMethods
|
112
|
+
|
113
|
+
def endpoint_for(name, config_source: self.class)
|
114
|
+
config_source.options_for(:endpoints, {}).fetch(name) # TODO: test non-existant endpoint
|
115
|
+
end
|
116
|
+
|
117
|
+
module DSL
|
118
|
+
def endpoint(name, **action_options, &block)
|
119
|
+
action_options = {controller: self}.merge(action_options) # FIXME: redundant with {DSL#endpoint}
|
120
|
+
|
121
|
+
endpoint = endpoint_for(name)
|
122
|
+
|
123
|
+
invoke_endpoint_with_dsl(endpoint: endpoint, **action_options, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def invoke_endpoint_with_dsl(options, &block)
|
127
|
+
_dsl = Trailblazer::Endpoint::DSL::Runtime.new(options, block) # provides #Or etc, is returned to {Controller#call}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
module API
|
132
|
+
def endpoint(name, config_source: self.class, **action_options)
|
133
|
+
endpoint = endpoint_for(name, config_source: config_source)
|
134
|
+
|
135
|
+
action_options = {controller: self}.merge(action_options) # FIXME: redundant with {DSL#endpoint}
|
136
|
+
|
137
|
+
block_options = config_source.options_for(:options_for_block_options, **action_options)
|
138
|
+
block_options = Trailblazer::Endpoint::Options.merge_with(action_options, block_options)
|
139
|
+
|
140
|
+
signal, (ctx, _) = Trailblazer::Endpoint::Controller.advance_endpoint_for_controller(
|
141
|
+
endpoint: endpoint,
|
142
|
+
block_options: block_options,
|
143
|
+
config_source: config_source,
|
144
|
+
**action_options
|
145
|
+
)
|
146
|
+
|
147
|
+
ctx
|
148
|
+
end
|
149
|
+
end # API
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
def self.advance_endpoint_for_controller(endpoint:, block_options:, **action_options)
|
154
|
+
domain_ctx, endpoint_options, flow_options = compile_options_for_controller(**action_options) # controller-specific, get from directives.
|
155
|
+
|
156
|
+
endpoint_options = endpoint_options.merge(action_options) # DISCUSS
|
157
|
+
|
158
|
+
Endpoint::Controller.advance_endpoint(
|
159
|
+
endpoint: endpoint,
|
160
|
+
block_options: block_options,
|
161
|
+
|
162
|
+
domain_ctx: domain_ctx,
|
163
|
+
endpoint_options: endpoint_options,
|
164
|
+
flow_options: flow_options,
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.compile_options_for_controller(options_for_domain_ctx: nil, config_source:, **action_options)
|
169
|
+
flow_options = config_source.options_for(:options_for_flow_options, **action_options)
|
170
|
+
endpoint_options = config_source.options_for(:options_for_endpoint, **action_options) # "class level"
|
171
|
+
domain_ctx = options_for_domain_ctx || config_source.options_for(:options_for_domain_ctx, **action_options)
|
172
|
+
|
173
|
+
return domain_ctx, endpoint_options, flow_options
|
174
|
+
end
|
175
|
+
|
176
|
+
# Ultimate low-level entry point.
|
177
|
+
# Remember that you don't _have_ to use Endpoint.with_or_etc to invoke an endpoint.
|
178
|
+
def self.advance_endpoint(endpoint:, block_options:, domain_ctx:, endpoint_options:, flow_options:)
|
179
|
+
|
180
|
+
# build Context(ctx),
|
181
|
+
args, _ = Trailblazer::Endpoint.arguments_for(
|
182
|
+
domain_ctx: domain_ctx,
|
183
|
+
flow_options: flow_options,
|
184
|
+
**endpoint_options,
|
185
|
+
)
|
186
|
+
|
187
|
+
signal, (ctx, _ ) = Trailblazer::Endpoint.with_or_etc(
|
188
|
+
endpoint,
|
189
|
+
args, # [ctx, flow_options]
|
190
|
+
|
191
|
+
**block_options,
|
192
|
+
# success_block: success_block,
|
193
|
+
# failure_block: failure_block,
|
194
|
+
# protocol_failure_block: protocol_failure_block,
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Default blocks for the {Adapter}.
|
199
|
+
def self.options_for_block_options(ctx, controller:, **)
|
200
|
+
{
|
201
|
+
success_block: ->(ctx, endpoint_ctx:, **) { controller.head 200 },
|
202
|
+
failure_block: ->(ctx, **) { controller.head 422 },
|
203
|
+
protocol_failure_block: ->(ctx, endpoint_ctx:, **) { controller.head endpoint_ctx[:status] }
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
end # Controller
|
6
208
|
end
|
7
209
|
end
|