Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lib/flow_runner/spec/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,16 @@ defmodule FlowRunner.Spec.Block do

* `{:invalid, reason}` — the flow runner will exit through the
block's default response.

* `{:error, reason}` — the block could not pick an exit at all (for
example a `Core.Case` whose tests all evaluated to false and which
has no default exit); the error is returned to the caller.
"""
@callback evaluate_outgoing(Container.t(), Flow.t(), Block.t(), Context.t(), user_input :: any) ::
{:ok, user_input :: any}
| {:ok, user_input :: any, opts :: Keyword.t()}
| {:invalid, reason :: String.t()}
| {:error, reason :: String.t()}

@derive Jason.Encoder
defstruct uuid: nil,
Expand Down Expand Up @@ -228,7 +233,9 @@ defmodule FlowRunner.Spec.Block do

@decorate with_span("FlowRunner.Spec.Block.evaluate_outgoing")
@spec evaluate_outgoing(Container.t(), Flow.t(), Block.t(), Context.t(), user_input :: any) ::
{:ok, Context.t(), Block.t()} | {:invalid, reason :: String.t()}
{:ok, Context.t(), Block.t() | nil}
| {:invalid, reason :: String.t()}
| {:error, reason :: String.t()}
def evaluate_outgoing(
container,
flow,
Expand Down Expand Up @@ -270,12 +277,16 @@ defmodule FlowRunner.Spec.Block do
{:ok, context} = evaluate_user_input(block, context, user_input)

fetch_default_block(block, flow, context)

{:error, reason} ->
{:error, reason}
end
end

defp normalize_outgoing({:ok, user_input}), do: {:ok, user_input, []}
defp normalize_outgoing({:ok, user_input, opts}) when is_list(opts), do: {:ok, user_input, opts}
defp normalize_outgoing({:invalid, _} = invalid), do: invalid
defp normalize_outgoing({:error, _} = error), do: error

@spec fetch_default_block(Block.t(), Flow.t(), Context.t()) ::
{:error, String.t()} | {:ok, Context.t(), Block.t() | nil}
Expand Down
16 changes: 8 additions & 8 deletions lib/flow_runner/spec/blocks/case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ defmodule FlowRunner.Spec.Blocks.Case do

@impl FlowRunner.Spec.Block
def evaluate_outgoing(_container, _flow, block, context, nil) do
{:ok, block_exit} = Block.evaluate_exits(block, context)

case FlowRunner.evaluate_expression_block(block_exit.name, context) do
# We didn't manage to parse it and it returned a parsing error
{:error, _error, _reason} -> {:ok, block_exit.name}
# We managed to evaluate it and it returned nil
nil -> {:ok, block_exit.name}
value -> {:ok, value}
with {:ok, block_exit} <- Block.evaluate_exits(block, context) do
case FlowRunner.evaluate_expression_block(block_exit.name, context) do
# We didn't manage to parse it and it returned a parsing error
{:error, _error, _reason} -> {:ok, block_exit.name}
# We managed to evaluate it and it returned nil
nil -> {:ok, block_exit.name}
value -> {:ok, value}
end
end
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule FlowRunner.MixProject do
use Mix.Project

@version "6.15.1"
@version "6.15.2"

def project do
[
Expand Down
61 changes: 61 additions & 0 deletions test/block_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule BlockTest do

alias FlowRunner.Context
alias FlowRunner.Spec.Block
alias FlowRunner.Spec.Container
alias FlowRunner.Spec.Exit
alias FlowRunner.Spec.Flow

Expand Down Expand Up @@ -259,6 +260,66 @@ defmodule BlockTest do
assert {:ok, %Exit{uuid: "failure-exit"}} = Block.evaluate_exits(block, context_false)
end

describe "evaluate_outgoing/5 on a Core.Case block" do
test "returns an error instead of raising when no exit matches and there is no default exit" do
routing_block = %Block{
uuid: "5a0a4c1e-1d0e-4a4e-9a8e-3c1d7b2f6e01",
name: "Routing for check",
type: "Core.Case",
exits: [
%Exit{
uuid: "b586afa7-0097-4805-9951-f6d3156c08db",
name: "Exit for CheckYes",
test: "block.value = 5",
destination_block: "8e2f8c2d-4b0b-4a3f-8a6c-1f2e3d4c5b6a"
}
]
}

context = %Context{vars: %{"block" => %{"value" => 10}}}

assert {:error, "No default exit available"} =
Block.evaluate_outgoing(
%Container{},
%Flow{blocks: [routing_block]},
routing_block,
context,
nil
)
end

test "stores the exit name when the name evaluates to nil" do
destination_uuid = "8e2f8c2d-4b0b-4a3f-8a6c-1f2e3d4c5b6a"

routing_block = %Block{
uuid: "5a0a4c1e-1d0e-4a4e-9a8e-3c1d7b2f6e02",
name: "Routing for check",
type: "Core.Case",
exits: [
%Exit{
uuid: "c2f1e9a0-6d3b-4f8e-9c1a-2b3d4e5f6a70",
name: "answer",
test: "true",
destination_block: destination_uuid
}
]
}

destination_block = %Block{uuid: destination_uuid, name: "destination", type: "Core.Log"}
context = %Context{vars: %{"answer" => nil}}

assert {:ok, %Context{vars: %{"Routing for check" => "answer"}},
%Block{uuid: ^destination_uuid}} =
Block.evaluate_outgoing(
%Container{},
%Flow{blocks: [routing_block, destination_block]},
routing_block,
context,
nil
)
end
end

describe "load_config_for_set_contact_property!/1" do
test "accepts the singular shape" do
assert %{
Expand Down
Loading