trailblazer-endpoint 0.0.3 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1f9924623a3e82ffce7398379c645bc0d6502e66d1d827231fb12b108e40978
4
- data.tar.gz: 8a2f575d512c830e3ea29d2cff65ac8aed6f86cf75364a516102664c2928c928
3
+ metadata.gz: be47945174c82b64947b1f14951b66c9395b65caf4bcb221792f50cae38bf25a
4
+ data.tar.gz: 67b8254e1c2775f6909f29cb7779635d38412a3ec972c350ad49cb0e287d1131
5
5
  SHA512:
6
- metadata.gz: 9b817a46e644b31127385bcac23a5ec122a5bdd4cb9fd3a425f4d92a1be3d4e2fb35a70f28d13850902116a892bd445df3e4c795b895557bdf085de7505f2c71
7
- data.tar.gz: fc0f421d1bcfa2c3ccf08f599ffa503c7d0cf7b8a64a387d88270ad68466e00f2cd79654b4a71ead8bf4425a231272b6ef6089dd5e5416d56f4961c7cba0f97d
6
+ metadata.gz: eddf0ed3f16dd28c3fbebe2d9170e763ddc40f785f2ecd752cecd56ef2b9c3bc96c3dd4d8361cf170a9f997025dfd52316b2b9b799fe09dcf2720955903c5240
7
+ data.tar.gz: 2fe89f93da8356a1f13e7b229f1a1137ddd0ee8c453c10858254097a42c1213e3e1dcdd23994eb2f4f16568e66ee696e3c0dfaeec0777f321931da98eca8691c
@@ -0,0 +1,16 @@
1
+ .bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /log
10
+ tmp
11
+ .DS_Store
12
+ log/
13
+ *.iml
14
+ .idea
15
+ *.swp
16
+ gemfiles/*.lock
@@ -0,0 +1,5 @@
1
+ appraise 'rails-app' do
2
+ gem 'rails', '6.0.3.1'
3
+ gem 'sqlite3', '~> 1.4'
4
+ gem "trailblazer-operation"
5
+ end
data/CHANGES.md CHANGED
@@ -1,3 +1,30 @@
1
+ # 0.0.8
2
+
3
+ * Add `Protocol.insert_copy_from_domain_ctx!` to copy from `domain_ctx` to the `endpoint_ctx`.
4
+
5
+ # 0.0.7
6
+
7
+ * BREAKING: Remove `:domain_ctx_filter` in favor of `Controller.insert_copy_to_domain_ctx!`.
8
+ * 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`.
9
+ * 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".
10
+
11
+ # 0.0.6
12
+
13
+ * `Controller::endpoint` short form introduced.
14
+ * Minor changes for `Controller.module`.
15
+ * Lots of cleanups.
16
+
17
+ # 0.0.5
18
+
19
+ * Removed `Protocol::Failure`. Until we have `Railway::End::Failure`, use a normal `Activity::End` everywhere instead of introducing our own.
20
+ * Default `with_or_etc:invoke` is `TaskWrap.invoke`.
21
+
22
+ # 0.0.4
23
+
24
+ * Use new `context-0.3.1`.
25
+ * Don't use `wtf?`.
26
+ * Don't create a `Context` anymore in `Endpoint.arguments_for`.
27
+
1
28
  # 0.0.3
2
29
 
3
30
  * Introduce `Options`.
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.
data/Rakefile CHANGED
@@ -5,6 +5,12 @@ task :default => [:test]
5
5
 
6
6
  Rake::TestTask.new(:test) do |test|
7
7
  test.libs << 'test'
8
- test.test_files = FileList['test/endpoint_test.rb', 'test/docs/*_test.rb', "test/adapter/*_test.rb"]
8
+ test.test_files = FileList['test/endpoint_test.rb', 'test/docs/*_test.rb', "test/adapter/*_test.rb", "test/config_test.rb"]
9
+ test.verbose = true
10
+ end
11
+
12
+ Rake::TestTask.new('test-rails-app') do |test|
13
+ test.libs << 'test'
14
+ test.test_files = FileList['test/rails-app/test/test_helper.rb', 'test/rails-app/test/**/*.rb']
9
15
  test.verbose = true
