trailblazer-endpoint 0.0.5 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +21 -0
  3. data/README.md +40 -5
  4. data/lib/trailblazer/endpoint/controller.rb +55 -22
  5. data/lib/trailblazer/endpoint/dsl.rb +2 -0
  6. data/lib/trailblazer/endpoint/options.rb +0 -23
  7. data/lib/trailblazer/endpoint/protocol/cipher.rb +27 -0
  8. data/lib/trailblazer/endpoint/protocol/controller.rb +102 -0
  9. data/lib/trailblazer/endpoint/protocol/find_process_model.rb +15 -0
  10. data/lib/trailblazer/endpoint/protocol.rb +2 -3
  11. data/lib/trailblazer/endpoint/version.rb +1 -1
  12. data/lib/trailblazer/endpoint.rb +45 -25
  13. data/test/docs/controller_test.rb +61 -1
  14. data/test/rails-app/Gemfile +17 -1
  15. data/test/rails-app/Gemfile.lock +134 -83
  16. data/test/rails-app/app/concepts/song/cell/create.rb +4 -0
  17. data/test/rails-app/app/concepts/song/cell/new.rb +4 -0
  18. data/test/rails-app/app/concepts/song/operation/create.rb +5 -1
  19. data/test/rails-app/app/concepts/song/view/create.erb +1 -0
  20. data/test/rails-app/app/concepts/song/view/new.erb +1 -0
  21. data/test/rails-app/app/controllers/api/v1/songs_controller.rb +13 -7
  22. data/test/rails-app/app/controllers/application_controller/api.rb +3 -1
  23. data/test/rails-app/app/controllers/application_controller/web.rb +35 -19
  24. data/test/rails-app/app/controllers/application_controller.rb +3 -1
  25. data/test/rails-app/app/controllers/auth_controller.rb +44 -0
  26. data/test/rails-app/app/controllers/home_controller.rb +5 -0
  27. data/test/rails-app/app/controllers/songs_controller.rb +255 -12
  28. data/test/rails-app/app/models/song.rb +4 -1
  29. data/test/rails-app/app/models/user.rb +3 -1
  30. data/test/rails-app/config/application.rb +1 -1
  31. data/test/rails-app/config/environments/test.rb +2 -0
  32. data/test/rails-app/config/routes.rb +21 -3
  33. data/test/rails-app/test/controllers/api_songs_controller_test.rb +6 -2
  34. data/test/rails-app/test/controllers/songs_controller_test.rb +125 -10
  35. data/trailblazer-endpoint.gemspec +2 -2
  36. metadata +23 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5856f0724650869732af67c5b45eee354e68a606b97d86153dffa3271adc9ee1
4
- data.tar.gz: 01504dc413db4e2dce833908d1687ba0492f157cea3a3bca58853d9615d597d5
3
+ metadata.gz: 31aba7b848760da16d7c5ad9a7ee24f821acac0b4fd72884e263a5e52468d868
4
+ data.tar.gz: 71d47c02a27874bbc9e16d64d2844d6d930888b77b653c582e9ebd5d94960f71
5
5
  SHA512:
