trailblazer-endpoint 0.0.6 → 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 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