init commit
This commit is contained in:
9
lib/workload_service/aggregates/quote_task.ex
Normal file
9
lib/workload_service/aggregates/quote_task.ex
Normal file
@@ -0,0 +1,9 @@
|
||||
defmodule WorkloadService.Aggregates.QuoteTask do
|
||||
use WorkloadService.Aggregates.Task,
|
||||
task_type: "quote",
|
||||
commands: WorkloadService.Commands.QuoteTask
|
||||
|
||||
def validate_submission(_) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
9
lib/workload_service/aggregates/solicitation_task.ex
Normal file
9
lib/workload_service/aggregates/solicitation_task.ex
Normal file
@@ -0,0 +1,9 @@
|
||||
defmodule WorkloadService.Aggregates.SolicitationTask do
|
||||
use WorkloadService.Aggregates.Task,
|
||||
task_type: "solicitation",
|
||||
commands: WorkloadService.Commands.SolicitationTask
|
||||
|
||||
def validate_submission(_) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
145
lib/workload_service/aggregates/task.ex
Normal file
145
lib/workload_service/aggregates/task.ex
Normal file
@@ -0,0 +1,145 @@
|
||||
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
|
||||
"""
|
||||
|
||||
@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)
|
||||
|
||||
quote do
|
||||
@behaviour Commanded.Aggregates.Aggregate
|
||||
@task_type unquote(task_type)
|
||||
|
||||
alias unquote(commands_module).CreateTask
|
||||
alias unquote(commands_module).SubmitResponse
|
||||
alias unquote(commands_module).ApproveSubmission
|
||||
alias unquote(commands_module).CompleteTask
|
||||
|
||||
alias Commanded.Aggregates.Aggregate
|
||||
|
||||
defstruct [
|
||||
:id,
|
||||
:application_id,
|
||||
:provider_id,
|
||||
:provider_name,
|
||||
:task_info,
|
||||
:submission,
|
||||
:attachments,
|
||||
:status,
|
||||
:version
|
||||
]
|
||||
|
||||
@impl Aggregate
|
||||
def execute(%__MODULE__{status: nil}, %CreateTask{} = cmd) do
|
||||
%WorkloadService.Events.TaskCreated{
|
||||
id: cmd.id,
|
||||
application_id: cmd.application_id,
|
||||
provider_id: cmd.provider_id,
|
||||
provider_name: cmd.provider_name,
|
||||
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
|
||||
new_status = if status == "approved", do: "draft", else: "draft"
|
||||
|
||||
%WorkloadService.Events.SubmissionUpdated{
|
||||
id: cmd.id,
|
||||
submission: cmd.submission,
|
||||
attachments: cmd.attachments
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Aggregate
|
||||
def execute(%__MODULE__{status: "draft"}, %ApproveSubmission{}) do
|
||||
%WorkloadService.Events.SubmissionApproved{
|
||||
id: nil
|
||||
}
|
||||
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"}, %CompleteTask{} = cmd) do
|
||||
%WorkloadService.Events.TaskCompleted{
|
||||
id: cmd.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,
|
||||
provider_id: e.provider_id,
|
||||
provider_name: e.provider_name,
|
||||
task_info: e.task_info,
|
||||
attachments: e.attachments,
|
||||
status: "created",
|
||||
version: agg.version + 1
|
||||
}
|
||||
end
|
||||
|
||||
@impl Aggregate
|
||||
def apply(%__MODULE__{} = agg, %WorkloadService.Events.SubmissionUpdated{} = e) do
|
||||
%{
|
||||
agg
|
||||
| submission: e.submission,
|
||||
attachments: e.attachments || [],
|
||||
status: "draft",
|
||||
version: agg.version + 1
|
||||
}
|
||||
end
|
||||
|
||||
@impl Aggregate
|
||||
def apply(%__MODULE__{} = agg, %WorkloadService.Events.SubmissionApproved{}) do
|
||||
%{
|
||||
agg
|
||||
| status: "approved",
|
||||
version: agg.version + 1
|
||||
}
|
||||
end
|
||||
|
||||
@impl Aggregate
|
||||
def apply(%__MODULE__{} = agg, %WorkloadService.Events.TaskCompleted{}) do
|
||||
%{
|
||||
agg
|
||||
| status: "completed",
|
||||
version: agg.version + 1
|
||||
}
|
||||
end
|
||||
|
||||
defoverridable execute: 2, apply: 2
|
||||
end
|
||||
end
|
||||
end
|
||||
48
lib/workload_service/aggregates/task_id.ex
Normal file
48
lib/workload_service/aggregates/task_id.ex
Normal file
@@ -0,0 +1,48 @@
|
||||
defmodule WorkloadService.Aggregates.TaskId do
|
||||
@moduledoc """
|
||||
Task identifier with org_id, type and task_id.
|
||||
ID format: "org_id:type:task_id" (e.g., "test:quote:uuid")
|
||||
"""
|
||||
|
||||
@derive Jason.Encoder
|
||||
defstruct [:org_id, :type, :task_id]
|
||||
|
||||
def new(org_id, type, task_id) when type in ["quote", "solicitation"] do
|
||||
%__MODULE__{
|
||||
org_id: org_id,
|
||||
type: type,
|
||||
task_id: task_id
|
||||
}
|
||||
end
|
||||
|
||||
def parse(string) when is_binary(string) do
|
||||
case String.split(string, ":", parts: 3) do
|
||||
[org_id, type, task_id] when type in ["quote", "solicitation"] ->
|
||||
{:ok, new(org_id, type, task_id)}
|
||||
|
||||
_ ->
|
||||
{:error, :invalid_task_id}
|
||||
end
|
||||
end
|
||||
|
||||
def parse!(string) do
|
||||
case parse(string) do
|
||||
{:ok, id} -> id
|
||||
{:error, reason} -> raise ArgumentError, "invalid task id #{inspect(string)}: #{reason}"
|
||||
end
|
||||
end
|
||||
|
||||
defimpl String.Chars do
|
||||
def to_string(%WorkloadService.Aggregates.TaskId{
|
||||
org_id: org_id,
|
||||
type: type,
|
||||
task_id: task_id
|
||||
}) do
|
||||
"#{org_id}:#{type}:#{task_id}"
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Commanded.Serialization.JsonDecoder do
|
||||
def decode(id), do: id
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user