10
16
  end
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "multi_json"
6
+ gem "minitest-line"
7
+ gem "dry-validation"
8
+ gem "rails", "6.0.3.1"
9
+ gem "sqlite3", "~> 1.4"
10
+ gem "trailblazer-operation"
11
+
12
+ gemspec path: "../"
@@ -2,8 +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, &block)
6
-
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
+ )
7
11
  # special considerations around the {domain_activity} and its taskWrap:
8
12
  #
9
13
  # 1. domain_ctx_filter (e.g. to filter {current_user})
@@ -20,24 +24,49 @@ module Trailblazer
20
24
  # scoping: {:domain_ctx} becomes ctx
21
25
  extensions_options.merge!(Endpoint.options_for_scope_domain_ctx) if scope_domain_ctx # TODO: test flag
22
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)
23
32
 
24
- domain_ctx_filter_callable = [[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["endpoint.domain_ctx_filter", domain_ctx_filter]]]
25
- extensions_options[:extensions] << Trailblazer::Activity::TaskWrap::Extension(merge: domain_ctx_filter_callable) if domain_ctx_filter
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
53
+
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
26
61
 
27
- app_protocol = Class.new(protocol) do
28
62
  step(Subprocess(domain_activity), {inherit: true, id: :domain_activity, replace: :domain_activity,
29
63
 
30
64
  # FIXME: where does this go?
31
65
  }.
32
66
  merge(extensions_options).
33
- merge(instance_exec(&block)) # the block is evaluated in the {Protocol} context.
67
+ merge(instance_exec(&protocol_block)) # the block is evaluated in the {Protocol} context.
34
68
  )
35
69
  end
36
-
37
- Class.new(adapter) do
38
- step(Subprocess(app_protocol), {inherit: true, id: :protocol, replace: :protocol})
39
- end # app_adapter
40
-
41
70
  end
42
71
 
43
72
  def self.options_for_scope_domain_ctx()
@@ -49,10 +78,13 @@ module Trailblazer
49
78
 
50
79
  # Runtime
51
80
  # Invokes the endpoint for you and runs one of the three outcome blocks.
52
- def self.with_or_etc(activity, args, failure_block:, success_block:, protocol_failure_block:)
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
+
53
84
  # args[1] = args[1].merge(focus_on: { variables: [:returned], steps: :invoke_workflow })
54
85
 
55
- signal, (endpoint_ctx, _ ) = Trailblazer::Developer.wtf?(activity, args)
86
+ # signal, (endpoint_ctx, _ ) = Trailblazer::Developer.wtf?(activity, args)
87
+ signal, (endpoint_ctx, _ ) = invoke.call(activity, args) # translates to Trailblazer::Developer.wtf?(activity, args)
56
88
 
57
89
  # this ctx is passed to the controller block.
58
90
  block_ctx = endpoint_ctx[:domain_ctx].merge(endpoint_ctx: endpoint_ctx, signal: signal, errors: endpoint_ctx[:errors]) # DISCUSS: errors? status?
@@ -77,7 +109,8 @@ module Trailblazer
77
109
 
78
110
  #@ For WORKFLOW and operations. not sure this method will stay here.
79
111
  def self.arguments_for(domain_ctx:, flow_options:, circuit_options: {}, **endpoint_options)
80
- domain_ctx = Trailblazer::Context::IndifferentAccess.build(domain_ctx, {}, [domain_ctx, flow_options], circuit_options)
112
+ # we don't have to create the Ctx wrapping explicitly here. this is done via `:input`.
113
+ # domain_ctx = Trailblazer::Context::IndifferentAccess.build(domain_ctx, {}, [domain_ctx, flow_options], circuit_options)
81
114
 
