trailblazer-endpoint 0.0.3 → 0.0.8

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +16 -0
  3. data/Appraisals +5 -0
  4. data/CHANGES.md +27 -0
  5. data/README.md +40 -5
  6. data/Rakefile +7 -1
  7. data/gemfiles/rails_app.gemfile +12 -0
  8. data/lib/trailblazer/endpoint.rb +50 -14
  9. data/lib/trailblazer/endpoint/adapter.rb +30 -121
  10. data/lib/trailblazer/endpoint/builder.rb +1 -1
  11. data/lib/trailblazer/endpoint/controller.rb +217 -1
  12. data/lib/trailblazer/endpoint/dsl.rb +8 -3
  13. data/lib/trailblazer/endpoint/options.rb +16 -69
  14. data/lib/trailblazer/endpoint/protocol.rb +7 -11
  15. data/lib/trailblazer/endpoint/protocol/cipher.rb +27 -0
  16. data/lib/trailblazer/endpoint/protocol/controller.rb +102 -0
  17. data/lib/trailblazer/endpoint/protocol/find_process_model.rb +15 -0
  18. data/lib/trailblazer/endpoint/version.rb +1 -1
  19. data/test/adapter/api_test.rb +6 -11
  20. data/test/adapter/web_test.rb +2 -5
  21. data/test/config_test.rb +25 -0
  22. data/test/docs/controller_test.rb +220 -73
  23. data/test/endpoint_test.rb +1 -1
  24. data/test/rails-app/.gitignore +8 -2
  25. data/test/rails-app/.ruby-version +1 -0
  26. data/test/rails-app/Gemfile +21 -9
  27. data/test/rails-app/Gemfile.lock +174 -118
  28. data/test/rails-app/app/concepts/app/api/v1/representer/errors.rb +16 -0
  29. data/test/rails-app/app/concepts/auth/jwt.rb +35 -0
  30. data/test/rails-app/app/concepts/auth/operation/authenticate.rb +32 -0
  31. data/test/rails-app/app/concepts/auth/operation/policy.rb +9 -0
  32. data/test/rails-app/app/concepts/song/cell/create.rb +4 -0
  33. data/test/rails-app/app/concepts/song/cell/new.rb +4 -0
  34. data/test/rails-app/app/concepts/song/operation/create.rb +17 -0
  35. data/test/rails-app/app/concepts/song/operation/show.rb +10 -0
  36. data/test/rails-app/app/concepts/song/representer.rb +5 -0
  37. data/test/rails-app/app/concepts/song/view/create.erb +1 -0
  38. data/test/rails-app/app/concepts/song/view/new.erb +1 -0
  39. data/test/rails-app/app/controllers/api/v1/songs_controller.rb +41 -0
  40. data/test/rails-app/app/controllers/application_controller.rb +8 -1
  41. data/test/rails-app/app/controllers/application_controller/api.rb +107 -0
  42. data/test/rails-app/app/controllers/application_controller/web.rb +46 -0
  43. data/test/rails-app/app/controllers/auth_controller.rb +44 -0
  44. data/test/rails-app/app/controllers/home_controller.rb +5 -0
  45. data/test/rails-app/app/controllers/songs_controller.rb +254 -13
  46. data/test/rails-app/app/models/song.rb +6 -0
  47. data/test/rails-app/app/models/user.rb +7 -0
  48. data/test/rails-app/bin/bundle +114 -0
  49. data/test/rails-app/bin/rails +4 -0
  50. data/test/rails-app/bin/rake +4 -0
  51. data/test/rails-app/bin/setup +33 -0
  52. data/test/rails-app/config/application.rb +26 -3
  53. data/test/rails-app/config/credentials.yml.enc +1 -0
  54. data/test/rails-app/config/database.yml +2 -2
  55. data/test/rails-app/config/environments/development.rb +7 -17
  56. data/test/rails-app/config/environments/production.rb +28 -23
  57. data/test/rails-app/config/environments/test.rb +10 -12
  58. data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -4
  59. data/test/rails-app/config/initializers/cors.rb +16 -0
  60. data/test/rails-app/config/initializers/trailblazer.rb +2 -0
  61. data/test/rails-app/config/locales/en.yml +11 -1
  62. data/test/rails-app/config/master.key +1 -0
  63. data/test/rails-app/config/routes.rb +27 -4
  64. data/test/rails-app/db/schema.rb +15 -0
  65. data/test/rails-app/test/controllers/api_songs_controller_test.rb +87 -0
  66. data/test/rails-app/test/controllers/songs_controller_test.rb +152 -145
  67. data/test/rails-app/test/test_helper.rb +7 -1
  68. data/test/test_helper.rb +0 -2
  69. data/trailblazer-endpoint.gemspec +2 -1
  70. metadata +69 -24
  71. data/test/rails-app/config/initializers/cookies_serializer.rb +0 -5
  72. data/test/rails-app/config/initializers/new_framework_defaults.rb +0 -24
  73. data/test/rails-app/config/initializers/session_store.rb +0 -3
  74. data/test/rails-app/config/secrets.yml +0 -22
  75. data/test/rails-app/test/helpers/.keep +0 -0
  76. data/test/rails-app/test/integration/.keep +0 -0
  77. data/test/rails-app/test/mailers/.keep +0 -0
  78. data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
  79. data/test/rails-app/vendor/assets/stylesheets/.keep +0 -0
