trailblazer-endpoint 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|