trailblazer-endpoint 0.0.1
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 +7 -0
 - data/CHANGES.md +3 -0
 - data/Gemfile +16 -0
 - data/README.md +11 -0
 - data/Rakefile +10 -0
 - data/lib/trailblazer-endpoint.rb +1 -0
 - data/lib/trailblazer/endpoint.rb +133 -0
 - data/lib/trailblazer/endpoint/adapter.rb +197 -0
 - data/lib/trailblazer/endpoint/builder.rb +56 -0
 - data/lib/trailblazer/endpoint/protocol.rb +122 -0
 - data/lib/trailblazer/endpoint/rails.rb +27 -0
 - data/lib/trailblazer/endpoint/version.rb +5 -0
 - data/test/adapter/api_test.rb +78 -0
 - data/test/adapter/representable_test.rb +7 -0
 - data/test/adapter/web_test.rb +40 -0
 - data/test/benchmark/skill_resolver_benchmark.rb +43 -0
 - data/test/docs/controller_test.rb +92 -0
 - data/test/docs/endpoint_test.rb +54 -0
 - data/test/endpoint_test.rb +908 -0
 - data/test/rails-app/.gitignore +21 -0
 - data/test/rails-app/Gemfile +17 -0
 - data/test/rails-app/Gemfile.lock +157 -0
 - data/test/rails-app/README.md +24 -0
 - data/test/rails-app/Rakefile +6 -0
 - data/test/rails-app/app/controllers/application_controller.rb +3 -0
 - data/test/rails-app/app/controllers/songs_controller.rb +26 -0
 - data/test/rails-app/app/models/application_record.rb +3 -0
 - data/test/rails-app/app/models/concerns/.keep +0 -0
 - data/test/rails-app/config.ru +5 -0
 - data/test/rails-app/config/application.rb +15 -0
 - data/test/rails-app/config/boot.rb +3 -0
 - data/test/rails-app/config/database.yml +25 -0
 - data/test/rails-app/config/environment.rb +5 -0
 - data/test/rails-app/config/environments/development.rb +54 -0
 - data/test/rails-app/config/environments/production.rb +86 -0
 - data/test/rails-app/config/environments/test.rb +42 -0
 - data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -0
 - data/test/rails-app/config/initializers/backtrace_silencers.rb +7 -0
 - data/test/rails-app/config/initializers/cookies_serializer.rb +5 -0
 - data/test/rails-app/config/initializers/filter_parameter_logging.rb +4 -0
 - data/test/rails-app/config/initializers/inflections.rb +16 -0
 - data/test/rails-app/config/initializers/mime_types.rb +4 -0
 - data/test/rails-app/config/initializers/new_framework_defaults.rb +24 -0
 - data/test/rails-app/config/initializers/session_store.rb +3 -0
 - data/test/rails-app/config/initializers/wrap_parameters.rb +14 -0
 - data/test/rails-app/config/locales/en.yml +23 -0
 - data/test/rails-app/config/routes.rb +6 -0
 - data/test/rails-app/config/secrets.yml +22 -0
 - data/test/rails-app/db/seeds.rb +7 -0
 - data/test/rails-app/log/.keep +0 -0
 - data/test/rails-app/test/controllers/.keep +0 -0
 - data/test/rails-app/test/controllers/songs_controller_test.rb +156 -0
 - data/test/rails-app/test/fixtures/.keep +0 -0
 - data/test/rails-app/test/fixtures/files/.keep +0 -0
 - data/test/rails-app/test/helpers/.keep +0 -0
 - data/test/rails-app/test/integration/.keep +0 -0
 - data/test/rails-app/test/mailers/.keep +0 -0
 - data/test/rails-app/test/models/.keep +0 -0
 - data/test/rails-app/test/test_helper.rb +10 -0
 - data/test/rails-app/tmp/.keep +0 -0
 - data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
 - data/test/rails-app/vendor/assets/stylesheets/.keep +0 -0
 - data/test/test_helper.rb +34 -0
 - data/trailblazer-endpoint.gemspec +26 -0
 - metadata +236 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: a13cc4ea9b50a656f620578a75ccd11aa01106bc22be8975e8758a660d1a13aa
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 4c006bbfeab10458615ee658e862cf152b42b17f1c0d8302f9d1308b8cdc1425
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 53dfeec3df63a69bbe4f481f58eaa6c71e32d55044066fda4eb7dc8579ef1b973079ff8fa6b7cbd7a6f4ce4ecdb767d25f37a6bf68eee99626557f1a1de2ab71
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 99b9e753a78f182ad274c677aa9f1c33bc85fbd8ad4ce57c2574a4b4ef29d3400e3553c6484674e3d75cec41dcacea76f601db77aee7273fdd066ec3c41c17c7
         
     | 
    
        data/CHANGES.md
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    | 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            source 'https://rubygems.org'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Specify your gem's dependencies in trailblazer.gemspec
         
     | 