@@ -0,0 +1,32 @@
1
+ # FIXME: this is not final, yet!
2
+ class Auth::Operation::Authenticate < Trailblazer::Activity::Railway
3
+ step :verify_token
4
+ step :is_token_expired?
5
+ step :set_current_user
6
+
7
+ def verify_token(ctx, request:, **)
8
+ auth_header = request.headers['Authorization'] || ""
9
+ jwt_encoded_token = auth_header.split(' ').last
10
+ ctx[:encoded_jwt_token] = jwt_encoded_token
11
+
12
+ # FIXME
13
+ #raise StandardError if JwtService.expired?(jwt_encoded_token)
14
+ ctx[:decoded_jwt_token] = Auth::Jwt.decode(jwt_encoded_token)
15
+ end
16
+
17
+ def is_token_expired?(ctx, decoded_jwt_token:, **)
18
+ expiration_integer = decoded_jwt_token.first['exp']
19
+ return false if expiration_integer.nil?
20
+ return false if (expiration_integer - DateTime.now.to_i) <= 0
21
+ return true
22
+ end
23
+
24
+ def set_current_user(ctx, decoded_jwt_token:, **)
25
+ user_id = decoded_jwt_token.first['user_id']
26
+
27
+ ctx[:current_user] = User.find_by(
28
+ id: user_id
29
+ )
30
+ true # FIXME
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ module Auth
2
+ module Operation
3
+ class Policy
4
+ def self.call(ctx, domain_ctx:, **)
5
+ domain_ctx[:params][:policy] == false ? false : true
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module Song::Cell
2
+ class Create < Trailblazer::Cell
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Song::Cell
2
+ class New < Trailblazer::Cell
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module Song::Operation
2
+ class Create < Trailblazer::Operation
3
+ step :contract
4
+ step :model
5
+ # step :validate
6
+ # step :save
7
+
8
+ def model(ctx, params:, **)
9
+ return unless params[:id]
10
+ ctx[:model] = Song.new(params[:id])
11
+ end
12
+
13
+ def contract(ctx, **)
14
+ ctx[:contract] = Struct.new(:errors).new()
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module Song::Operation
2
+ class Show < Trailblazer::Operation
3
+ step :model, Output(:failure) => End(:not_found)
4
+
5
+ def model(ctx, params:, **)
6
+ return unless params[:id] == "1"
7
+ ctx[:model] = Song.new(params[:id])
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class Song::Representer < Representable::Decorator
2
+ include Representable::JSON
3
+
4
+ property :id
5
+ end
@@ -0,0 +1 @@
1
+ <div><%= model %><%= options[:current_user] %></div>
@@ -0,0 +1 @@
1
+ <div><%= model %></div>
@@ -0,0 +1,41 @@
1
+ #:controller
2
+ module Api
3
+ module V1
4
+ #:endpoint
5
+ class SongsController < ApplicationController::Api
6
+ endpoint Song::Operation::Create
7
+ endpoint Song::Operation::Show do
8
+ {Output(:not_found) => Track(:not_found)} # add additional wiring to {domain_activity}
9
+ end
10
+ #:endpoint end
11
+
12
+ #:create
13
+ def create
14
+ endpoint Song::Operation::Create, representer_class: Song::Representer
15
+ end
16
+ #:create end
17
+
18
+ #~empty
19
+ def show
20
+ endpoint Song::Operation::Show, representer_class: Song::Representer
21
+ end
22
+
23
+ def show_with_options
24
+ endpoint Song::Operation::Show, representer_class: Song::Representer, protocol_failure_block: ->(ctx, endpoint_ctx:, **) { head endpoint_ctx[:status] + 1 }
25
+ end
26
+
27
+ class WithOptionsController < ApplicationController::Api
28
+ endpoint Song::Operation::Show do {Output(:not_found) => Track(:not_found)} end
29
+
30
+ #:show-options
31
+ def show
32
+ endpoint Song::Operation::Show, representer_class: Song::Representer,
33
+ protocol_failure_block: ->(ctx, endpoint_ctx:, **) { head endpoint_ctx[:status] + 1 }
34
+ end
35
+ #:show-options end
36
+ end
37
+ #~empty end
38
+ end
39
+ end
40
+ end
41
+ #:controller end
@@ -1,3 +1,10 @@
1
+ require "trailblazer/endpoint/controller"
2
+
1
3
  class ApplicationController < ActionController::Base