82
115
  [
83
116
  [
@@ -128,3 +161,6 @@ require "trailblazer/endpoint/adapter"
128
161
  require "trailblazer/endpoint/dsl"
129
162
  require "trailblazer/endpoint/controller"
130
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"
@@ -9,20 +9,32 @@ module Trailblazer
9
9
 
10
10
 
11
11
  module Adapter
12
- class Web <Trailblazer::Activity::FastTrack
12
+ class Web < Trailblazer::Activity::Path
13
13
  _404_path = ->(*) { step :_404_status }
14
14
  _401_path = ->(*) { step :_401_status }
15
15
  _403_path = ->(*) { step :_403_status }
16
16
  # _422_path = ->(*) { step :_422_status } # TODO: this is currently represented by the {failure} track.
17
17
 
18
+ # FIXME: is this really the only way to add an {End} to all this?
19
+ @state.update_sequence do |sequence:, **|
20
+ sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :fail_fast), magnetic_to: :fail_fast, id: "End.fail_fast") # TODO: rename to {protocol_failure}
21
+ sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :failure), magnetic_to: :failure, id: "End.failure")
22
+
23
+ recompile_activity!(sequence)
24
+
25
+ sequence
26
+ end
27
+
18
28
  step Subprocess(Protocol), # this will get replaced
19
29
  id: :protocol,
20
30
  Output(:not_authorized) => Path(track_color: :not_authorized, connect_to: Id(:protocol_failure), &_403_path),
21
31
  Output(:not_found) => Path(track_color: :not_found, connect_to: Id(:protocol_failure), &_404_path),
22
32
  Output(:not_authenticated) => Path(track_color: :not_authenticated, connect_to: Id(:protocol_failure), &_401_path),
23
- Output(:invalid_data) => Track(:failure) # application error, since it's usually a failed validation.
33
+ Output(:invalid_data) => Track(:failure), # application error, since it's usually a failed validation.
34
+ Output(:failure) => Track(:failure) # application error, since it's usually a failed validation.
35
+
36
+ step :protocol_failure, magnetic_to: nil, Output(:success) => Track(:fail_fast)#, Output(:failure) => Track(:fail_fast)
24
37
 
25
- step :protocol_failure, magnetic_to: nil, Output(:success) => Track(:fail_fast), Output(:failure) => Track(:fail_fast)
26
38
 
27
39
  def protocol_failure(ctx, **)
28
40
  true
@@ -41,28 +53,34 @@ module Trailblazer
41
53
  def _403_status(ctx, **)
42
54
  ctx[:status] = 403
43
55
  end
44
- end
56
+ end # Web
45
57
 
46
58
  class API < Web
47
59
  step :_200_status, after: :protocol
48
60
 
49
- def _200_status(ctx, **)
50
- ctx[:status] = 200
61
+ def _200_status(ctx, success_status: 200, **)
62
+ ctx[:status] = success_status
51
63
  end
52
64
 
53
- fail :_422_status, before: "End.failure"
65
+ step :_422_status, before: "End.failure", magnetic_to: :failure, Output(:success) => Track(:failure)
54
66
 
55
67
  def _422_status(ctx, **)
56
68
  ctx[:status] = 422
57
69
  end
58
70
 
59
71
 
60
- def self.insert_error_handler_steps(adapter)
72
+ def self.insert_error_handler_steps(adapter) # TODO: evaluate if needed?
61
73
  adapter = Class.new(adapter) do
62
- step :handle_not_authenticated, magnetic_to: :not_authenticated, Output(:success) => Track(:not_authenticated), Output(:failure) => Track(:not_authenticated), before: :_401_status
63
- step :handle_not_authorized, magnetic_to: :not_authorized, Output(:success) => Track(:not_authorized), Output(:failure) => Track(:not_authorized), before: :_403_status
74
+ API.insert_error_handler_steps!(self)
75
+ end
76
+ end
77
+
78
+ def self.insert_error_handler_steps!(adapter)
79
+ adapter.instance_exec do
80
+ step :handle_not_authenticated, magnetic_to: :not_authenticated, Output(:success) => Track(:not_authenticated), before: :_401_status
81
+ step :handle_not_authorized, magnetic_to: :not_authorized, Output(:success) => Track(:not_authorized), before: :_403_status
64
82
  # step :handle_not_found, magnetic_to: :not_found, Output(:success) => Track(:not_found), Output(:failure) => Track(:not_found)
65
- fail :handle_invalid_data
83
+ step :handle_invalid_data, before: :_422_status, magnetic_to: :failure, Output(:success) => Track(:failure)
66
84
  end
67
85
  end
68
86
 
@@ -81,117 +99,8 @@ module Trailblazer
81
99
  end
82
100
  end
83
101
  end