| 
      
 4 
     | 
    
         
            +
            gemspec
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            gem "multi_json"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            gem "minitest-line"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # gem "trailblazer-activity", path: "../trailblazer-activity"
         
     | 
| 
      
 11 
     | 
    
         
            +
            # gem "trailblazer-activity-dsl-linear", path: "../trailblazer-activity-dsl-linear"
         
     | 
| 
      
 12 
     | 
    
         
            +
            # gem "trailblazer-operation", path: "../operation"
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            gem "dry-validation"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            # gem "trailblazer-developer", path: "../trailblazer-developer"
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Trailblazer::Endpoint
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            *Generic HTTP handlers for operation results.*
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Decouple finding out *what happened* from *what to do*.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            t test/controllers/songs_controller_test.rb --backtrace
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## TODO
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * make travis build run `cd test/rails-app/ && rake`
         
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,10 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "bundler/gem_tasks"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "rake/testtask"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            task :default => [:test]
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Rake::TestTask.new(:test) do |test|
         
     | 
| 
      
 7 
     | 
    
         
            +
              test.libs << 'test'
         
     | 
| 
      
 8 
     | 
    
         
            +
              test.test_files = FileList['test/endpoint_test.rb', 'test/docs/*_test.rb', "test/adapter/*_test.rb"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              test.verbose = true
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "trailblazer/endpoint"
         
     | 
| 
         @@ -0,0 +1,133 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Trailblazer
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Endpoint
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Create an {Endpoint} class with the provided adapter and protocol.
         
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  # special considerations around the {domain_activity} and its taskWrap:
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #  1. domain_ctx_filter (e.g. to filter {current_user})
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #  2. :input (scope {:domain_ctx})
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #  3. call (domain_activity)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #  4. :output
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #  5. save return signal
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  extensions_options = {
         
     | 
| 
      
 17 
     | 
    
         
            +
                    extensions: [Trailblazer::Activity::TaskWrap::Extension(merge: Trailblazer::Endpoint::Protocol::Domain.extension_for_terminus_handler)],
         
     | 
| 
      
 18 
     | 
    
         
            +
                  }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  # scoping: {:domain_ctx} becomes ctx
         
     | 
| 
      
 21 
     | 
    
         
            +
                  extensions_options.merge!(Endpoint.options_for_scope_domain_ctx) if scope_domain_ctx # TODO: test flag
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 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
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  app_protocol = Class.new(protocol) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                    step(Subprocess(domain_activity), {inherit: true, id: :domain_activity, replace: :domain_activity,
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            # FIXME: where does this go?
         
     | 
| 
      
 31 
     | 
    
         
            +
                    }.
         
     | 
| 
      
 32 
     | 
    
         
            +
                      merge(extensions_options).
         
     | 
| 
      
 33 
     | 
    
         
            +
                      merge(instance_exec(&block)) # the block is evaluated in the {Protocol} context.
         
     | 
| 
      
 34 
     | 
    
         
            +
                    )
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  Class.new(adapter) do
         
     | 
| 
      
 38 
     | 
    
         
            +
                    step(Subprocess(app_protocol), {inherit: true, id: :protocol, replace: :protocol})
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end # app_adapter
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def self.options_for_scope_domain_ctx()
         
     | 
| 
      
 44 
     | 
    
         
            +
                  {
         
     | 
| 
      
 45 
     | 
    
         
            +
                    input:  ->(ctx, **) { ctx[:domain_ctx] }, # gets automatically Context()'ed.
         
     | 
| 
      
 46 
     | 
    
         
            +
                    output: ->(domain_ctx, **) { {:domain_ctx => domain_ctx} }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  }
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # Runtime
         
     | 
| 
      
 51 
     | 
    
         
            +
                # 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:)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # args[1] = args[1].merge(focus_on: { variables: [:returned], steps: :invoke_workflow })
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  signal, (endpoint_ctx, _ ) = Trailblazer::Developer.wtf?(activity, args)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  # this ctx is passed to the controller block.
         
     | 
| 
      
 58 
     | 
    
         
            +
                  block_ctx = endpoint_ctx[:domain_ctx].merge(endpoint_ctx: endpoint_ctx, signal: signal, errors: endpoint_ctx[:errors]) # DISCUSS: errors? status?
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # if signal < Trailblazer::Activity::End::Success
         
     | 
| 
      
 61 
     | 
    
         
            +
                  adapter_terminus_semantic = signal.to_h[:semantic]
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  executed_block =
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if adapter_terminus_semantic    == :success
         
     | 
| 
      
 65 
     | 
    
         
            +
                      success_block
         
     | 
| 
      
 66 
     | 
    
         
            +
                    elsif adapter_terminus_semantic == :fail_fast
         
     | 
| 
      
 67 
     | 
    
         
            +
                      protocol_failure_block
         
     | 
| 
      
 68 
     | 
    
         
            +
                    else
         
     | 
| 
      
 69 
     | 
    
         
            +
                      failure_block
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  executed_block.(block_ctx, **block_ctx)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  # we return the original context???
         
     | 
| 
      
 75 
     | 
    
         
            +
                  return signal, [endpoint_ctx]
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                # def self.default_success_if(success_id)
         
     | 
| 
      
 79 
     | 
    
         
            +
                #   ->(signal:, graph:, **) { signal[:lane_positions][suspend_activity].last == graph.find(success_id).task }
         
     | 
| 
      
 80 
     | 
    
         
            +
                # end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                #@ For WORKFLOW and operations. not sure this method will stay here.
         
     | 
| 
      
 83 
     | 
    
         
            +
                def self.arguments_for(domain_ctx:, collaboration:, dictionary: collaboration.to_h[:dictionary], flow_options:, circuit_options: {}, **options)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  domain_ctx      = Trailblazer::Context::IndifferentAccess.build(domain_ctx, {}, [domain_ctx, flow_options], circuit_options)
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  [
         
     | 
| 
      
 87 
     | 
    
         
            +
                    [
         
     | 
| 
      
 88 
     | 
    
         
            +
                      {
         
     | 
| 
      
 89 
     | 
    
         
            +
                          activity:                       collaboration,
         
     | 
| 
      
 90 
     | 
    
         
            +
                          domain_ctx:                     domain_ctx, # DISCUSS: is this where {:resume_data} comes in?
         
     | 
| 
      
 91 
     | 
    
         
            +
                          # process_model_class:            process_model_class,
         
     | 
| 
      
 92 
     | 
    
         
            +
                          # process_model_from_resume_data: process_model_from_resume_data,
         
     | 
| 
      
 93 
     | 
    
         
            +
                          # find_process_model:             find_process_model,
         
     | 
| 
      
 94 
     | 
    
         
            +
                          # encrypted_resume_data:          encrypted_resume_data,
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                          dictionary:                     dictionary,
         
     | 
| 
      
 97 
     | 
    
         
            +
                          # cipher_key:                     cipher_key,
         
     | 
| 
      
 98 
     | 
    
         
            +
                          **options,
         
     | 
| 
      
 99 
     | 
    
         
            +
                      },
         
     | 
| 
      
 100 
     | 
    
         
            +
                      flow_options
         
     | 
| 
      
 101 
     | 
    
         
            +
                    ],
         
     | 
| 
      
 102 
     | 
    
         
            +
                    circuit_options
         
     | 
| 
      
 103 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                # FIXME: name will change! this is for controllers, only!
         
     | 
| 
      
 107 
     | 
    
         
            +
                def self.advance_from_controller(endpoint, success_block:, failure_block:, protocol_failure_block: protocol_failure_block, **argument_options)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  args = Trailblazer::Endpoint.arguments_for(argument_options)
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  signal, (ctx, _ ) = Trailblazer::Endpoint.with_or_etc(
         
     | 
| 
      
 111 
     | 
    
         
            +
                    endpoint,
         
     | 
| 
      
 112 
     | 
    
         
            +
                    args[0], # [ctx, flow_options]
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                    success_block:          success_block,
         
     | 
| 
      
 115 
     | 
    
         
            +
                    failure_block:          failure_block,
         
     | 
| 
      
 116 
     | 
    
         
            +
                    protocol_failure_block: protocol_failure_block,
         
     | 
| 
      
 117 
     | 
    
         
            +
                  )
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  ctx
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
              end
         
     | 
| 
      
 122 
     | 
    
         
            +
            end
         
     | 
| 
      
 123 
     | 
    
         
            +
            #       created: Dry::Matcher::Case.new(
         
     | 
| 
      
 124 
     | 
    
         
            +
            #         match:   ->(result) { result.success? && result["model.action"] == :new }, # the "model.action" doesn't mean you need Model.
         
     | 
| 
      
 125 
     | 
    
         
            +
            #         resolve: ->(result) { result }),
         
     | 
| 
      
 126 
     | 
    
         
            +
            #       not_found: Dry::Matcher::Case.new(
         
     | 
| 
      
 127 
     | 
    
         
            +
            #         match:   ->(result) { result.failure? && result["result.model"] && result["result.model"].failure? },
         
     | 
| 
      
 128 
     | 
    
         
            +
            #         resolve: ->(result) { result }),
         
     | 
| 
      
 129 
     | 
    
         
            +
            #       # TODO: we could add unauthorized here.
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            require "trailblazer/endpoint/protocol"
         
     | 
| 
      
 133 
     | 
    
         
            +
            require "trailblazer/endpoint/adapter"
         
     | 
| 
         @@ -0,0 +1,197 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Trailblazer
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Endpoint
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # The idea is to use the CreatePrototypeProtocol's outputs as some kind of protocol, outcomes that need special handling
         
     | 
| 
      
 5 
     | 
    
         
            +
                # can be wired here, or merged into one (e.g. 401 and failure is failure).
         
     | 
| 
      
 6 
     | 
    
         
            +
                # I am writing this class in the deep forests of the Algarve, hiding from the GNR.
         
     | 
| 
      
 7 
     | 
    
         
            +
                # class Adapter < Trailblazer::Activity::FastTrack # TODO: naming. it's after the "application logic", more like Controller
         
     | 
| 
      
 8 
     | 
    
         
            +
             # Currently reusing End.fail_fast as a "something went wrong, but it wasn't a real application error!"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                module Adapter
         
     | 
| 
      
 12 
     | 
    
         
            +
                  class Web <Trailblazer::Activity::FastTrack
         
     | 
| 
      
 13 
     | 
    
         
            +
                    _404_path = ->(*) { step :_404_status }
         
     | 
| 
      
 14 
     | 
    
         
            +
                    _401_path = ->(*) { step :_401_status }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    _403_path = ->(*) { step :_403_status }
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # _422_path = ->(*) { step :_422_status } # TODO: this is currently represented by the {failure} track.
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    step Subprocess(Protocol), # this will get replaced
         
     | 
| 
      
 19 
     | 
    
         
            +
                      id: :protocol,
         
     | 
| 
      
 20 
     | 
    
         
            +
                      Output(:not_authorized)     => Path(track_color: :not_authorized, connect_to: Id(:protocol_failure), &_403_path),
         
     | 
| 
      
 21 
     | 
    
         
            +
                      Output(:not_found)          => Path(track_color: :not_found, connect_to: Id(:protocol_failure), &_404_path),
         
     | 
| 
      
 22 
     | 
    
         
            +
                      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.
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    step :protocol_failure, magnetic_to: nil, Output(:success) => Track(:fail_fast), Output(:failure) => Track(:fail_fast)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    def protocol_failure(ctx, **)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      true
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            # FIXME:::::::
         
     | 
| 
      
 33 
     | 
    
         
            +
                    def _401_status(ctx, **)
         
     | 
| 
      
 34 
     | 
    
         
            +
                      ctx[:status] = 401
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    def _404_status(ctx, **)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      ctx[:status] = 404
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    def _403_status(ctx, **)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      ctx[:status] = 403
         
     | 
| 
      
 43 
     | 
    
         
            +
                    end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  class API < Web
         
     | 
| 
      
 47 
     | 
    
         
            +
                    step :_200_status, after: :protocol
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    def _200_status(ctx, **)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      ctx[:status] = 200
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    fail :_422_status, before: "End.failure"
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    def _422_status(ctx, **)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      ctx[:status] = 422
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    def self.insert_error_handler_steps(adapter)
         
     | 
| 
      
 61 
     | 
    
         
            +
                      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
         
     | 
| 
      
 64 
     | 
    
         
            +
                        # step :handle_not_found, magnetic_to: :not_found, Output(:success) => Track(:not_found), Output(:failure) => Track(:not_found)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        fail :handle_invalid_data
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    class Errors < Struct.new(:message, :errors) # FIXME: extract
         
     | 
| 
      
 70 
     | 
    
         
            +
                      module Handlers
         
     | 
| 
      
 71 
     | 
    
         
            +
                        def handle_not_authenticated(ctx, errors:, **)
         
     | 
| 
      
 72 
     | 
    
         
            +
                          errors.message = "Authentication credentials were not provided or are invalid."
         
     | 
| 
      
 73 
     | 
    
         
            +
                        end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                        def handle_not_authorized(ctx, errors:, **)
         
     | 
| 
      
 76 
     | 
    
         
            +
                          errors.message = "Action not allowed due to a policy setting."
         
     | 
| 
      
 77 
     | 
    
         
            +
                        end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                        def handle_invalid_data(ctx, errors:, **)
         
     | 
| 
      
 80 
     | 
    
         
            +
                          errors.message = "The submitted data is invalid."
         
     | 
| 
      
 81 
     | 
    
         
            +
                        end
         
     | 
| 
      
 82 
     | 
    
         
            +
                      end
         
     | 
| 
      
 83 
     | 
    
         
            +
                    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
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                    # def exec_success(ctx, success_block:, **)
         
     | 
| 
      
 192 
     | 
    
         
            +
                    #   success_block.call(ctx, **ctx.to_hash) # DISCUSS: use Nested(dynamic) ?
         
     | 
| 
      
 193 
     | 
    
         
            +
                    # end
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Trailblazer
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Endpoint
         
     | 
| 
      
 3 
     | 
    
         
            +
                # you don't need this if you build your endpoints manually
         
     | 
| 
      
 4 
     | 
    
         
            +
                class Builder < Trailblazer::Activity::Railway
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # step :build_policy
         
     | 
| 
      
 6 
     | 
    
         
            +
                  step :build_protocol_block
         
     | 
| 
      
 7 
     | 
    
         
            +
                  step :normalize_tuple
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  # def build_policy(ctx, policies:, **)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def build_protocol_block(ctx, policy:, **)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    ctx[:protocol_block] = -> { step Subprocess(policy), id: :policy, replace: :policy, inherit: true; {} }
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def normalize_tuple(ctx, protocol_block:, options_for_build: {}, **)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    ctx[:build_options] = {
         
     | 
| 
      
 18 
     | 
    
         
            +
                      protocol_block:    protocol_block,
         
     | 
| 
      
 19 
     | 
    
         
            +
                      options_for_build: options_for_build
         
     | 
| 
      
 20 
     | 
    
         
            +
                    }
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  module DSL
         
     | 
| 
      
 25 
     | 
    
         
            +
                    module_function
         
     | 
| 
      
 26 
     | 
    
         
            +
                    #
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # @return endpoint_options
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    def build_options_for(builder:, **options)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      signal, (ctx, _) = builder.([options])
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                      ctx[:build_options] # ["web:submitted?", {protocol_block: ..., options_for_build: ...}]
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    def endpoint_for(id:, builder:, default_options:, **config)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      options = build_options_for(builder: builder, **config)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      return id, Trailblazer::Endpoint.build(default_options.merge(options[:options_for_build]), &options[:protocol_block])
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    # {dsl_options} being something like
         
     | 
| 
      
 42 
     | 
    
         
            +
                    #
         
     | 
| 
      
 43 
     | 
    
         
            +
                    #   "api:Start.default" => {policies: []},
         
     | 
| 
      
 44 
     | 
    
         
            +
                    #   "api:status?"       => {policies: [:user_owns_diagram]},
         
     | 
| 
      
 45 
     | 
    
         
            +
                    #   "api:download?"     => {policies: [:user_owns_diagram]},
         
     | 
| 
      
 46 
     | 
    
         
            +
                    #   "api:delete?"       => {policies: [:user_owns_diagram]},
         
     | 
| 
      
 47 
     | 
    
         
            +
                    def endpoints_for(dsl_options, **options)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      endpoints = dsl_options.collect do |id, config|
         
     | 
| 
      
 49 
     | 
    
         
            +
                        endpoint_for(id: id, **options, **config) # config is per endpoint, options are "global"
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end.to_h
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                end # Builder
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,122 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Trailblazer
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Endpoint
         
     | 
| 
      
 3 
     | 
    
         
            +
                # The {Protocol} implements auth*, and calls the domain OP/WF.
         
     | 
| 
      
 4 
     | 
    
         
            +
                # You still have to implement handlers (like {#authorize} and {#handle_not_authorized}) yourself. This might change soon.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Protocol must provide all ends for the Adapter (401,403 and 404 in particular), even if the ran op/workflow doesn't have it.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   Still thinking about how to do that best.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                # Termini and their "pendants" in HTTP, which is unrelated to protocol!! Protocol is application-focused and doesn't know about HTTP.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #   failure: 411
         
     | 
| 
      
 11 
     | 
    
         
            +
                #   success: 200
         
     | 
| 
      
 12 
     | 
    
         
            +
                #   not_found: 404
         
     | 
| 
      
 13 
     | 
    
         
            +
                #   not_authenticated: 401
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   not_authorized: 403
         
     | 
| 
      
 15 
     | 
    
         
            +
                class Protocol < Trailblazer::Activity::Railway
         
     | 
| 
      
 16 
     | 
    
         
            +
                  class Noop < Trailblazer::Activity::Railway
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  class Failure < Trailblazer::Activity::End # DISCUSS: move to Act::Railway?
         
     | 
| 
      
 20 
     | 
    
         
            +
                    # class Authentication < Failure
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def self._Path(semantic:, &block) # DISCUSS: the problem with Path currently is https://github.com/trailblazer/trailblazer-activity-dsl-linear/issues/27
         
     | 
| 
      
 25 
     | 
    
         
            +
                    Path(track_color: semantic, end_id: "End.#{semantic}", end_task: Failure.new(semantic: semantic), &block)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  step :authenticate, Output(:failure) => _Path(semantic: :not_authenticated) do
         
     | 
| 
      
 29 
     | 
    
         
            +
                      # step :handle_not_authenticated
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  step :policy, Output(:failure) => _Path(semantic: :not_authorized) do # user from cookie, etc
         
     | 
| 
      
 33 
     | 
    
         
            +
                    # step :handle_not_authorized
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # Here, we test a domain OP with ADDITIONAL explicit ends that get wired to the Adapter (vaidation_error => failure).
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # We still need to test the other way round: wiring a "normal" failure to, say, not_found, by inspecting the ctx.
         
     | 
| 
      
 38 
     | 
    
         
            +
                  step Subprocess(Noop), id: :domain_activity
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # add the {End.not_found} terminus to this Protocol. I'm not sure that's the final style, but since a {Protocol} needs to provide all
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # termini for the Adapter this is the only way to get it working right now.
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # FIXME: is this really the only way to add an {End} to all this?
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @state.update_sequence do |sequence:, **|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    sequence = Activity::Path::DSL.append_end(sequence, task: Failure.new(semantic: :not_found), magnetic_to: :not_found, id: "End.not_found")
         
     | 
| 
      
 47 
     | 
    
         
            +
                    sequence = Activity::Path::DSL.append_end(sequence, task: Failure.new(semantic: :invalid_data), magnetic_to: :invalid_data, id: "End.invalid_data")
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    recompile_activity!(sequence)
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    sequence
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # Best-practices of useful routes and handlers that work with 2.1-OPs.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  class Standard < Protocol
         
     | 
| 
      
 56 
     | 
    
         
            +
                    step :handle_not_authenticated, magnetic_to: :not_authenticated, Output(:success) => Track(:not_authenticated), Output(:failure) => Track(:not_authenticated)#, before: "End.not_authenticated"
         
     | 
| 
      
 57 
     | 
    
         
            +
                    step :handle_not_authorized,    magnetic_to: :not_authorized, Output(:success) => Track(:not_authorized), Output(:failure) => Track(:not_authorized)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # step :handle_invalid_data,      magnetic_to: :invalid_data, Output(:success) => Track(:invalid_data), Output(:failure) => Track(:invalid_data)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    # TODO: allow translation.
         
     | 
| 
      
 62 
     | 
    
         
            +
                    module Handler
         
     | 
| 
      
 63 
     | 
    
         
            +
                      def handle_not_authorized(ctx, errors:, **)
         
     | 
| 
      
 64 
     | 
    
         
            +
                        errors.message = "Action not allowed due to a policy setting."
         
     | 
| 
      
 65 
     | 
    
         
            +
                      end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                      def handle_not_authenticated(ctx, errors:, **)
         
     | 
| 
      
 68 
     | 
    
         
            +
                        errors.message = "Authentication credentials were not provided or are invalid."
         
     | 
| 
      
 69 
     | 
    
         
            +
                      end
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    class Termini # FIXME: this means with invalid_data, not_found termini? 2.1?
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  module Bridge
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # this "bridge" should be optional for "legacy operations" that don't have explicit ends.
         
     | 
| 
      
 79 
     | 
    
         
            +
                    # we have to inspect the ctx to find out what "really" happened (e.g. model empty ==> 404)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      NotFound      = Class.new(Trailblazer::Activity::Signal)
         
     | 
| 
      
 81 
     | 
    
         
            +
                      NotAuthorized = Class.new(Trailblazer::Activity::Signal)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      NotAuthenticated = Class.new(Trailblazer::Activity::Signal)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    def self.insert(protocol, **)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      Class.new(protocol) do
         
     | 
| 
      
 86 
     | 
    
         
            +
                        fail :success?, after: :domain_activity,
         
     | 
| 
      
 87 
     | 
    
         
            +
                        # FIXME: how to add more signals/outcomes?
         
     | 
| 
      
 88 
     | 
    
         
            +
                        Output(NotFound, :not_found)            => Track(:not_found),
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                        # FIXME: Track(:not_authorized) is defined before this step, so the Forward search doesn't find it.
         
     | 
| 
      
 91 
     | 
    
         
            +
                        # solution would be to walk down sequence and find the first {:magnetic_to} "not_authorized"
         
     | 
| 
      
 92 
     | 
    
         
            +
                        Output(NotAuthorized, :not_authorized)  => Track(:not_authorized) # FIXME: how to "insert into path"? => Track(:not_authorized) doesn't play!
         
     | 
| 
      
 93 
     | 
    
         
            +
                      end
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  module Domain
         
     | 
| 
      
 99 
     | 
    
         
            +
                    # taskWrap step that saves the return signal of the {domain_activity}.
         
     | 
| 
      
 100 
     | 
    
         
            +
                    # The taskWrap step is usually inserted after {task_wrap.output}.
         
     | 
| 
      
 101 
     | 
    
         
            +
                    def self.terminus_handler(wrap_ctx, original_args)
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    #      Unrecognized Signal `"bla"` returned from EndpointTest::LegacyCreate. Registered signals are,
         
     | 
| 
      
 104 
     | 
    
         
            +
                    # - #<Trailblazer::Activity::End semantic=:failure>
         
     | 
| 
      
 105 
     | 
    
         
            +
                    # - #<Trailblazer::Activity::End semantic=:success>
         
     | 
| 
      
 106 
     | 
    
         
            +
                    # - #<Trailblazer::Activity::End semantic=:fromail_fast>
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                    # {:return_args} is the original "endpoint ctx" that was returned from the {:output} filter.
         
     | 
| 
      
 109 
     | 
    
         
            +
                      wrap_ctx[:return_args][0][:domain_activity_return_signal] = wrap_ctx[:return_signal]
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                      return wrap_ctx, original_args
         
     | 
| 
      
 112 
     | 
    
         
            +
                    end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                    def self.extension_for_terminus_handler
         
     | 
| 
      
 115 
     | 
    
         
            +
                      # this is called after {:output}.
         
     | 
| 
      
 116 
     | 
    
         
            +
                      [[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_after), "task_wrap.call_task", ["endpoint.end_signal", method(:terminus_handler)]]]
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
              end
         
     | 
| 
      
 122 
     | 
    
         
            +
            end
         
     |