2
- protect_from_forgery with: :exception
4
+ def self.current_user_in_domain_ctx
5
+ ->(_ctx, ((ctx, a), b)) { ctx[:domain_ctx][:current_user] = ctx[:current_user]; [_ctx, [[ctx, a], b]] } # FIXME: extract to lib?
6
+ end
3
7
  end
8
+
9
+
10
+ # directive
@@ -0,0 +1,107 @@
1
+ #:app-controller
2
+ #:app-include
3
+ class ApplicationController::Api < ApplicationController
4
+ include Trailblazer::Endpoint::Controller.module(api: true, application_controller: true)
5
+ #:app-include end
6
+
7
+ def self.options_for_block_options(ctx, controller:, **)
8
+ response_block = ->(ctx, endpoint_ctx:, **) do
9
+ controller.render json: endpoint_ctx[:representer], status: endpoint_ctx[:status]
10
+ end
11
+
12
+ {
13
+ success_block: response_block,
14
+ failure_block: response_block,
15
+ protocol_failure_block: response_block
16
+ }
17
+ end
18
+
19
+ directive :options_for_block_options, method(:options_for_block_options)
20
+ #:app-controller end
21
+
22
+ #:options_for_endpoint
23
+ def self.options_for_endpoint(ctx, controller:, **)
24
+ {
25
+ request: controller.request,
26
+ errors_representer_class: App::Api::V1::Representer::Errors,
27
+ errors: Trailblazer::Endpoint::Adapter::API::Errors.new,
28
+ }
29
+ end
30
+
31
+ directive :options_for_endpoint, method(:options_for_endpoint)
32
+ #:options_for_endpoint end
33
+
34
+ #:options_for_domain_ctx
35
+ def self.options_for_domain_ctx(ctx, controller:, **) # TODO: move to ApplicationController
36
+ {
37
+ params: controller.params,
38
+ }
39
+ end
40
+
41
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
42
+ #:options_for_domain_ctx end
43
+
44
+ #:protocol
45
+ class Protocol < Trailblazer::Endpoint::Protocol
46
+ step Auth::Operation::Policy, inherit: true, id: :policy, replace: :policy
47
+ step Subprocess(Auth::Operation::Authenticate), inherit: true, id: :authenticate, replace: :authenticate
48
+ end
49
+ #:protocol end
50
+
51
+ #:adapter
52
+ module Adapter
53
+ class Representable < Trailblazer::Endpoint::Adapter::API
54
+ step :render # added before End.success
55
+ step :render_errors, after: :_422_status, magnetic_to: :failure, Output(:success) => Track(:failure)
56
+ step :render_errors, after: :protocol_failure, magnetic_to: :fail_fast, Output(:success) => Track(:fail_fast), id: :render_protocol_failure_errors
57
+
58
+ def render(ctx, domain_ctx:, representer_class:, **) # this is what usually happens in your {Responder}.
59
+ ctx[:representer] = representer_class.new(domain_ctx[:model] || raise("no model found!"))
60
+ end
61
+
62
+ def render_errors(ctx, errors:, errors_representer_class:, **) # TODO: extract with {render}
63
+ ctx[:representer] = errors_representer_class.new(errors)
64
+ end
65
+
66
+ Trailblazer::Endpoint::Adapter::API.insert_error_handler_steps!(self)
67
+ include Trailblazer::Endpoint::Adapter::API::Errors::Handlers # handler methods to set an error message.
68
+ end # Representable
69
+ end
70
+ #:adapter end
71
+
72
+ puts Trailblazer::Developer.render(Adapter::Representable)
73
+
74
+ #:endpoint
75
+ # app/controllers/application_controller/api.rb
76
+ endpoint protocol: Protocol, adapter: Adapter::Representable do
77
+ # {Output(:not_found) => Track(:not_found)}
78
+ {}
79
+ end
80
+ #:endpoint end
81
+ end
82
+
83
+
84
+ # header 'Authorization', "Bearer #{result['jwt_token']}" if result['jwt_token']
85
+
86
+
87
+ # ΓΞΞ Protocol ΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞ˥
88
+ # | (Start)--->[Authenticate]-->[Policy]-->[Show]----------->((success)) |
89
+ # | | | | L------------->((failure)) |
90
+ # | | | L------------->((not_found)) |
91
+ # | | L------------------->((not_authorized)) |
92
+ # | L----------------------------->((not_authenticated)) |
93
+ # | ((invalid_data)) |
94
+ # LΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞ˩
95
+ #--˥˩ ˪
96
+
97
+ # ΓΞΞ Adapter::Representable ΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞ˥
98
+ # | |
99
+ # | (Start)---> ΓΞΞ Protocol ΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞ˥ |
100
+ # | (Start)--->[Authenticate]-->[Policy]-->[Show]----------->((success)) |--->[_200_status]--->[render]---------------------------------------------------------------->((success)) |
101
+ # | | | | L------------->((failure)) |-˥ |
102
+ # | | | L------------->((not_found)) |-|------------------------------->[_404_status]-----------v |
103
+ # | | L------------------->((not_authorized)) |-|->[handle_not_authorized]------>[_403_status]--->[protocol_failure]--->[render_errors]--->((fail_fast)) |
104
+ # | L----------------------------->((not_authenticated)) |-|->[handle_not_authenticated]--->[_401_status]-----------^ |
105
+ # | ((invalid_data)) |-┴->[handle_invalid_data]-------->[_422_status]--->[render_errors]--------------------------->((failure)) |
106
+ # LΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞ˩ |
107
+ # LΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞΞ˩
@@ -0,0 +1,46 @@
1
+ #:app-include
2
+ #:options
3
+ #:protocol
4
+ #:generic
5
+ class ApplicationController::Web < ApplicationController
6
+ #~pskip
7
+ #~gskip
8
+ include Trailblazer::Endpoint::Controller.module(dsl: true, application_controller: true)
9
+ #:app-include end
10
+
11
+ def self.options_for_endpoint(ctx, controller:, **)
12
+ {
13
+ session: controller.session,
14
+ }
15
+ end
16
+
17
+ directive :options_for_endpoint, method(:options_for_endpoint)
18
+ #:options end
19
+
20
+ # directive :options_for_flow_options, method(:options_for_flow_options)
21
+ # directive :options_for_block_options, method(:options_for_block_options)
22
+ #~pskip end
23
+ class Protocol < Trailblazer::Endpoint::Protocol
24
+ # provide method for {step :authenticate}
25
+ def authenticate(ctx, session:, **)
26
+ ctx[:current_user] = User.find_by(id: session[:user_id])
27
+ end
28
+
29
+ # provide method for {step :policy}
30
+ def policy(ctx, domain_ctx:, **)
31
+ Policy.(domain_ctx)
32
+ end
33
+
34
+ Trailblazer::Endpoint::Protocol::Controller.insert_copy_to_domain_ctx!(self, :current_user => :current_user)
35
+ Trailblazer::Endpoint::Protocol::Controller.insert_copy_from_domain_ctx!(self, :model => :process_model)
36
+ end
37
+ #:protocol end
38
+ Policy = ->(domain_ctx) { domain_ctx[:params][:policy] == "false" ? false : true }
39
+ #~gskip end
40
+ endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web
41
+ end
42
+ #:generic end
43
+
44
+ # do
45
+ # {Output(:not_found) => Track(:not_found)}
46
+ # end
@@ -0,0 +1,44 @@
1
+ class AuthController < ApplicationController::Web
2
+ # We could use a fully-fledged operation here, with a contract and whatnot.
3
+ def self.authenticate(ctx, request:, params:, **)
4
+ if params[:username] == "yogi@trb.to" && params[:password] == "secret"
5
+ ctx[:current_user] = User.find_by(email: params[:username])
6
+
7
+ return true
8
+ else
9
+ return false # let's be extra explicit!
10
+ end
11
+ end
12
+
13
+ endpoint("sign_in", domain_activity: Class.new(Trailblazer::Activity::Railway)) do
14
+ # step nil, delete: :domain_activity
15
+ step nil, delete: :policy
16
+ step AuthController.method(:authenticate), replace: :authenticate, inherit: true, id: :authenticate
17
+
18
+ {}
19
+ end
20
+
21
+ def self.options_for_endpoint(ctx, controller:, **)
22
+ {
23
+ params: controller.params,
24
+ request: controller.request,
25
+ }
26
+ end
27
+
28
+ directive :options_for_endpoint, method(:options_for_endpoint)
29
+
30
+ def sign_in
31
+ endpoint "sign_in" do |ctx, current_user:, **|
32
+ session[:user_id] = current_user.id # Working on {session} is HTTP-specific and done in the controller.
33
+
34
+ redirect_to dashboard_path
35
+ # render html: "Yes!"
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def dashboard_path
42
+ "/"
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ class HomeController < ApplicationController
2
+ def dashboard
3
+ render html: "Yo!"
4
+ end
5
+ end
@@ -1,26 +1,267 @@
1
- class SongsController < ApplicationController
2
- require "trailblazer/endpoint/rails"
3
- include Trailblazer::Endpoint::Controller
1
+ #:endpoint
2
+ #:or
3
+ #:create
4
+ class SongsController < ApplicationController::Web
5
+ endpoint Song::Operation::Create
4
6
 
