defmodule Runbox.Runtime.Simple.ComponentNetwork do @moduledoc group: :internal @moduledoc """ Component network builds and validates template dependency network for Simple scenario runtime. The component network for Simple scenarios is quite simple. * first you have input topics, usually followed by tick timezip for each of them * following is a timezip zipping all input topics into a single stream * the stream is processed by the template which is the heart of the scenario, it produces a stream of output actions * finally there's an output sink executing the output actions """ @behaviour Runbox.Runtime.ComponentNetwork @type timezip :: {:timezip, to_zip :: [component()]} @type template :: {:template, module()} @type tick_timezip :: {:tick_timezip, topic :: String.t()} @type input :: {:input_topic | :load_topic, topic :: String.t()} @type output_sink() :: :output_sink @type component :: template() | input() | tick_timezip() | output_sink() | timezip() @type t :: [component()] @impl true def convert_to_network([template]) do template end @impl true def create([template], _opts) do network = List.flatten([ inputs(template.info.topics), [{:template, template.module}, :output_sink] ]) {:ok, network} end def create([], _) do {:error, :no_template_found} end def create(_, _) do {:error, :multiple_templates_not_supported} end @impl true def input_topics(network) do network |> Enum.filter(fn {:input_topic, _} -> true {:load_topic, _} -> true _ -> false end) |> Enum.map(fn {_, topic} -> topic end) end # generate topics, optionally with tick timezips and a single timezip at the end, if needed defp inputs([topic]), do: input_with_tick_timezip(topic) defp inputs(topics) do topic_components = Enum.map(topics, &input_with_tick_timezip/1) timezip = [{:timezip, Enum.map(topic_components, &List.last/1)}] List.flatten(topic_components) ++ timezip end defp input_with_tick_timezip({_, logical_topic} = topic), do: [topic, {:tick_timezip, logical_topic}] end