Add scoped postbox template previews

This commit is contained in:
2026-08-06 12:42:20 +02:00
parent c53646a8aa
commit d107c09f74
15 changed files with 1300 additions and 56 deletions
+67
View File
@@ -378,6 +378,8 @@ class PostboxTemplateRevisionPayload(BaseModel):
function_type_id: str | None = Field(default=None, max_length=36)
scope_kind: Literal["tenant", "unit", "subtree", "unit_type"] = "tenant"
scope_id: str | None = Field(default=None, max_length=255)
scope_structure_id: str | None = Field(default=None, max_length=36)
scope_relation_type_ids: list[str] = Field(default_factory=list, max_length=20)
name_pattern: str = Field(
default="{unit_name} / {function_name}",
min_length=1,
@@ -398,6 +400,15 @@ class PostboxTemplateRevisionPayload(BaseModel):
default_factory=PostboxRoutingPolicyPayload
)
@model_validator(mode="after")
def normalize_scope(self) -> "PostboxTemplateRevisionPayload":
self.scope_relation_type_ids = list(
dict.fromkeys(
value.strip() for value in self.scope_relation_type_ids if value.strip()
)
)
return self
@model_validator(mode="after")
def validate_encryption(self) -> "PostboxTemplateRevisionPayload":
if self.encryption_profile == "server_envelope_v1":
@@ -412,15 +423,69 @@ class PostboxTemplateRevisionPayload(BaseModel):
return self
def _validate_template_write_scope(
payload: PostboxTemplateRevisionPayload,
) -> None:
if payload.scope_kind == "subtree" and not payload.scope_structure_id:
raise ValueError("A subtree scope requires an organization structure.")
if payload.scope_kind != "subtree" and (
payload.scope_structure_id or payload.scope_relation_type_ids
):
raise ValueError(
"Hierarchy structure and relation filters apply only to subtree scopes."
)
class PostboxTemplateCreateRequest(PostboxTemplateRevisionPayload):
slug: str = Field(min_length=1, max_length=120)
name: str = Field(min_length=1, max_length=250)
description: str | None = None
@model_validator(mode="after")
def validate_write_scope(self) -> "PostboxTemplateCreateRequest":
_validate_template_write_scope(self)
return self
class PostboxTemplatePreviewRequest(PostboxTemplateCreateRequest):
template_id: str | None = Field(default=None, max_length=36)
context_key: str | None = Field(default=None, max_length=255)
limit: int = Field(default=200, ge=1, le=500)
class PostboxTemplatePreviewTarget(BaseModel):
organization_unit_id: str
organization_unit_name: str
function_id: str
function_name: str
address: str
name: str
holder_count: int = Field(ge=0)
vacant: bool
status: str
existing_postbox_id: str | None = None
diagnostics: list[str] = Field(default_factory=list)
class PostboxTemplatePreviewResponse(BaseModel):
targets: list[PostboxTemplatePreviewTarget] = Field(default_factory=list)
total: int = Field(ge=0)
ready_count: int = Field(ge=0)
existing_count: int = Field(ge=0)
vacant_count: int = Field(ge=0)
blocked_count: int = Field(ge=0)
truncated: bool = False
diagnostics: list[str] = Field(default_factory=list)
class PostboxTemplateReviseRequest(PostboxTemplateRevisionPayload):
base_revision: int = Field(ge=1)
@model_validator(mode="after")
def validate_write_scope(self) -> "PostboxTemplateReviseRequest":
_validate_template_write_scope(self)
return self
class PostboxTemplateRevisionItem(PostboxTemplateRevisionPayload):
id: str
@@ -523,6 +588,8 @@ class PostboxGroupingUpdateRequest(PostboxGroupingPayload):
class PostboxGroupingItem(PostboxGroupingPayload):
id: str
total_count: int = Field(default=0, ge=0)
unread_count: int = Field(default=0, ge=0)
resource_revision: int = Field(ge=1)
etag: str
created_at: datetime