7
+ #:endpoint end
5
8
  def create
6
- endpoint Create, path: songs_path, args: [ params, { "user.current" => ::Module } ]
9
+ endpoint Song::Operation::Create do |ctx, current_user:, model:, **|
10
+ render html: cell(Song::Cell::Create, model, current_user: current_user)
11
+ end.Or do |ctx, contract:, **| # validation failure
12
+ render html: cell(Song::Cell::New, contract)
13
+ end
14
+ end
15
+ #:create end
16
+
17
+ #~oskip
18
+ class CreateOrController < SongsController
19
+ #~oskip end
20
+ def create
21
+ endpoint Song::Operation::Create do |ctx, current_user:, model:, **|
22
+ render html: cell(Song::Cell::Create, model, current_user: current_user)
23
+ end.Or do |ctx, contract:, **| # validation failure
24
+ render json: contract.errors, status: 422
25
+ end
26
+ end
27
+ end
28
+ #:or end
29
+
30
+ def create_without_block
31
+ endpoint Song::Operation::Create
32
+ end
33
+
34
+ class CreateWithOptionsController < SongsController
35
+ #:create-options
36
+ def create
37
+ endpoint Song::Operation::Create, session: {user_id: 2} do |ctx, current_user:, model:, **|
38
+ render html: cell(Song::Cell::Create, model, current_user: current_user)
39
+ end
40
+ end
41
+ #:create-options end
42
+ end
43
+
44
+
45
+ class CreateWithOptionsForDomainCtxController < SongsController
46
+ #:domain_ctx
47
+ def create
48
+ endpoint Song::Operation::Create, options_for_domain_ctx: {params: {id: 999}} do |ctx, model:, **|
49
+ render html: cell(Song::Cell::Create, model)
50
+ end
51
+ end
52
+ #:domain_ctx end
53
+ end
54
+
55
+ class CreateEndpointCtxController < SongsController
56
+ #:endpoint_ctx
57
+ def create
58
+ endpoint Song::Operation::Create do |ctx, endpoint_ctx:, **|
59
+ render html: "Created", status: endpoint_ctx[:status]
60
+ end.Or do |ctx, **| # validation failure
61
+ #~empty
62
+ #~empty end
63
+ end
64
+ end
65
+ #:endpoint_ctx end
66
+ end
67
+
68
+ # end.Or do |ctx, endpoint_ctx:, **| # validation failure
69
+ # render json: endpoint_ctx.keys, status: 422
70
+ # end
71
+
72
+
73
+ class CreateWithProtocolFailureController < SongsController
74
+ #:protocol_failure
75
+ def create_with_protocol_failure
76
+ endpoint Song::Operation::Create do |ctx, **|
77
+ redirect_to dashboard_path
78
+ end.protocol_failure do |ctx, **|
79
+ render html: "wrong login, app crashed", status: 500
80
+ end
81
+ end
82
+ #:protocol_failure end
83
+ end
84
+
85
+ # {:process_model} is copied from {domain_ctx[:model]} via {#insert_copy_from_domain_ctx!}.
86
+ class Create1Controller < SongsController
87
+ def create
88
+ endpoint Song::Operation::Create do |ctx, endpoint_ctx:, **|
89
+ render html: endpoint_ctx[:process_model].inspect.html_safe
90
+ end
91
+ end
92
+ end
93
+
94
+
95
+ # endpoint_ctx
96
+ # :resume_data
97
+ # domain_ctx
98
+ # :resume_data (copy)
99
+
100
+
101
+ # authenticate
102
+
103
+ # deserialize ==> {resume_data: {id: 1}}
104
+ # deserialize_process_model_id_from_resume_data
105
+
106
+ # find_process_model
107
+ # policy
108
+ # domain_activity
109
+
110
+ # serialize suspend_data and deserialize resume_data
111
+ class SerializeController < SongsController
112
+ endpoint Song::Operation::Create,
113
+ protocol: ApplicationController::Web::Protocol
114
+ # serialize: true
115
+
116
+ def self.options_for_block_options(ctx, **)
117
+ {
118
+ invoke: Trailblazer::Developer.method(:wtf?) # FIXME
119
+ }
120
+ end
121
+
122
+
123
+ def self.options_for_endpoint(ctx, controller:, **)
124
+ {
125
+ cipher_key: Rails.application.config.cipher_key,
126
+
127
+ encrypted_resume_data: controller.params[:encrypted_resume_data],
128
+ }
129
+ end
130
+
131
+ directive :options_for_block_options, method(:options_for_block_options)
132
+ directive :options_for_endpoint, method(:options_for_endpoint)
133
+
134
+ def create
135
+ encrypted_value = Trailblazer::Workflow::Cipher.encrypt_value({}, cipher_key: cipher_key, value: JSON.dump({id: "findings received", class: Object}))
136
+
137
+ endpoint Song::Operation::Create, encrypted_resume_data: encrypted_value, process_model_from_resume_data: false do |ctx, current_user:, endpoint_ctx:, **|
138
+ render html: cell(Song::Cell::Create, model, current_user: current_user)
139
+ end.Or do |ctx, contract:, **| # validation failure
140
+ render html: cell(Song::Cell::New, contract)
141
+ end
142
+ end
7
143
  end
