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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4dd0d753bd8dc039e3f55e66d80d3d9d4904fcc0d442ea62860fbab4a0b05d5e
4
- data.tar.gz: 0ecfa53ed1607d4d3785d47ffef3f817033b18ec29c6cbbf06e4f466c90a2638
3
+ metadata.gz: d241ba658c264d5ec48e241c59abe89811646109f07a1f900231d7963245bcad
4
+ data.tar.gz: '0682d8ad85197f3049c481f3868ae87982991c913ffd8e5f3ae8185b2554a274'
5
5
  SHA512:
6
- metadata.gz: 7762ada8e3fafbd1a68e5f4045af8e57b03568745ac55236d756da6a4fe681f6818abd4e27b737b162872b9ebd4589f440b1b7ebee01729d2cfd6dd88702d980
7
- data.tar.gz: 6f56d293369de1da783002e8d9f602f2847eafbe2f7ad80f5f6a08fc305542f3d432d7c9878376cc421a38de58e19d60eac2d436fe2650191b36f66d149bb722
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.
@@ -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, domain_ctx_filter: nil, protocol_block: ->(*) { Hash.new })
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
- domain_ctx_filter_callable = [[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["endpoint.domain_ctx_filter", domain_ctx_filter]]]
24
- extensions_options[:extensions] << Trailblazer::Activity::TaskWrap::Extension(merge: domain_ctx_filter_callable) if domain_ctx_filter
54
+ if find_process_model
55
+ Protocol::Controller.insert_find_process_model!(self, before: :policy) # TODO: test before: :policy
56
+ end
25
57
 
26
- # puts Trailblazer::Developer.render(protocol)
27
- # puts
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
- endpoint_config(name, **options)
100
+ build_endpoint(name, **options)
100
101
  end
101
102
 
102
- def generic_endpoint_config(protocol:, adapter:, **options)
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 endpoint_config(name, domain_activity: name, **options)
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
- end
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
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Endpoint
3
- VERSION = "0.0.6"
3
+ VERSION = "0.0.7"
4
4
  end
5
5
  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, domain_ctx_filter: Trailblazer::Endpoint.domain_ctx_filter([:current_user, :process_model]), scope_domain_ctx: true
61
+ endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web,
62
+ scope_domain_ctx: true
62
63
  end
63
64
 
64
65
  class HtmlController < ApplicationController
@@ -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"
@@ -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)
5
- trailblazer-activity-dsl-linear (>= 0.3.0, < 0.4.0)
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.2)
160
- trailblazer-activity (>= 0.11.2, < 1.0.0)
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
@@ -1,3 +1,6 @@
1
1
  class Song < Struct.new(:id)
2
-
2
+ def self.find_by(id: false)
3
+ return unless id
4
+ return Song.new(id)
5
+ end
3
6
  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.0", "< 0.4.0"
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.6
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-17 00:00:00.000000000 Z
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.0
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.0
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