Files
workload-service/lib/workload_service/aggregates/task.ex
HaimKortovich c81b1673d4
Some checks failed
Build and Publish / build-release (push) Has been cancelled
add auth
2026-05-15 10:19:57 -05:00

177 lines
5.0 KiB
Elixir

defmodule WorkloadService.Aggregates.Task do
@moduledoc """
Behaviour and __using__ macro for task aggregates.
Each task type can override validation and add specific behavior.
Usage:
defmodule WorkloadService.Aggregates.QuoteTask do
use WorkloadService.Aggregates.Task,
task_type: "quote"
end
## With custom submission type
defmodule QuoteTaskWithCustomSubmission do
defmodule CustomSubmission do
@type t :: %{...custom_fields: term()}
end
use WorkloadService.Aggregates.Task,
task_type: "quote",
submission_type: CustomSubmission.t()
end
"""
@callback validate_submission(map()) :: :ok | {:error, term()}
defmacro __using__(opts) do
task_type = Keyword.fetch!(opts, :task_type)
commands_module = Keyword.get(opts, :commands, WorkloadService.Commands.Task)
submission_type = Keyword.get(opts, :submission_type, quote(do: map()))
quote do
@type t :: %__MODULE__{
id: WorkloadService.Aggregates.TaskId.t() | nil,
application_id: WorkloadService.Aggregates.ApplicationId.t() | nil,
task_info: map() | nil,
submission: unquote(submission_type) | nil,
attachments: [String.t()],
status: String.t() | nil
}
@behaviour Commanded.Aggregates.Aggregate
@task_type unquote(task_type)
alias unquote(commands_module).CreateTask
alias unquote(commands_module).SubmitResponse
alias unquote(commands_module).RequestApproval
alias unquote(commands_module).ApproveSubmission
alias unquote(commands_module).CompleteTask
alias Commanded.Aggregates.Aggregate
@derive Jason.Encoder
defstruct [
:id,
:application_id,
:task_info,
:submission,
:attachments,
:status
]
@impl Aggregate
def execute(%__MODULE__{status: nil}, %CreateTask{} = cmd) do
%WorkloadService.Events.TaskCreated{
id: cmd.id,
application_id: cmd.application_id,
task_info: cmd.task_info,
attachments: cmd.attachments || []
}
end
@impl Aggregate
def execute(%__MODULE__{}, %CreateTask{}) do
{:error, :task_already_exists}
end
@impl Aggregate
def execute(%__MODULE__{status: status}, %SubmitResponse{} = cmd)
when status in [nil, "created", "draft", "approved"] do
with :ok <- validate_submission(cmd.submission) do
%WorkloadService.Events.SubmissionUpdated{
id: cmd.id,
submission: cmd.submission,
attachments: cmd.attachments
}
end
end
@impl Aggregate
def execute(%__MODULE__{status: "draft"}, %RequestApproval{} = cmd) do
%WorkloadService.Events.ApprovalRequested{
id: cmd.id
}
end
@impl Aggregate
def execute(%__MODULE__{status: status}, %RequestApproval{}) do
{:error, {:invalid_state, "cannot request approval in state: #{status}"}}
end
@impl Aggregate
def execute(%__MODULE__{id: id, status: "draft"}, %ApproveSubmission{}) do
%WorkloadService.Events.SubmissionApproved{
id: id
}
end
@impl Aggregate
def execute(%__MODULE__{status: status}, %ApproveSubmission{}) do
{:error, {:invalid_state, "cannot approve in state: #{status}"}}
end
@impl Aggregate
def execute(%__MODULE__{status: "approved", id: id}, %CompleteTask{} = cmd) do
%WorkloadService.Events.TaskCompleted{
id: id,
completed_by: cmd.completed_by
}
end
@impl Aggregate
def execute(%__MODULE__{status: status}, %CompleteTask{}) do
{:error, {:invalid_state, "cannot complete in state: #{status}"}}
end
@impl Aggregate
def apply(%__MODULE__{} = agg, %WorkloadService.Events.TaskCreated{} = e) do
%{
agg
| id: e.id,
application_id: e.application_id,
task_info: e.task_info,
attachments: e.attachments,
status: "created"
}
end
@impl Aggregate
def apply(%__MODULE__{} = agg, %WorkloadService.Events.SubmissionUpdated{} = e) do
%{
agg
| submission: e.submission,
attachments: e.attachments || [],
status: "draft"
}
end
@impl Aggregate
def apply(%__MODULE__{} = agg, %WorkloadService.Events.SubmissionApproved{}) do
%{
agg
| status: "approved"
}
end
@impl Aggregate
def apply(%__MODULE__{} = agg, %WorkloadService.Events.ApprovalRequested{}) do
%{
agg
| status: "approval_requested"
}
end
@impl Aggregate
def apply(%__MODULE__{} = agg, %WorkloadService.Events.TaskCompleted{}) do
%{
agg
| status: "completed"
}
end
defoverridable execute: 2, apply: 2
end
end
end