trailblazer-endpoint 0.0.2 → 0.0.7
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 +28 -0
- data/README.md +40 -5
- data/Rakefile +7 -1
- data/gemfiles/rails_app.gemfile +12 -0
- data/lib/trailblazer/endpoint.rb +55 -16
- 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 +31 -0
- data/lib/trailblazer/endpoint/options.rb +92 -0
- data/lib/trailblazer/endpoint/protocol.rb +7 -11
- data/lib/trailblazer/endpoint/protocol/cipher.rb +27 -0
- data/lib/trailblazer/endpoint/protocol/controller.rb +93 -0
- data/lib/trailblazer/endpoint/protocol/find_process_model.rb +15 -0
- 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 +340 -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 +21 -9
- data/test/rails-app/Gemfile.lock +173 -118
- 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 +45 -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 +245 -13
- data/test/rails-app/app/models/song.rb +6 -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 +10 -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 +26 -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 +146 -144
- data/test/rails-app/test/test_helper.rb +7 -1
- data/test/test_helper.rb +0 -2
- data/trailblazer-endpoint.gemspec +2 -1
- metadata +73 -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
@@ -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
|
@@ -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
|
+
# Builds and registers an endpoint in a controller class.
|
96
|
+
def endpoint(name, **options, &block)
|
97
|
+
options = options.merge(protocol_block: block) if block_given?
|
98
|
+
|
99
|
+
return generic_endpoint_config(**name, **options) if name.is_a?(Hash)
|
100
|
+
build_endpoint(name, **options)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Configures generic {:adapter}, {:protocol}, etc.
|
104
|
+
def generic_endpoint_config(**options)
|
105
|
+
self.singleton_class.define_method :generic_options do |ctx,**|
|
106
|
+
{
|
107
|
+
**options
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
directive :generic_options, method(:generic_options) # FIXME: do we need this?
|
112
|
+
end
|
113
|
+
|
114
|
+
def build_endpoint(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,31 @@
|
|
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
|
+
default_block_options.merge( # this adds :invoke.
|
23
|
+
success_block: success_block || default_block_options[:success_block],
|
24
|
+
failure_block: failure_block || default_block_options[:failure_block],
|
25
|
+
protocol_failure_block: protocol_failure_block || default_block_options[:protocol_failure_block]
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
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,18 +18,13 @@ 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
|
29
26
|
# step :handle_not_authenticated
|
30
|
-
|
27
|
+
end
|
31
28
|
|
32
29
|
step :policy, Output(:failure) => _Path(semantic: :not_authorized) do # user from cookie, etc
|
33
30
|
# step :handle_not_authorized
|
@@ -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
|
|
@@ -116,7 +113,6 @@ module Trailblazer
|
|
116
113
|
[[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_after), "task_wrap.call_task", ["endpoint.end_signal", method(:terminus_handler)]]]
|
117
114
|
end
|
118
115
|
end
|
119
|
-
|
120
|
-
end
|
116
|
+
end # Protocol
|
121
117
|
end
|
122
118
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "openssl"
|
2
|
+
|
3
|
+
module Trailblazer
|
4
|
+
class Endpoint::Protocol
|
5
|
+
module Controller
|
6
|
+
module Cipher # FIXME: copied from Tyrant!
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def encrypt_value(ctx, value:, cipher_key:, **)
|
10
|
+
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').encrypt
|
11
|
+
cipher.key = Digest::SHA1.hexdigest(cipher_key)[0..23] # ArgumentError: key must be 24 bytes
|
12
|
+
s = cipher.update(value) + cipher.final
|
13
|
+
|
14
|
+
ctx[:encrypted_value] = s.unpack('H*')[0].upcase
|
15
|
+
end
|
16
|
+
|
17
|
+
def decrypt_value(ctx, encrypted_value:, cipher_key:, **)
|
18
|
+
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').decrypt
|
19
|
+
cipher.key = Digest::SHA1.hexdigest(cipher_key)[0..23]
|
20
|
+
s = [encrypted_value].pack("H*").unpack("C*").pack("c*")
|
21
|
+
|
22
|
+
ctx[:decrypted_value] = cipher.update(s) + cipher.final
|
23
|
+
end
|
24
|
+
end # Cipher
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Endpoint::Protocol
|
3
|
+
# Deserialize incoming state.
|
4
|
+
# Serialize outgoing state.
|
5
|
+
# What else?
|
6
|
+
module Controller
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def decrypt?(ctx, encrypted_resume_data:, **)
|
10
|
+
encrypted_resume_data
|
11
|
+
end
|
12
|
+
|
13
|
+
def deserialize_resume_data(ctx, decrypted_value:, **)
|
14
|
+
ctx[:resume_data] = JSON.parse(decrypted_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize_process_model?(ctx, process_model_from_resume_data:, **)
|
18
|
+
process_model_from_resume_data
|
19
|
+
end
|
20
|
+
|
21
|
+
def deserialize_process_model_id(ctx, resume_data:, **)
|
22
|
+
ctx[:process_model_id] = resume_data["id"] # DISCUSS: overriding {:process_model_id}?
|
23
|
+
end
|
24
|
+
|
25
|
+
def encrypt?(ctx, domain_ctx:, **)
|
26
|
+
ctx[:suspend_data] = domain_ctx[:suspend_data]
|
27
|
+
end
|
28
|
+
|
29
|
+
def serialize_suspend_data(ctx, suspend_data:, **)
|
30
|
+
ctx[:serialized_suspend_data] = JSON.dump(suspend_data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy_suspend_data_to_endpoint_ctx(ctx, domain_ctx:, **)
|
34
|
+
ctx[:suspend_data] = domain_ctx[:suspend_data]
|
35
|
+
end
|
36
|
+
|
37
|
+
# FIXME: use Model() mechanics.
|
38
|
+
def deserialize_process_model_id_from_resume_data(ctx, resume_data:, **)
|
39
|
+
# DISCUSS: should we warn when overriding an existing {process_model_id}?
|
40
|
+
ctx[:process_model_id] = resume_data["id"] # DISCUSS: overriding {:process_model_id}? # FIXME: stolen from Advance___::Controller
|
41
|
+
end
|
42
|
+
|
43
|
+
def insert_deserialize_steps!(activity, deserialize_before: :policy)
|
44
|
+
activity.module_eval do
|
45
|
+
step Controller.method(:decrypt?), id: :decrypt?, before: deserialize_before # error out if no serialized_resume_data given.
|
46
|
+
step Controller::Cipher.method(:decrypt_value), id: :decrypt,
|
47
|
+
input: {cipher_key: :cipher_key, encrypted_resume_data: :encrypted_value} , before: deserialize_before,
|
48
|
+
# Output(:failure) => Track(:success),
|
49
|
+
Output(:success) => Path(connect_to: Track(:success), track_color: :deserialize, before: deserialize_before) do # usually, Path goes into {policy}
|
50
|
+
|
51
|
+
step Controller.method(:deserialize_resume_data), id: :deserialize_resume_data
|
52
|
+
# DISCUSS: unmarshall?
|
53
|
+
# step Controller.method(:deserialize_process_model_id?), id: :deserialize_process_model_id?, activity.Output(Trailblazer::Activity::Left, :failure) => activity.Id(around_activity_id)
|
54
|
+
# step Controller.method(:deserialize_process_model_id), id: :deserialize_process_model_id
|
55
|
+
|
56
|
+
step ->(*) { true } # FIXME: otherwise we can't insert an element AFTER :deserialize_resume_data
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def insert_serialize_steps!(activity, serialize_after: :domain_activity)
|
62
|
+
activity.module_eval do
|
63
|
+
# FIXME: reverse order for insertion
|
64
|
+
step Controller::Cipher.method(:encrypt_value), id: :encrypt , after: serialize_after,
|
65
|
+
input: {cipher_key: :cipher_key, serialized_suspend_data: :value}, output: {encrypted_value: :encrypted_suspend_data}
|
66
|
+
step Controller.method(:serialize_suspend_data), id: :serialize_suspend_data , after: serialize_after
|
67
|
+
pass Controller.method(:copy_suspend_data_to_endpoint_ctx), id: :copy_suspend_data_to_endpoint_ctx , after: serialize_after
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Insert the "experimental" {find_process_model} steps
|
72
|
+
def insert_find_process_model!(protocol, **options)
|
73
|
+
protocol.module_eval do
|
74
|
+
step Subprocess(FindProcessModel), Output(:failure) => End(:not_found),
|
75
|
+
id: :find_process_model,
|
76
|
+
**options
|
77
|
+
# after: :authenticate
|
78
|
+
end
|
79
|
+
|
80
|
+
insert_copy_to_domain_ctx!(protocol, :process_model => :model)
|
81
|
+
end
|
82
|
+
|
83
|
+
def insert_copy_to_domain_ctx!(protocol, variables)
|
84
|
+
variables.each do |original_name, domain_name|
|
85
|
+
protocol.module_eval do
|
86
|
+
pass ->(ctx, domain_ctx:, **) { domain_ctx[domain_name] = ctx[original_name] if ctx.key?(original_name) },
|
87
|
+
id: :"copy_[#{original_name.inspect}]_to_domain_ctx[#{domain_name.inspect}]", before: :domain_activity
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|