8
144
 
9
- def update
10
- endpoint Update, path: songs_path, args: [params]
145
+ # TODO: not really a doc test.
146
+ # the entire deserialize cycle is skipped since we only use {:serialize}
147
+ class Serialize1Controller < SerializeController
148
+ class Create < Trailblazer::Operation
149
+ pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
150
+ end
151
+
152
+ endpoint "Create",
153
+ domain_activity: Create,
154
+ serialize: true,
155
+ deserialize: true
156
+
157
+ def create
158
+ # {:model} and {:memory} are from the domain_ctx.
159
+ # {:encrypted_suspend_data} from endpoint.
160
+ endpoint "Create" do |ctx, model:, endpoint_ctx:, **|
161
+ render html: "#{model.inspect}/#{ctx[:memory].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
162
+ end.Or do |ctx, **| # validation failure
163
+ render html: "xxx", status: 500
164
+ end
165
+ end
11
166
  end
12
167
 
13
- def update_with_user
14
- endpoint Update, path: songs_path, args: [ params, { "user.current" => ::Module } ]
168
+ # TODO: not really a doc test.
169
+ # ---------deserialize cycle is skipped.
170
+ # we serialize {:remember}.
171
+ class Serialize2Controller < Serialize1Controller # "render confirm page"
172
+ class Create < Trailblazer::Operation
173
+ pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
174
+ step ->(ctx, **) { ctx[:suspend_data] = {remember: OpenStruct.new(id: 1), id: 9} } # write to domain_ctx[:suspend_data]
175
+ end
176
+
177
+ endpoint "Create",
178
+ domain_activity: Create,
179
+ serialize: true
15
180
  end
