79 lines
3.1 KiB
Python
79 lines
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
|
from govoplan_core.core.approvals import ApprovalActorSelector, ApprovalStepDefinition
|
|
|
|
|
|
class CampaignApprovalSelectorInput(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
kind: Literal["account", "group", "role", "function_assignment", "any_account"]
|
|
value: str = Field(min_length=1, max_length=255)
|
|
label: str | None = Field(default=None, max_length=255)
|
|
|
|
|
|
class CampaignApprovalStepInput(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
key: str = Field(min_length=1, max_length=120)
|
|
label: str = Field(min_length=1, max_length=255)
|
|
selectors: list[CampaignApprovalSelectorInput] = Field(min_length=1, max_length=500)
|
|
required_approvals: int = Field(default=1, ge=1, le=500)
|
|
rejection_policy: Literal["fail_fast", "collect"] = "fail_fast"
|
|
due_at: datetime | None = None
|
|
signature_required: bool = False
|
|
forbidden_evidence_roles: list[
|
|
Literal["author", "owner", "validator", "builder", "reviewer"]
|
|
] = Field(default_factory=list, max_length=5)
|
|
|
|
def to_definition(self) -> ApprovalStepDefinition:
|
|
return ApprovalStepDefinition(
|
|
key=self.key,
|
|
label=self.label,
|
|
selectors=tuple(
|
|
ApprovalActorSelector(item.kind, item.value, item.label)
|
|
for item in self.selectors
|
|
),
|
|
required_approvals=self.required_approvals,
|
|
rejection_policy=self.rejection_policy,
|
|
due_at=self.due_at,
|
|
signature_required=self.signature_required,
|
|
forbidden_evidence_roles=tuple(self.forbidden_evidence_roles),
|
|
)
|
|
|
|
|
|
class CampaignApprovalRequestInput(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
title: str = Field(
|
|
default="Approve Campaign delivery", min_length=1, max_length=255
|
|
)
|
|
description: str | None = Field(default=None, max_length=10_000)
|
|
steps: list[CampaignApprovalStepInput] = Field(default_factory=list, max_length=100)
|
|
template_id: str | None = Field(default=None, max_length=36)
|
|
template_revision: int | None = Field(default=None, ge=1)
|
|
unique_actors_across_steps: bool = True
|
|
expires_at: datetime | None = None
|
|
policy_refs: list[str] = Field(default_factory=list, max_length=500)
|
|
idempotency_key: str = Field(min_length=1, max_length=160)
|
|
|
|
@model_validator(mode="after")
|
|
def validate_source(self) -> "CampaignApprovalRequestInput":
|
|
template = self.template_id is not None or self.template_revision is not None
|
|
if template and (self.template_id is None or self.template_revision is None):
|
|
raise ValueError("Template id and revision must be supplied together.")
|
|
if template and self.steps:
|
|
raise ValueError("Use either an Approval template or inline steps.")
|
|
if not template and not self.steps:
|
|
raise ValueError(
|
|
"At least one Approval step is required without a template."
|
|
)
|
|
return self
|
|
|
|
|
|
__all__ = ["CampaignApprovalRequestInput"]
|