trailblazer-endpoint 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +6 -0
- data/lib/trailblazer/endpoint.rb +41 -23
- data/lib/trailblazer/endpoint/controller.rb +5 -5
- data/lib/trailblazer/endpoint/dsl.rb +2 -0
- data/lib/trailblazer/endpoint/protocol.rb +2 -3
- 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/docs/controller_test.rb +2 -1
- data/test/rails-app/Gemfile +6 -0
- data/test/rails-app/Gemfile.lock +18 -4
- data/test/rails-app/app/controllers/application_controller/web.rb +3 -2
- data/test/rails-app/app/controllers/songs_controller.rb +174 -0
- data/test/rails-app/app/models/song.rb +4 -1
- data/test/rails-app/config/environments/test.rb +2 -0
- data/test/rails-app/config/routes.rb +10 -0
- data/test/rails-app/test/controllers/songs_controller_test.rb +69 -0
- data/trailblazer-endpoint.gemspec +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d241ba658c264d5ec48e241c59abe89811646109f07a1f900231d7963245bcad
|
4
|
+
data.tar.gz: '0682d8ad85197f3049c481f3868ae87982991c913ffd8e5f3ae8185b2554a274'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c7b9c72993a46d19c81307a94423e732b7eb0d433526cb9d32e9f7e70e54fa4eb36e3cf062eb3d77eebc9372955c8853ddbbb2b13ef9a22e711c0c7b09a9cad
|
7
|
+
data.tar.gz: ac107aae848481b55309b9c4fc607dd42c505fc16c1a267657cba4852660ecd72c8de4dcc9036b8a09bcf99d0d7d4c75d6fca9a956ba53562b8769f2913801a3
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 0.0.7
|
2
|
+
|
3
|
+
* BREAKING: Remove `:domain_ctx_filter` in favor of `Controller.insert_copy_to_domain_ctx!`.
|
4
|
+
* Add support for serializing `:suspend_data` and deserializing `:resume_data` so session data can get automatically encrypted and passed to the next action. This used to sit in `workflow`.
|
5
|
+
* Add `:find_process_model`. This introduces a new protocol step before `policy` to find the "process model" instead of letting the domain operation or even the policy (or both!) find the "current model".
|
6
|
+
|
1
7
|
# 0.0.6
|
2
8
|
|
3
9
|
* `Controller::endpoint` short form introduced.
|
data/lib/trailblazer/endpoint.rb
CHANGED
@@ -2,7 +2,12 @@ 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,
|
5
|
+
def self.build(protocol:, adapter:, domain_activity:, scope_domain_ctx: true, protocol_block: ->(*) { Hash.new },
|
6
|
+
serialize: false, # TODO: plug-in, not hardcoded!
|
7
|
+
deserialize: false,# TODO: plug-in, not hardcoded!
|
8
|
+
find_process_model: false, # TODO: plug-in, not hardcoded!
|
9
|
+
deserialize_process_model_id_from_resume_data: false # TODO: plug-in, not hardcoded!
|
10
|
+
)
|
6
11
|
# special considerations around the {domain_activity} and its taskWrap:
|
7
12
|
#
|
8
13
|
# 1. domain_ctx_filter (e.g. to filter {current_user})
|
@@ -19,14 +24,41 @@ module Trailblazer
|
|
19
24
|
# scoping: {:domain_ctx} becomes ctx
|
20
25
|
extensions_options.merge!(Endpoint.options_for_scope_domain_ctx) if scope_domain_ctx # TODO: test flag
|
21
26
|
|
27
|
+
app_protocol = build_protocol(protocol, domain_activity: domain_activity, extensions_options: extensions_options, protocol_block: protocol_block, serialize: serialize, deserialize: deserialize,
|
28
|
+
find_process_model: find_process_model, deserialize_process_model_id_from_resume_data: deserialize_process_model_id_from_resume_data
|
29
|
+
)
|
30
|
+
|
31
|
+
# puts Trailblazer::Developer.render(app_protocol)
|
32
|
+
|
33
|
+
Class.new(adapter) do
|
34
|
+
step(Subprocess(app_protocol), {inherit: true, id: :protocol, replace: :protocol})
|
35
|
+
end # app_adapter
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# @private
|
40
|
+
def self.build_protocol(protocol, domain_activity:, extensions_options:, protocol_block:, serialize:, deserialize:, find_process_model:, deserialize_process_model_id_from_resume_data:)
|
41
|
+
Class.new(protocol) do
|
42
|
+
if serialize
|
43
|
+
Protocol::Controller.insert_serialize_steps!(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
if deserialize
|
47
|
+
Protocol::Controller.insert_deserialize_steps!(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
if serialize || deserialize
|
51
|
+
Protocol::Controller.insert_copy_to_domain_ctx!(self, :resume_data => :resume_data)
|
52
|
+
end
|
22
53
|
|
23
|
-
|
24
|
-
|
54
|
+
if find_process_model
|
55
|
+
Protocol::Controller.insert_find_process_model!(self, before: :policy) # TODO: test before: :policy
|
56
|
+
end
|
25
57
|
|
26
|
-
|
27
|
-
|
58
|
+
if deserialize_process_model_id_from_resume_data
|
59
|
+
pass Protocol::Controller.method(:deserialize_process_model_id_from_resume_data), after: :deserialize_resume_data, magnetic_to: :deserialize, Output(:success) => Track(:deserialize)
|
60
|
+
end
|
28
61
|
|
29
|
-
app_protocol = Class.new(protocol) do
|
30
62
|
step(Subprocess(domain_activity), {inherit: true, id: :domain_activity, replace: :domain_activity,
|
31
63
|
|
32
64
|
# FIXME: where does this go?
|
@@ -35,13 +67,6 @@ module Trailblazer
|
|
35
67
|
merge(instance_exec(&protocol_block)) # the block is evaluated in the {Protocol} context.
|
36
68
|
)
|
37
69
|
end
|
38
|
-
|
39
|
-
# puts Trailblazer::Developer.render(app_protocol)
|
40
|
-
|
41
|
-
Class.new(adapter) do
|
42
|
-
step(Subprocess(app_protocol), {inherit: true, id: :protocol, replace: :protocol})
|
43
|
-
end # app_adapter
|
44
|
-
|
45
70
|
end
|
46
71
|
|
47
72
|
def self.options_for_scope_domain_ctx()
|
@@ -51,16 +76,6 @@ module Trailblazer
|
|
51
76
|
}
|
52
77
|
end
|
53
78
|
|
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
|
-
|
64
79
|
# Runtime
|
65
80
|
# Invokes the endpoint for you and runs one of the three outcome blocks.
|
66
81
|
def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:, invoke: Trailblazer::Activity::TaskWrap.method(:invoke))
|
@@ -146,3 +161,6 @@ require "trailblazer/endpoint/adapter"
|
|
146
161
|
require "trailblazer/endpoint/dsl"
|
147
162
|
require "trailblazer/endpoint/controller"
|
148
163
|
require "trailblazer/endpoint/options"
|
164
|
+
require "trailblazer/endpoint/protocol/controller"
|
165
|
+
require "trailblazer/endpoint/protocol/find_process_model"
|
166
|
+
require "trailblazer/endpoint/protocol/cipher"
|
@@ -92,18 +92,18 @@ module Trailblazer
|
|
92
92
|
extended.directive(:endpoints, ->(*) { {} })
|
93
93
|
end
|
94
94
|
|
95
|
+
# Builds and registers an endpoint in a controller class.
|
95
96
|
def endpoint(name, **options, &block)
|
96
97
|
options = options.merge(protocol_block: block) if block_given?
|
97
98
|
|
98
99
|
return generic_endpoint_config(**name, **options) if name.is_a?(Hash)
|
99
|
-
|
100
|
+
build_endpoint(name, **options)
|
100
101
|
end
|
101
102
|
|
102
|
-
|
103
|
+
# Configures generic {:adapter}, {:protocol}, etc.
|
104
|
+
def generic_endpoint_config(**options)
|
103
105
|
self.singleton_class.define_method :generic_options do |ctx,**|
|
104
106
|
{
|
105
|
-
protocol: protocol,
|
106
|
-
adapter: adapter,
|
107
107
|
**options
|
108
108
|
}
|
109
109
|
end
|
@@ -111,7 +111,7 @@ module Trailblazer
|
|
111
111
|
directive :generic_options, method(:generic_options) # FIXME: do we need this?
|
112
112
|
end
|
113
113
|
|
114
|
-
def
|
114
|
+
def build_endpoint(name, domain_activity: name, **options)
|
115
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
116
|
|
117
117
|
endpoint = Trailblazer::Endpoint.build(build_options)
|
@@ -19,9 +19,11 @@ module Trailblazer
|
|
19
19
|
# #call
|
20
20
|
def to_args(default_block_options)
|
21
21
|
return options,
|
22
|
+
default_block_options.merge( # this adds :invoke.
|
22
23
|
success_block: success_block || default_block_options[:success_block],
|
23
24
|
failure_block: failure_block || default_block_options[:failure_block],
|
24
25
|
protocol_failure_block: protocol_failure_block || default_block_options[:protocol_failure_block]
|
26
|
+
)
|
25
27
|
end
|
26
28
|
end
|
27
29
|
end
|
@@ -24,7 +24,7 @@ module Trailblazer
|
|
24
24
|
|
25
25
|
step :authenticate, Output(:failure) => _Path(semantic: :not_authenticated) do
|
26
26
|
# step :handle_not_authenticated
|
27
|
-
|
27
|
+
end
|
28
28
|
|
29
29
|
step :policy, Output(:failure) => _Path(semantic: :not_authorized) do # user from cookie, etc
|
30
30
|
# step :handle_not_authorized
|
@@ -113,7 +113,6 @@ module Trailblazer
|
|
113
113
|
[[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_after), "task_wrap.call_task", ["endpoint.end_signal", method(:terminus_handler)]]]
|
114
114
|
end
|
115
115
|
end
|
116
|
-
|
117
|
-
end
|
116
|
+
end # Protocol
|
118
117
|
end
|
119
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
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Trailblazer::Endpoint::Protocol
|
2
|
+
class FindProcessModel < Trailblazer::Activity::Railway
|
3
|
+
# step :find_process_model?, Output(:failure) => Id("End.success")
|
4
|
+
step :find_process_model#, Output(:failure) => End(:not_found) # DISCUSS: currently, {End.failure} implies {not_found}.
|
5
|
+
|
6
|
+
# DISCUSS: should the implementation remain in {Activity}?
|
7
|
+
# def find_process_model?(ctx, find_process_model:, **)
|
8
|
+
# find_process_model
|
9
|
+
# end
|
10
|
+
|
11
|
+
def find_process_model(ctx, process_model_class:, process_model_id:, **)
|
12
|
+
ctx[:process_model] = process_model_class.find_by(id: process_model_id)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -58,7 +58,8 @@ class DocsControllerTest < Minitest::Spec
|
|
58
58
|
include T.def_steps(:authenticate, :policy)
|
59
59
|
end
|
60
60
|
|
61
|
-
endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web,
|
61
|
+
endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web,
|
62
|
+
scope_domain_ctx: true
|
62
63
|
end
|
63
64
|
|
64
65
|
class HtmlController < ApplicationController
|
data/test/rails-app/Gemfile
CHANGED
@@ -21,3 +21,9 @@ gem "minitest-line"
|
|
21
21
|
gem "trailblazer-cells"
|
22
22
|
gem "cells-erb"
|
23
23
|
gem "cells-rails"
|
24
|
+
|
25
|
+
|
26
|
+
# FIXME
|
27
|
+
gem "trailblazer-workflow", path: "../../../trailblazer-workflow"
|
28
|
+
# gem "trailblazer-activity-dsl-linear", path: "../../../trailblazer-activity-dsl-linear"
|
29
|
+
gem "trailblazer-activity-dsl-linear", ">= 0.3.4"
|
data/test/rails-app/Gemfile.lock
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../../../trailblazer-workflow
|
3
|
+
specs:
|
4
|
+
trailblazer-workflow (0.0.8)
|
5
|
+
trailblazer-activity
|
6
|
+
trailblazer-activity-implementation
|
7
|
+
trailblazer-developer (>= 0.0.10)
|
8
|
+
trailblazer-endpoint
|
9
|
+
|
1
10
|
PATH
|
2
11
|
remote: ../..
|
3
12
|
specs:
|
4
|
-
trailblazer-endpoint (0.0.
|
5
|
-
trailblazer-activity-dsl-linear (>= 0.3.
|
13
|
+
trailblazer-endpoint (0.0.7)
|
14
|
+
trailblazer-activity-dsl-linear (>= 0.3.4, < 0.4.0)
|
6
15
|
|
7
16
|
GEM
|
8
17
|
remote: https://rubygems.org/
|
@@ -156,8 +165,11 @@ GEM
|
|
156
165
|
tilt (2.0.10)
|
157
166
|
trailblazer-activity (0.11.3)
|
158
167
|
trailblazer-context (>= 0.3.1, < 0.4.0)
|
159
|
-
trailblazer-activity-dsl-linear (0.3.
|
160
|
-
trailblazer-activity (>= 0.11.
|
168
|
+
trailblazer-activity-dsl-linear (0.3.4)
|
169
|
+
trailblazer-activity (>= 0.11.3, < 1.0.0)
|
170
|
+
trailblazer-activity-implementation (0.0.3)
|
171
|
+
trailblazer-activity (>= 0.8.0)
|
172
|
+
trailblazer-activity-dsl-linear
|
161
173
|
trailblazer-cells (0.0.3)
|
162
174
|
cells (>= 4.1.0.rc1, < 5.0.0)
|
163
175
|
trailblazer-context (0.3.1)
|
@@ -190,9 +202,11 @@ DEPENDENCIES
|
|
190
202
|
multi_json
|
191
203
|
rails (~> 6.0.3, >= 6.0.3.1)
|
192
204
|
sqlite3 (~> 1.4)
|
205
|
+
trailblazer-activity-dsl-linear (>= 0.3.4)
|
193
206
|
trailblazer-cells
|
194
207
|
trailblazer-endpoint!
|
195
208
|
trailblazer-operation
|
209
|
+
trailblazer-workflow!
|
196
210
|
|
197
211
|
BUNDLED WITH
|
198
212
|
2.1.4
|
@@ -30,12 +30,13 @@ class ApplicationController::Web < ApplicationController
|
|
30
30
|
def policy(ctx, domain_ctx:, **)
|
31
31
|
Policy.(domain_ctx)
|
32
32
|
end
|
33
|
+
|
34
|
+
Trailblazer::Endpoint::Protocol::Controller.insert_copy_to_domain_ctx!(self, :current_user => :current_user)
|
33
35
|
end
|
34
36
|
#:protocol end
|
35
37
|
Policy = ->(domain_ctx) { domain_ctx[:params][:policy] == "false" ? false : true }
|
36
38
|
#~gskip end
|
37
|
-
endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web
|
38
|
-
domain_ctx_filter: ApplicationController.current_user_in_domain_ctx
|
39
|
+
endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web
|
39
40
|
end
|
40
41
|
#:generic end
|
41
42
|
|
@@ -81,4 +81,178 @@ end
|
|
81
81
|
end
|
82
82
|
#:protocol_failure end
|
83
83
|
end
|
84
|
+
|
85
|
+
|
86
|
+
# endpoint_ctx
|
87
|
+
# :resume_data
|
88
|
+
# domain_ctx
|
89
|
+
# :resume_data (copy)
|
90
|
+
|
91
|
+
|
92
|
+
# authenticate
|
93
|
+
|
94
|
+
# deserialize ==> {resume_data: {id: 1}}
|
95
|
+
# deserialize_process_model_id_from_resume_data
|
96
|
+
|
97
|
+
# find_process_model
|
98
|
+
# policy
|
99
|
+
# domain_activity
|
100
|
+
|
101
|
+
# serialize suspend_data and deserialize resume_data
|
102
|
+
class SerializeController < SongsController
|
103
|
+
endpoint Song::Operation::Create,
|
104
|
+
protocol: ApplicationController::Web::Protocol
|
105
|
+
# serialize: true
|
106
|
+
|
107
|
+
def self.options_for_block_options(ctx, **)
|
108
|
+
{
|
109
|
+
invoke: Trailblazer::Developer.method(:wtf?) # FIXME
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def self.options_for_endpoint(ctx, controller:, **)
|
115
|
+
{
|
116
|
+
cipher_key: Rails.application.config.cipher_key,
|
117
|
+
|
118
|
+
encrypted_resume_data: controller.params[:encrypted_resume_data],
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
directive :options_for_block_options, method(:options_for_block_options)
|
123
|
+
directive :options_for_endpoint, method(:options_for_endpoint)
|
124
|
+
|
125
|
+
def create
|
126
|
+
encrypted_value = Trailblazer::Workflow::Cipher.encrypt_value({}, cipher_key: cipher_key, value: JSON.dump({id: "findings received", class: Object}))
|
127
|
+
|
128
|
+
endpoint Song::Operation::Create, encrypted_resume_data: encrypted_value, process_model_from_resume_data: false do |ctx, current_user:, endpoint_ctx:, **|
|
129
|
+
render html: cell(Song::Cell::Create, model, current_user: current_user)
|
130
|
+
end.Or do |ctx, contract:, **| # validation failure
|
131
|
+
render html: cell(Song::Cell::New, contract)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# TODO: not really a doc test.
|
137
|
+
# the entire deserialize cycle is skipped since we only use {:serialize}
|
138
|
+
class Serialize1Controller < SerializeController
|
139
|
+
class Create < Trailblazer::Operation
|
140
|
+
pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
|
141
|
+
end
|
142
|
+
|
143
|
+
endpoint "Create",
|
144
|
+
domain_activity: Create,
|
145
|
+
serialize: true,
|
146
|
+
deserialize: true
|
147
|
+
|
148
|
+
def create
|
149
|
+
# {:model} and {:memory} are from the domain_ctx.
|
150
|
+
# {:encrypted_suspend_data} from endpoint.
|
151
|
+
endpoint "Create" do |ctx, model:, endpoint_ctx:, **|
|
152
|
+
render html: "#{model.inspect}/#{ctx[:memory].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
|
153
|
+
end.Or do |ctx, **| # validation failure
|
154
|
+
render html: "xxx", status: 500
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# TODO: not really a doc test.
|
160
|
+
# ---------deserialize cycle is skipped.
|
161
|
+
# we serialize {:remember}.
|
162
|
+
class Serialize2Controller < Serialize1Controller # "render confirm page"
|
163
|
+
class Create < Trailblazer::Operation
|
164
|
+
pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
|
165
|
+
step ->(ctx, **) { ctx[:suspend_data] = {remember: OpenStruct.new(id: 1), id: 9} } # write to domain_ctx[:suspend_data]
|
166
|
+
end
|
167
|
+
|
168
|
+
endpoint "Create",
|
169
|
+
domain_activity: Create,
|
170
|
+
serialize: true
|
171
|
+
end
|
172
|
+
|
173
|
+
# we can read from {:resume_data}
|
174
|
+
class Serialize3Controller < Serialize1Controller # "process submitted confirm page"
|
175
|
+
class Create < Trailblazer::Operation
|
176
|
+
pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
|
177
|
+
pass ->(ctx, **) { ctx[:memory] = ctx[:resume_data] } # read/process the suspended data
|
178
|
+
end
|
179
|
+
|
180
|
+
endpoint "Create",
|
181
|
+
domain_activity: Create,
|
182
|
+
deserialize: true
|
183
|
+
end
|
184
|
+
|
185
|
+
# find process_model via id in suspend/resume data (used to be called {process_model_from_resume_data})
|
186
|
+
class Serialize4Controller < Serialize1Controller
|
187
|
+
class Create < Trailblazer::Operation
|
188
|
+
pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
|
189
|
+
pass ->(ctx, **) { ctx[:memory] = ctx[:resume_data] } # read/process the suspended data
|
190
|
+
end
|
191
|
+
|
192
|
+
endpoint "Create",
|
193
|
+
domain_activity: Create,
|
194
|
+
deserialize: true,
|
195
|
+
find_process_model: true,
|
196
|
+
deserialize_process_model_id_from_resume_data: true
|
197
|
+
|
198
|
+
def create
|
199
|
+
endpoint "Create", process_model_class: Song do |ctx, endpoint_ctx:, **|
|
200
|
+
render html: "#{endpoint_ctx[:process_model_id].inspect}/#{ctx[:memory].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# find process_model from resume
|
206
|
+
# FIXME: what is the diff to Controller4?
|
207
|
+
class Serialize5Controller < Serialize1Controller
|
208
|
+
endpoint "Create",
|
209
|
+
domain_activity: Serialize4Controller::Create,
|
210
|
+
deserialize: true,
|
211
|
+
find_process_model: true,
|
212
|
+
deserialize_process_model_id_from_resume_data: true
|
213
|
+
|
214
|
+
def create
|
215
|
+
endpoint "Create", find_process_model: true, process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
|
216
|
+
render html: "#{model.inspect}/#{ctx[:memory].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# find process_model from action_options
|
222
|
+
class Serialize6Controller < Serialize1Controller
|
223
|
+
endpoint "Create",
|
224
|
+
domain_activity: Serialize4Controller::Create,
|
225
|
+
protocol: ApplicationController::Web::Protocol,
|
226
|
+
find_process_model: true
|
227
|
+
|
228
|
+
def create
|
229
|
+
endpoint "Create", find_process_model: true, process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
|
230
|
+
render html: "#{model.inspect}/#{endpoint_ctx[:process_model].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Configure only {:find_process_model} and {:protocol}.
|
236
|
+
class Serialize7Controller < Serialize1Controller
|
237
|
+
endpoint find_process_model: true # generic setting for all endpoints in this controller.
|
238
|
+
|
239
|
+
endpoint "Create", # no need to specify {:find_process_model}
|
240
|
+
domain_activity: Serialize4Controller::Create
|
241
|
+
|
242
|
+
endpoint "New",
|
243
|
+
find_process_model: false,
|
244
|
+
domain_activity: Serialize4Controller::Create
|
245
|
+
|
246
|
+
def create
|
247
|
+
endpoint "Create", process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
|
248
|
+
render html: "#{model.inspect}/#{endpoint_ctx[:process_model].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def new
|
253
|
+
endpoint "New", process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
|
254
|
+
render html: "#{model.inspect}/#{endpoint_ctx[:process_model].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
84
258
|
end
|
@@ -35,4 +35,6 @@ Rails.application.configure do
|
|
35
35
|
|
36
36
|
# Raises error for missing translations.
|
37
37
|
# config.action_view.raise_on_missing_translations = true
|
38
|
+
|
39
|
+
config.cipher_key = "e1e1cc87asdfasdfasdfasfdasdfasdfasvhnfvbdb" # FIXME: this usually happens via the new key mechanism in Rails 6
|
38
40
|
end
|
@@ -15,4 +15,14 @@ Rails.application.routes.draw do
|
|
15
15
|
get "/v1/songs_with_options/:id", to: "api/v1/songs_controller/with_options#show"
|
16
16
|
|
17
17
|
get "/", to: "home#dashboard"
|
18
|
+
|
19
|
+
post "/songs/serialize", to: "songs_controller/serialize#create"
|
20
|
+
post "/songs/serialize1", to: "songs_controller/serialize1#create"
|
21
|
+
post "/songs/serialize2", to: "songs_controller/serialize2#create"
|
22
|
+
post "/songs/serialize3", to: "songs_controller/serialize3#create"
|
23
|
+
post "/songs/serialize4", to: "songs_controller/serialize4#create"
|
24
|
+
post "/songs/serialize5", to: "songs_controller/serialize5#create"
|
25
|
+
post "/songs/serialize6", to: "songs_controller/serialize6#create"
|
26
|
+
post "/songs/serialize7", to: "songs_controller/serialize7#create"
|
27
|
+
post "/songs/serialize72", to: "songs_controller/serialize7#new"
|
18
28
|
end
|
@@ -70,6 +70,75 @@ class SongsControllerTest < ActionDispatch::IntegrationTest
|
|
70
70
|
assert_equal "wrong login, app crashed", response.body
|
71
71
|
end
|
72
72
|
|
73
|
+
test "serializing/deserializing/find_process_model_from_resume_data" do
|
74
|
+
# # 401
|
75
|
+
# post "/songs/serialize/"
|
76
|
+
# assert_response 401
|
77
|
+
|
78
|
+
# sign in
|
79
|
+
post "/auth/sign_in", params: {username: "yogi@trb.to", password: "secret"}
|
80
|
+
assert_equal 1, session[:user_id]
|
81
|
+
|
82
|
+
|
83
|
+
# When {:encrypted_resume_data} is {nil} the entire deserialize cycle is skipped.
|
84
|
+
# Nothing gets serialized.
|
85
|
+
# This is considered an error since we're expecting resume data.
|
86
|
+
post "/songs/serialize1/", params: {} # encrypted_resume_data: nil
|
87
|
+
assert_response 500
|
88
|
+
assert_equal "xxx", response.body # DISCUSS: is this an application or a protocol error?
|
89
|
+
|
90
|
+
# Nothing deserialized, but {:remember} serialized
|
91
|
+
# "confirm_delete form"
|
92
|
+
post "/songs/serialize2/", params: {} # {:remember} serialized
|
93
|
+
assert_response 200
|
94
|
+
encrypted_string = "0109C4E535EDA2CCE8CD69E50C179F5950CC4A2A898504F951C995B6BCEAFE1DFAB02894854B96B9D11C23E25DB5FB03"
|
95
|
+
assert_equal "false/nil/#{encrypted_string}", response.body
|
96
|
+
|
97
|
+
# {:remember} deserialized
|
98
|
+
# "submit confirm_delete"
|
99
|
+
# We're expecting serialized data and don't serialize anything.
|
100
|
+
post "/songs/serialize3/", params: {encrypted_resume_data: encrypted_string} # {:remember} serialized
|
101
|
+
assert_response 200
|
102
|
+
assert_equal "false/{\"remember\"=>\"#<OpenStruct id=1>\", \"id\"=>9}/", response.body
|
103
|
+
|
104
|
+
# retrieve process_model via id in {:resume_data}
|
105
|
+
# we can see {endpoint_ctx[:process_model_id]}
|
106
|
+
# We're expecting serialized data and don't serialize anything.
|
107
|
+
post "/songs/serialize4/", params: {encrypted_resume_data: encrypted_string}
|
108
|
+
assert_response 200
|
109
|
+
assert_equal "9/{\"remember\"=>\"#<OpenStruct id=1>\", \"id\"=>9}/", response.body
|
110
|
+
|
111
|
+
# retrieve process_model via {:resume_data}'s serialized id.
|
112
|
+
# We're expecting serialized data and don't serialize anything.
|
113
|
+
post "/songs/serialize5/", params: {encrypted_resume_data: encrypted_string}
|
114
|
+
assert_response 200
|
115
|
+
assert_equal "#<struct Song id=9>/{\"remember\"=>\"#<OpenStruct id=1>\", \"id\"=>9}/", response.body
|
116
|
+
# model not found
|
117
|
+
post "/songs/serialize5/", params: {encrypted_resume_data: "36C61CCE30E6CFDE637DF0DA9257CC49"}
|
118
|
+
assert_response 404
|
119
|
+
assert_equal "", response.body
|
120
|
+
|
121
|
+
# find model without serialize from params, no serialized stuff passed
|
122
|
+
# DISCUSS: this could be a different test
|
123
|
+
post "/songs/serialize6/", params: {id: 1}
|
124
|
+
assert_response 200
|
125
|
+
assert_equal "#<struct Song id=\"1\">/#<struct Song id=\"1\">/", response.body
|
126
|
+
# model not found
|
127
|
+
post "/songs/serialize6/", params: {id: nil}
|
128
|
+
assert_response 404
|
129
|
+
assert_equal "", response.body
|
130
|
+
|
131
|
+
# {find_process_model: true} set on controller level, for all endpoints.
|
132
|
+
post "/songs/serialize7/", params: {id: 1}
|
133
|
+
assert_response 200
|
134
|
+
assert_equal "#<struct Song id=\"1\">/#<struct Song id=\"1\">/", response.body
|
135
|
+
# {find_process_model: false} overrides controller setting
|
136
|
+
post "/songs/serialize72/", params: {id: 1}
|
137
|
+
assert_response 200
|
138
|
+
assert_equal "false/nil/", response.body
|
139
|
+
|
140
|
+
end
|
141
|
+
|
73
142
|
test "sign_in" do
|
74
143
|
# wrong credentials
|
75
144
|
post "/auth/sign_in", params: {}
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency "trailblazer-activity-dsl-linear", ">= 0.3.
|
20
|
+
spec.add_dependency "trailblazer-activity-dsl-linear", ">= 0.3.4", "< 0.4.0"
|
21
21
|
|
22
22
|
spec.add_development_dependency "bundler"
|
23
23
|
spec.add_development_dependency "rake"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-endpoint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trailblazer-activity-dsl-linear
|
@@ -16,7 +16,7 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.3.
|
19
|
+
version: 0.3.4
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: 0.4.0
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.3.
|
29
|
+
version: 0.3.4
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 0.4.0
|
@@ -109,6 +109,9 @@ files:
|
|
109
109
|
- lib/trailblazer/endpoint/dsl.rb
|
110
110
|
- lib/trailblazer/endpoint/options.rb
|
111
111
|
- lib/trailblazer/endpoint/protocol.rb
|
112
|
+
- lib/trailblazer/endpoint/protocol/cipher.rb
|
113
|
+
- lib/trailblazer/endpoint/protocol/controller.rb
|
114
|
+
- lib/trailblazer/endpoint/protocol/find_process_model.rb
|
112
115
|
- lib/trailblazer/endpoint/rails.rb
|
113
116
|
- lib/trailblazer/endpoint/version.rb
|
114
117
|
- test/adapter/api_test.rb
|