Files
govoplan-campaign/src/govoplan_campaign/backend/approval_schemas.py
T

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"]