Skip to main content

lib/cuerdo/arazzo/workflow.ex

defmodule Cuerdo.Arazzo.Workflow do
  @moduledoc """
  An Arazzo [Workflow](https://spec.openapis.org/arazzo/v1.0.1.html#workflow-object) object
  """
  import Cuerdo.Arazzo.Utils

  alias Cuerdo.Arazzo
  alias Cuerdo.Arazzo.{Context, FailureAction, Output, Parameter, Step, SuccessAction}

  alias Cuerdo.Errors.InvalidInputs

  use Cuerdo.Object,
    schema: %{
      workflowId: programming_friendly_id(),
      summary: Zoi.string() |> Zoi.optional(),
      description: Zoi.string() |> Zoi.optional(),
      # `inputs` is optional in the spec, but we make it required here
      # because it's the only part we use for parametrizaton
      inputs:
        Zoi.any(
          description: """
          JSON Schema 2020-12 object representing the JSON schema inputs given to the workflow
          """
        ),
      dependsOn: Zoi.array(programming_friendly_id()) |> Zoi.default([]),
      steps: Zoi.array(Step.schema(), min_length: 1),
      failureActions: Zoi.array(FailureAction.schema() |> or_reusable()) |> Zoi.default([]),
      successActions: Zoi.array(SuccessAction.schema() |> or_reusable()) |> Zoi.default([]),
      outputs: Output.schema() |> Zoi.default(%{}),
      parameters:
        Zoi.array(Parameter.schema() |> or_reusable(), unique_items: true) |> Zoi.default([])
    }

  @type t :: %__MODULE__{}

  @doc """
  Validates the workflow inputs match the defined inputs schema.
  """
  @spec validate_inputs(any(), t(), Context.t()) :: :ok | {:error, Exception.t()}
  def validate_inputs(
        workflow_inputs,
        %__MODULE__{} = workflow,
        %Context{} = context
      ) do
    with {:ok, inputs_schema} <- Arazzo.build_schema(workflow.inputs, context),
         {:ok, root} <- JSV.build(inputs_schema, resolver: Cuerdo.Resolver),
         {:ok, _} <- JSV.validate(workflow_inputs, root) do
      :ok
    else
      {:error, %JSV.ValidationError{} = exc} ->
        {:error, %InvalidInputs{inputs: workflow_inputs, message: Exception.message(exc)}}

      {:error, exc} = error when is_exception(exc) ->
        error
    end
  end
end