6
- metadata.gz: 9c6322150c0dad7b568a77c8dc8b4af4a48c40e6358d18b16cf57e50f3f9515bae3e38a1875f3eb48e420192dbe978a5f7631b5b35f7c1e532e58e52d3f2043a
7
- data.tar.gz: 3ff1847a6e2e59fba1d8b3713b7069d58ea7e89be250ab79d5d87b795a5141c6a2c5dd1cc653257191d07906c66c4700fd12408b03200bdc89d87b312451cf96
6
+ metadata.gz: 24837b2a2fc722d9101b56fe9f35d648f7408574a518bc66fbefcd539ffd4363c44232fb8c51373902782d15986134cecb8d5c811fd504cc6a425ee53d90f8a1
7
+ data.tar.gz: b25455ecd91f83b3e4bf067b6fbb9e1188d162f894379ec01de91fbb09bff5e38e0517684d1220ef49d7a26a6ab1f477bed554ee8bf407f84950629f7329a435
data/CHANGES.md CHANGED
@@ -1,3 +1,24 @@
1
+ # 0.0.9
2
+
3
+ * Fix Ruby 3.0 code.
4
+ * Update dependencies.
5
+
6
+ # 0.0.8
7
+
8
+ * Add `Protocol.insert_copy_from_domain_ctx!` to copy from `domain_ctx` to the `endpoint_ctx`.
9
+
10
+ # 0.0.7
11
+
12
+ * BREAKING: Remove `:domain_ctx_filter` in favor of `Controller.insert_copy_to_domain_ctx!`.
13
+ * 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`.
14
+ * 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".
15
+
16
+ # 0.0.6
17
+
18
+ * `Controller::endpoint` short form introduced.
19
+ * Minor changes for `Controller.module`.
20
+ * Lots of cleanups.
21
+
1
22
  # 0.0.5
2
23
 
3
24
  * Removed `Protocol::Failure`. Until we have `Railway::End::Failure`, use a normal `Activity::End` everywhere instead of introducing our own.
data/README.md CHANGED
@@ -1,11 +1,46 @@
1
1
  # Trailblazer::Endpoint
2
2
 
3
- *Generic HTTP handlers for operation results.*
3
+ *Endpoints handle authentication, authorization, calling the business logic and response rendering.*
4
4
 
5
- Decouple finding out *what happened* from *what to do*.
5
+ ## Overview
6
6
 
7
- t test/controllers/songs_controller_test.rb --backtrace
7
+ An endpoint links your routing with your business code. The idea is that your controllers are pure HTTP routers, calling the respective endpoint for each action. From there, the endpoint takes over, handles authentication, policies, executing the domain code, interpreting the result, and providing hooks to render a response.
8
8
 
9
- ## TODO
9
+ Instead of dealing with a mix of `before_filter`s, Rack-middlewares, controller code and callbacks, an endpoint is just another activity and allows to be customized with the well-established Trailblazer mechanics.
10
10
 
11
- * make travis build run `cd test/rails-app/ && rake`
11
+
12
+ In a Rails controller, a controller action could look as follows.
13
+
14
+ ```ruby
15
+ class DiagramsController < ApplicationController
16
+ endpoint Diagram::Operation::Create, [:is_logged_in?, :can_add_diagram?]
17
+
18
+ def create
19
+ endpoint Diagram::Operation::Create do |ctx, **|
20
+ redirect_to diagram_path(ctx[:diagram].id)
21
+ end.Or do |ctx, **|
22
+ render :form
23
+ end
24
+ end
25
+ end
26
+ ```
27
+
28
+ While routing and redirecting/rendering still happens in Rails, all remaining steps are handled in the endpoint.
29
+
30
+ An API controller action, where the rendering is done generically, could look much simpler.
31
+
32
+ ```ruby
33
+ class API::V1::DiagramsController < ApplicationController
34
+ endpoint Diagram::Operation::Create, [:is_logged_in?, :can_add_diagram?]
35
+
36
+ def create
37
+ endpoint Diagram::Operation::Create
38
+ end
39
+ end
40
+ ```
41
+
42
+ Endpoints are easily customized but their main intent is to reduce fuzzy controller code and providing best practices for both HTML-rendering controllers and APIs.
43
+
44
+ ## Documentation
45
+
46
+ Read the [full documentation for endpoint](https://trailblazer.to/2.1/docs/endpoint.html) on our website.
@@ -2,9 +2,9 @@ module Trailblazer
2
2
  class Endpoint
3
3
  module Controller
4
4
  def self.extended(extended)
5
- extended.extend Trailblazer::Endpoint::Options::DSL
5
+ extended.extend Trailblazer::Endpoint::Options::DSL # ::directive
6
6
  extended.extend Trailblazer::Endpoint::Options::DSL::Inherit
7
- extended.extend Trailblazer::Endpoint::Options
7
+ extended.extend Trailblazer::Endpoint::Options # ::options_for
8
8
  extended.extend DSL::Endpoint
9
9
 
10
10
  extended.include InstanceMethods # {#endpoint_for}
@@ -17,26 +17,41 @@ module Trailblazer
17
17
  end
18
18
 
19
19
  # @experimental
20
+ # TODO: test application_controller with and without dsl/api
21
+
20
22
  def self.module(framework: :rails, api: false, dsl: false, application_controller: false)
21
- if api
23
+ if application_controller && !api && !dsl # FIXME: not tested! this is useful for an actual AppController with block_options or flow_options settings, "globally"
24
+ Module.new do
25
+ def self.included(includer)
26
+ includer.extend(Controller) # only ::directive and friends.
27
+ end
28
+ end
29
+ elsif api
22
30
  Module.new do
31
+ @application_controller = application_controller
23
32
  def self.included(includer)
24
- includer.extend(Controller)
25
- includer.include(InstanceMethods)
33
+ if @application_controller
34
+ includer.extend Controller
35
+ end
26
36
  includer.include(InstanceMethods::API)
27
37
  end
28
38
  end
29
- elsif dsl and !application_controller
39
+ elsif dsl
30
40
  Module.new do
41
+ @application_controller = application_controller
31
42
  def self.included(includer)
32
- # includer.extend Trailblazer::Endpoint::Controller
43
+ if @application_controller
44
+ includer.extend Controller
45
+ end
33
46
  includer.include Trailblazer::Endpoint::Controller::InstanceMethods::DSL
34
47
  includer.include Trailblazer::Endpoint::Controller::Rails
35
48
  includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultBlocks
36
49
  includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultParams
37
50
  includer.include Trailblazer::Endpoint::Controller::Rails::Process
38
51
  end
39
- end
52
+ end # Module
53
+ else
54
+ raise
40
55
  end
41
56
  end
42
57
 
@@ -77,18 +92,19 @@ module Trailblazer
77
92
  extended.directive(:endpoints, ->(*) { {} })
78
93
  end
79
94
 
80
- def endpoint(name, **options, &block)
95
+ # Builds and registers an endpoint in a controller class.
96
+ def endpoint(name=nil, **options, &block)
81
97
  options = options.merge(protocol_block: block) if block_given?
82
98
 
83
- return generic_endpoint_config(**name, **options) if name.is_a?(Hash)
84
- endpoint_config(name, **options)
99
+ return generic_endpoint_config(**options) if name.nil?
100
+
101
+ build_endpoint(name, **options)
85
102
  end
86
103
 
87
- def generic_endpoint_config(protocol:, adapter:, **options)
104
+ # Configures generic {:adapter}, {:protocol}, etc.
105
+ def generic_endpoint_config(**options)
88
106
  self.singleton_class.define_method :generic_options do |ctx,**|
89
107
  {
90
- protocol: protocol,
91
- adapter: adapter,
92
108
  **options
93
109
  }
94
110
  end
@@ -96,13 +112,12 @@ module Trailblazer
96
112
  directive :generic_options, method(:generic_options) # FIXME: do we need this?
97
113
  end
98
114
 
99
- def endpoint_config(name, **options)
100
- puts "~~~~~~~~~~~~~~~config"
101
- build_options = options_for(:generic_options, {}).merge(options) # DISCUSS: why don't we add this as another directive option/step?
115
+ def build_endpoint(name, domain_activity: name, **options)
116
+ build_options = options_for(:generic_options, {}).merge(domain_activity: domain_activity, **options) # DISCUSS: why don't we add this as another directive option/step?
102
117
 
103
- endpoint = Trailblazer::Endpoint.build(build_options)
118
+ endpoint = Trailblazer::Endpoint.build(**build_options)
104
119
 
105
- directive :endpoints, ->(*) { {name => endpoint} }
120
+ directive :endpoints, ->(*) { {name.to_s => endpoint} }
106
121
  end
107
122
 
108
123
  end
@@ -111,15 +126,33 @@ module Trailblazer
111
126
  module InstanceMethods
112
127
 
113
128
  def endpoint_for(name, config_source: self.class)
114
- config_source.options_for(:endpoints, {}).fetch(name) # TODO: test non-existant endpoint
129
+ config_source.options_for(:endpoints, {}).fetch(name.to_s) # TODO: test non-existant endpoint
115
130
  end
116
131
 
117
132
  module DSL
118
133
  def endpoint(name, **action_options, &block)
119
- action_options = {controller: self}.merge(action_options) # FIXME: redundant with {DSL#endpoint}
134
+ action_options = {controller: self}.merge(action_options) # FIXME: redundant with {API#endpoint}
120
135
 
121
136
  endpoint = endpoint_for(name)
122
137
 
138
+ # raise name.inspect unless block_given?
139
+ # TODO: check {dsl: false}
140
+ # unless block_given? # FIXME
141
+ # config_source = self.class # FIXME
142
+ # block_options = config_source.options_for(:options_for_block_options, **action_options)
143
+ # block_options = Trailblazer::Endpoint::Options.merge_with(action_options, block_options)
144
+
145
+ # signal, (ctx, _) = Trailblazer::Endpoint::Controller.advance_endpoint_for_controller(
146
+ # endpoint: endpoint,
147
+ # block_options: block_options,
148
+ # config_source: config_source,
149
+ # **action_options
150
+ # )
151
+
152
+ # return ctx
153
+ # end
154
+
155
+
123
156
  invoke_endpoint_with_dsl(endpoint: endpoint, **action_options, &block)
124
157
  end
125
158
 
@@ -132,7 +165,7 @@ module Trailblazer
132
165
  def endpoint(name, config_source: self.class, **action_options)
133
166
  endpoint = endpoint_for(name, config_source: config_source)
134
167
 
135
- action_options = {controller: self}.merge(action_options) # FIXME: redundant with {DSL#endpoint}
168
+ action_options = {controller: self}.merge(action_options) # FIXME: redundant with {InstanceMethods#endpoint}
136
169
 
137
170
  block_options = config_source.options_for(:options_for_block_options, **action_options)
138
171
  block_options = Trailblazer::Endpoint::Options.merge_with(action_options, block_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
@@ -67,29 +67,6 @@ module Trailblazer
67
67
  target.instance_variable_set(:@config, config)
68
68
  end
69
69
 
70
- class State < Module
71
- def initialize(normalizer, config)
72
- @normalizer = normalizer
73
- @config = config
74
- end
75
-
76
- # called once when extended in {ApplicationController}.
77
- def extended(extended)
78
- super
79
-
80
- extended.extend(Inherited)
81
- Normalizer.add_normalizer!(extended, @normalizer, @config)
82
- end
83
-
84
- end
85
- module Inherited
86
- def inherited(subclass)
87
- super
88
-
89
- Normalizer.add_normalizer!(subclass, @normalizer, @config)
90
- end
91
- end
92
-
93
70
  def self.add(normalizer, directive_name, options)
94
71
  Class.new(normalizer) do
95
72
  options.collect do |callable|
@@ -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,102 @@
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] # FIXME: use {#insert_copy_from_domain_ctx!}
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, before: :domain_activity) # FIXME: `:before` untested!
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: before
88
+ end
89
+ end
90
+ end
91
+
92
+ def insert_copy_from_domain_ctx!(protocol, variables, after: :domain_activity) # FIXME: `:after` untested!
93
+ variables.each do |domain_name, endpoint_name|
94
+ protocol.module_eval do
95
+ pass ->(ctx, domain_ctx:, **) { ctx[endpoint_name] = domain_ctx[domain_name] if domain_ctx.key?(domain_name) },
96
+ id: :"copy_[#{endpoint_name.inspect}]_from_domain_ctx[#{domain_name.inspect}]", after: after
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ 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
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Endpoint
3
- VERSION = "0.0.5"
3
+ VERSION = "0.0.9"
4
4
  end
5
5
  end
@@ -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
22
49
 
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
50
+ if serialize || deserialize
51
+ Protocol::Controller.insert_copy_to_domain_ctx!(self, {:resume_data => :resume_data})
52
+ end
25
53
 
26
- # puts Trailblazer::Developer.render(protocol)
27
- # puts
54
+ if find_process_model
55
+ Protocol::Controller.insert_find_process_model!(self, before: :policy) # TODO: test before: :policy
56
+ end
57
+
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,19 +76,11 @@ 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
- def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:, invoke: Trailblazer::Activity::TaskWrap.method(:invoke)) # invoke: Trailblazer::Developer.method(:wtf?)
81
+ def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:, invoke: Trailblazer::Activity::TaskWrap.method(:invoke))
82
+ # def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:, invoke: Trailblazer::Developer.method(:wtf?))
83
+
67
84
  # args[1] = args[1].merge(focus_on: { variables: [:returned], steps: :invoke_workflow })
68
85
 
69
86
  # signal, (endpoint_ctx, _ ) = Trailblazer::Developer.wtf?(activity, args)
@@ -114,7 +131,7 @@ module Trailblazer
114
131
  end
115
132
 
116
133
  # FIXME: name will change! this is for controllers, only!
117
- def self.advance_from_controller(endpoint, success_block:, failure_block:, protocol_failure_block: protocol_failure_block, **argument_options)
134
+ def self.advance_from_controller(endpoint, success_block:, failure_block:, protocol_failure_block:, **argument_options)
118
135
  args = Trailblazer::Endpoint.arguments_for(argument_options)
119
136
 
120
137
  signal, (ctx, _ ) = Trailblazer::Endpoint.with_or_etc(
@@ -144,3 +161,6 @@ require "trailblazer/endpoint/adapter"
144
161
  require "trailblazer/endpoint/dsl"
145
162
  require "trailblazer/endpoint/controller"
146
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"
@@ -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
@@ -311,4 +312,63 @@ class DocsControllerTest < Minitest::Spec
311
312
  end
312
313
  end
313
314
 
315
+ class ControllerEndpointMethodTest < Minitest::Spec
316
+ # Test {Controller::endpoint}
317
+
318
+ class Protocol < Trailblazer::Endpoint::Protocol
319
+ def policy(*); true; end
320
+ def authenticate(*); true; end
321
+ end
322
+
323
+ class BasicController
324
+ include Trailblazer::Endpoint::Controller.module(api: true, application_controller: true)
325
+
326
+ directive :options_for_block_options, Trailblazer::Endpoint::Controller.method(:options_for_block_options)
327
+
328
+ endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web
329
+
330
+ def head(status)
331
+ @status = status
332
+ end
333
+
334
+ def self.options_for_block_options(ctx, controller:, **)
335
+ {
336
+ success_block: ->(ctx, endpoint_ctx:, **) { controller.head("#{ctx[:op]}") },
337
+ failure_block: ->(ctx, status:, **) { },
338
+ protocol_failure_block: ->(ctx, status:, **) { }
339
+ }
340
+ end
341
+
342
+ directive :options_for_block_options, method(:options_for_block_options)
343
+ end
344
+
345
+ class RodaController < BasicController
346
+ class Create < Trailblazer::Activity::Railway
347
+ def save(ctx, **); ctx[:op] = self.class; end;
348
+ step :save
349
+ end
350
+ class Update < Create
351
+ end
352
+
353
+ # {Controller::endpoint}: {:domain_activity} defaults to {name} when not given
354
+ endpoint Create # class {name}s are ok
355
+ endpoint :update, domain_activity: Update # symbol {name} is ok
356
+
357
+ def show
358
+ endpoint Create
359
+ @status
360
+ end
361
+
362
+ def update
363
+ endpoint :update
364
+ @status
365
+ end
366
+ end
367
+
368
+
369
+ it "what" do
370
+ RodaController.new.show.must_equal %{ControllerEndpointMethodTest::RodaController::Create}
371
+ RodaController.new.update.must_equal %{ControllerEndpointMethodTest::RodaController::Update}
372
+ end
373
+ end
314
374
 
@@ -13,7 +13,23 @@ gem 'sqlite3', '~> 1.4'
13
13
  # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
14
14
  # gem 'rack-cors'
15
15
 
16
- gem "trailblazer-operation"
16
+ # gem "trailblazer-operation"
17
+ gem "trailblazer-operation", path: "../../../trailblazer-operation"
18
+ gem "trailblazer-context", path: "../../../trailblazer-context"
19
+ gem "trailblazer-activity-dsl-linear", path: "../../../trailblazer-activity-dsl-linear"
20
+ gem "trailblazer-activity", path: "../../../trailblazer-activity"
21
+
22
+
17
23
  gem "trailblazer-endpoint", path: "../../."
18
24
  gem "jwt"
19
25
  gem "multi_json"
26
+ gem "minitest-line"
27
+ gem "trailblazer-cells"
28
+ gem "cells-erb"
29
+ gem "cells-rails"
30
+
31
+
32
+ # FIXME
33
+ # gem "trailblazer-workflow", path: "../../../trailblazer-workflow"
34
+ # gem "trailblazer-activity-dsl-linear", path: "../../../trailblazer-activity-dsl-linear"
35
+ # gem "trailblazer-activity-dsl-linear", ">= 0.3.4"