From add1bb4d9bca9653f03940d616972942b7974163 Mon Sep 17 00:00:00 2001 From: Nick Sutterer Date: Fri, 3 May 2024 16:38:48 +0200 Subject: [PATCH] we can generate the Schema from the PRO JSON. wow, we barely have to write a line of code now. --- lib/trailblazer/workflow.rb | 1 + lib/trailblazer/workflow/discovery.rb | 8 +-- lib/trailblazer/workflow/generate/schema.rb | 45 +++++++++++++++++ lib/trailblazer/workflow/task/discover.rb | 27 ++++++++-- lib/trailblazer/workflow/test/assertions.rb | 2 +- test/discover_task_test.rb | 55 +++++++++++++++++++++ test/discovery_test.rb | 3 +- 7 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 lib/trailblazer/workflow/generate/schema.rb diff --git a/lib/trailblazer/workflow.rb b/lib/trailblazer/workflow.rb index 8fb3ee6..c39685e 100644 --- a/lib/trailblazer/workflow.rb +++ b/lib/trailblazer/workflow.rb @@ -25,6 +25,7 @@ module Workflow require "trailblazer/workflow/introspect/state_table" require "trailblazer/workflow/introspect/event_table" +require "trailblazer/workflow/generate/schema" require "trailblazer/workflow/generate/state_table" require "trailblazer/workflow/generate/state_guards" diff --git a/lib/trailblazer/workflow/discovery.rb b/lib/trailblazer/workflow/discovery.rb index 4f3f6d7..5ead01c 100644 --- a/lib/trailblazer/workflow/discovery.rb +++ b/lib/trailblazer/workflow/discovery.rb @@ -37,7 +37,9 @@ def call(json_filename:) # TODO: add outputs - _schema = Trailblazer::Workflow::Collaboration(json_file: json_filename, lanes: stubbed_lanes) # FIXME: we're re-parsing JSON. + schema = Trailblazer::Workflow::Collaboration(json_file: json_filename, lanes: stubbed_lanes) # FIXME: we're re-parsing JSON. + + return schema, ctx end end @@ -58,7 +60,7 @@ def call(json_filename:, start_lane:, dsl_options_for_run_multiple_times: {}) # imply we start from a public resume and discover the path? # we could save work on {run_multiple_times} with this. - collaboration = Stub.(json_filename: json_filename) + collaboration, parsed_structure = Stub.(json_filename: json_filename) initial_lane_positions = Collaboration::Synchronous.initial_lane_positions(collaboration.to_h[:lanes]) initial_lane_positions = Collaboration::Positions.new(initial_lane_positions.collect { |activity, task| Trailblazer::Workflow::Collaboration::Position.new(activity, task) }) # FIXME: initial_lane_positions should return {Collaboration::Positions} @@ -185,7 +187,7 @@ def call(json_filename:, start_lane:, dsl_options_for_run_multiple_times: {}) end end - return discovered_states, collaboration + return discovered_states, collaboration, parsed_structure end module DSL diff --git a/lib/trailblazer/workflow/generate/schema.rb b/lib/trailblazer/workflow/generate/schema.rb new file mode 100644 index 0000000..0a37ba3 --- /dev/null +++ b/lib/trailblazer/workflow/generate/schema.rb @@ -0,0 +1,45 @@ +module Trailblazer + module Workflow + class Generate + class Schema < Trailblazer::Activity::Railway + step :render + + def render(ctx, parsed_structure:, namespace:, json_filename:, **options) + lanes_cfg = parsed_structure[:lane_hints] + + lanes = parsed_structure[:intermediates].collect do |json_id, intermediate| + tasks = intermediate.wiring.find_all { |task_ref, _| task_ref.data[:type] == :task } + + implementation = tasks.collect do |task_ref, _| + %( "#{task_ref.data[:label]}" => Trailblazer::Activity::Railway.Subprocess(#{namespace}::#{task_ref.data[:label]}),) + end.join("\n") + + icon, label, _ = json_id.split(".") # FIXME: abstract. + + # lane_task_2_wiring = intermediate.wiring.find_all { |task_ref, _| task_ref.data[:type] == :task } + %( "#{json_id}" => { + label: "#{label}", + icon: "#{icon}", + implementation: { +#{implementation} + } + },) + end.join("\n") + + snippet = %(module #{namespace} + Schema = Trailblazer::Workflow.Collaboration( + json_file: "#{json_filename}", + lanes: { +#{lanes} + }, # :lanes + state_guards: #{namespace}::StateGuards::Decider, + ) +end +) + + ctx[:snippet] = snippet + end + end + end + end +end diff --git a/lib/trailblazer/workflow/task/discover.rb b/lib/trailblazer/workflow/task/discover.rb index 275b25b..4c5bba2 100644 --- a/lib/trailblazer/workflow/task/discover.rb +++ b/lib/trailblazer/workflow/task/discover.rb @@ -6,15 +6,17 @@ module Discover module_function # DISCUSS: what about running this before we have a schema? - def call(namespace:, target_dir:, test_filename:, **discovery_options) + def call(namespace:, target_dir:, test_filename:, json_filename:, **discovery_options) filepath = Filepath.new(target_dir) + schema_filename = filepath.("schema.rb") state_guard_filename = filepath.("state_guards.rb") state_table_filename = filepath.("generated/state_table.rb") iteration_set_filename = filepath.("generated/iteration_set.json") - states, schema = Trailblazer::Workflow::Discovery.( + states, schema, parsed_structure = Trailblazer::Workflow::Discovery.( + json_filename: json_filename, **discovery_options # run_multiple_times: run_multiple_times, ) @@ -42,6 +44,15 @@ def call(namespace:, target_dir:, test_filename:, **discovery_options) state_table_filename: state_table_filename, namespace: namespace, ) + + Produce::Schema.( + parsed_structure: parsed_structure, + namespace: namespace, + filename: schema_filename, + json_filename: json_filename, + ) + + return states end @@ -118,7 +129,17 @@ def call(iteration_set, filename:, state_table_filename:, lanes_cfg:, namespace: File.write(filename, ruby_output) end end - end + + class Schema + def self.call(parsed_structure:, namespace:, filename:, json_filename:, **) + _, (ctx, _) = Trailblazer::Workflow::Generate::Schema.invoke([{parsed_structure: parsed_structure, namespace: namespace, json_filename: json_filename}, {}]) + ruby_output = ctx[:snippet] + + File.write(filename, ruby_output) + end + end + + end # Produce class Filepath def initialize(base_path) diff --git a/lib/trailblazer/workflow/test/assertions.rb b/lib/trailblazer/workflow/test/assertions.rb index 657dd32..2b3ae08 100644 --- a/lib/trailblazer/workflow/test/assertions.rb +++ b/lib/trailblazer/workflow/test/assertions.rb @@ -48,7 +48,7 @@ def assert_advance(event_label, test_plan:, schema:, ctx: {seq: []}, flow_option # we're expecting an invalid transition. if invalid # assert_equal signal.to_h[:semantic], :failure # TODO: reuse endpoint/matcher - assert [:failure, :not_authorized].include?(signal.to_h[:semantic]) # TODO: reuse endpoint/matcher + assert [:failure, :not_authorized, :invalid_event].include?(signal.to_h[:semantic]) # TODO: reuse endpoint/matcher # FIXME: either invalid_event or not_authorized? depends on the level of endpoint. return ctx # FIXME: test this! end diff --git a/test/discover_task_test.rb b/test/discover_task_test.rb index 1a9c093..7c770d2 100644 --- a/test/discover_task_test.rb +++ b/test/discover_task_test.rb @@ -85,6 +85,61 @@ module Posting::Collaboration::Generated "⏸︎ Update form♦Notify approver [110]" => {suspend_tuples: [["lifecycle", "suspend-Gateway_1wzosup"], ["UI", "suspend-Gateway_1g3fhu2"], ["editor", "suspend-gw-to-catch-before-Activity_05zip3u"]], catch_tuples: [["UI", "catch-before-Activity_1165bw9"], ["UI", "catch-before-Activity_1dt5di5"]]}, } end +) + + assert_equal File.read("#{TEST_ROOT}/app/concepts/posting/collaboration/schema.rb"), %(module Posting::Collaboration + Schema = Trailblazer::Workflow.Collaboration( + json_file: "../fixtures/v1/posting-v11.json", + lanes: { + "⛾.lifecycle.posting" => { + label: "lifecycle", + icon: "⛾", + implementation: { + "Update" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Update), + "Notify approver" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Notify approver), + "Approve" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Approve), + "Publish" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Publish), + "Archive" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Archive), + "Delete" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Delete), + "Revise" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Revise), + "Reject" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Reject), + "Create" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Create), + } + }, + "☝.UI.blogger" => { + label: "UI", + icon: "☝", + implementation: { + "Update form" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Update form), + "Notify approver" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Notify approver), + "Publish" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Publish), + "Delete" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Delete), + "Delete? form" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Delete? form), + "Cancel" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Cancel), + "Revise" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Revise), + "Revise form" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Revise form), + "Update" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Update), + "Update form with errors" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Update form with errors), + "Revise form with errors" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Revise form with errors), + "Archive" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Archive), + "Create" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Create), + "Create form with errors" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Create form with errors), + "Create form" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Create form), + } + }, + "☑.editor.reviewer" => { + label: "editor", + icon: "☑", + implementation: { + "Approve" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Approve), + "Reject" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Reject), + "Notify" => Trailblazer::Activity::Railway.Subprocess(Posting::Collaboration::Notify), + } + }, + }, # :lanes + state_guards: Posting::Collaboration::StateGuards::Decider, + ) +end ) end diff --git a/test/discovery_test.rb b/test/discovery_test.rb index 61f5710..e77ff18 100644 --- a/test/discovery_test.rb +++ b/test/discovery_test.rb @@ -34,7 +34,7 @@ def stub_task(lane_label, task_label) end it "Discovery.call" do - states, schema = Trailblazer::Workflow::Discovery.( + states, schema, parsed_structure = Trailblazer::Workflow::Discovery.( json_filename: "test/fixtures/v1/posting-v11.json", start_lane: "UI", @@ -54,6 +54,7 @@ def stub_task(lane_label, task_label) }, ) + assert_equal parsed_structure[:intermediates].keys, ["⛾.lifecycle.posting", "☝.UI.blogger", "☑.editor.reviewer"] # pp states assert_equal states.size, 22