16
181
 
17
- def show
18
- endpoint Show, path: songs_path, args: [ params, { "user.current" => ::Module } ]
182
+ # we can read from {:resume_data}
183
+ class Serialize3Controller < Serialize1Controller # "process submitted confirm page"
184
+ class Create < Trailblazer::Operation
185
+ pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
186
+ pass ->(ctx, **) { ctx[:memory] = ctx[:resume_data] } # read/process the suspended data
187
+ end
188
+
189
+ endpoint "Create",
190
+ domain_activity: Create,
191
+ deserialize: true
19
192
  end
20
193
 
21
- def create_with_custom_handlers
22
- endpoint Create, path: songs_path, args: [ params, { "user.current" => ::Module } ] do |m|
23
- m.created { |result| render json: result["representer.serializer.class"].new(result["model"]), status: 999 }
194
+ # find process_model via id in suspend/resume data (used to be called {process_model_from_resume_data})
195
+ class Serialize4Controller < Serialize1Controller
196
+ class Create < Trailblazer::Operation
197
+ pass ->(ctx, **) { ctx[:model] = ctx.key?(:model) ? ctx[:model] : false }
198
+ pass ->(ctx, **) { ctx[:memory] = ctx[:resume_data] } # read/process the suspended data
199
+ end
200
+
201
+ endpoint "Create",
202
+ domain_activity: Create,
203
+ deserialize: true,
204
+ find_process_model: true,
205
+ deserialize_process_model_id_from_resume_data: true
206
+
207
+ def create
208
+ endpoint "Create", process_model_class: Song do |ctx, endpoint_ctx:, **|
209
+ render html: "#{endpoint_ctx[:process_model_id].inspect}/#{ctx[:memory].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
210
+ end
211
+ end
212
+ end
213
+
214
+ # find process_model from resume
215
+ # FIXME: what is the diff to Controller4?
216
+ class Serialize5Controller < Serialize1Controller
217
+ endpoint "Create",
218
+ domain_activity: Serialize4Controller::Create,
219
+ deserialize: true,
220
+ find_process_model: true,
221
+ deserialize_process_model_id_from_resume_data: true
222
+
223
+ def create
224
+ endpoint "Create", find_process_model: true, process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
225
+ render html: "#{model.inspect}/#{ctx[:memory].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
226
+ end
227
+ end
228
+ end
229
+
230
+ # find process_model from action_options
231
+ class Serialize6Controller < Serialize1Controller
232
+ endpoint "Create",
233
+ domain_activity: Serialize4Controller::Create,
234
+ protocol: ApplicationController::Web::Protocol,
235
+ find_process_model: true
236
+
237
+ def create
238
+ endpoint "Create", find_process_model: true, process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
239
+ render html: "#{model.inspect}/#{endpoint_ctx[:process_model].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
240
+ end
241
+ end
242
+ end
243
+
244
+ # Configure only {:find_process_model} and {:protocol}.
245
+ class Serialize7Controller < Serialize1Controller
246
+ endpoint find_process_model: true # generic setting for all endpoints in this controller.
247
+
248
+ endpoint "Create", # no need to specify {:find_process_model}
249
+ domain_activity: Serialize4Controller::Create
250
+
251
+ endpoint "New",
252
+ find_process_model: false,
253
+ domain_activity: Serialize4Controller::Create
254
+
255
+ def create
256
+ endpoint "Create", process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
257
+ render html: "#{model.inspect}/#{endpoint_ctx[:process_model].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
258
+ end
259
+ end
260
+
261
+ def new
262
+ endpoint "New", process_model_class: Song, process_model_id: params[:id] do |ctx, model:, endpoint_ctx:, **|
263
+ render html: "#{model.inspect}/#{endpoint_ctx[:process_model].inspect}/#{endpoint_ctx[:encrypted_suspend_data]}".html_safe
264
+ end
24
265
  end
25
266
  end
26
267
  end