84
- end
85
-
86
- # Basic endpoint adapter for a HTTP document API.
87
- # As always: "work in progress" ;)
88
- #
89
- # {End.fail_fast} currently implies a 4xx-able error.
90
- class API_ < Trailblazer::Activity::FastTrack
91
- _404_path = ->(*) { step :_404_status }
92
- _401_path = ->(*) { step :_401_status; step :_401_error_message }
93
- _403_path = ->(*) { step :_403_status }
94
- # _422_path = ->(*) { step :_422_status } # TODO: this is currently represented by the {failure} track.
95
-
96
- # The API Adapter automatically wires well-defined outputs for you to well-defined paths. :)
97
- # FIXME
98
-
99
- step Subprocess(Protocol), # this will get replaced
100
- id: :protocol,
101
- Output(:not_authorized) => Path(track_color: :_403, connect_to: Id(:render_protocol_failure_config), &_403_path),
102
- Output(:not_found) => Path(track_color: :_404, connect_to: Id(:protocol_failure), &_404_path),
103
- Output(:not_authenticated) => Path(track_color: :_401, connect_to: Id(:render_protocol_failure_config), &_401_path), # head(401), representer: Representer::Error, message: no token
104
- Output(:invalid_data) => Track(:failure) # application error, since it's usually a failed validation.
105
-
106
- # extensions: [Trailblazer::Activity::TaskWrap::Extension(merge: TERMINUS_HANDLER)]
107
- # failure is automatically wired to failure, being an "application error" vs. a "protocol error (auth, etc)"
108
-
109
-
110
- fail :failure_render_config
111
- fail :failure_config_status
112
- fail :render_failure
113
-
114
- step :success_render_config
115
- step :success_render_status
116
- step :render_success
117
-
118
-
119
- # DISCUSS: "protocol failure" and "application failure" should be the same path, probably?
120
- step :render_protocol_failure_config, magnetic_to: nil, Output(:success) => Path(connect_to: Id("End.fail_fast")) do
121
- step :render_protocol_failure
122
- step :protocol_failure
123
- end
124
-
125
- =begin
126
- render_protocol_failure_config # representer
127
- render_protocol_failure # Representer.new
128
- protocol_failure # true
129
- =end
130
-
131
- def success_render_status(ctx, **)
132
- ctx[:status] = 200
133
- end
134
-
135
- def success_render_config(ctx, representer:, **)
136
- true
137
- end
138
-
139
- def render_protocol_failure_config(*args)
140
- failure_render_config(*args)
141
- end
142
-
143
- # ROAR
144
- def render_success(ctx, representer:, domain_ctx:, **)
145
- model = domain_ctx[:model]
146
- ctx[:json] = representer.new(model).to_json # FIXME: use the same as render_failure.
147
- end
148
-
149
- def failure_render_config(ctx, error_representer:, **)
150
- ctx[:representer] = error_representer
151
- end
152
-
153
- def failure_config_status(ctx, **)
154
- ctx[:status] = 422
155
- end
156
-
157
- def protocol_failure(*args)
158
- #failure_config(*args)
159
- true
160
- end
161
- def render_protocol_failure(*args)
162
- render_failure(*args)
163
- end
164
-
165
- # ROAR
166
- def render_failure(ctx, error_representer:, errors:, **)
167
- # render_success(*args)
168
- ctx[:json] = error_representer.new(errors).to_json
169
- end
170
- # how/where would we configure each endpoint? (per action)
171
- # class Endpoint
172
- # representer ...
173
- # message ...
174
-
175
- def _401_status(ctx, **)
176
- ctx[:status] = 401
177
- end
178
-
179
- def _404_status(ctx, **)
180
- ctx[:status] = 404
181
- end
182
-
183
- def _403_status(ctx, **)
184
- ctx[:status] = 403
185
- end
186
-
187
- def _401_error_message(ctx, **)
188
- ctx[:error_message] = "Authentication credentials were not provided or invalid."
189
- end
102
+ end # API
190
103
 
191
- # def exec_success(ctx, success_block:, **)
192
- # success_block.call(ctx, **ctx.to_hash) # DISCUSS: use Nested(dynamic) ?
193
- # end
194
- end
195
104
  end
196
105
  end
197
106
  end