145 lines
4.6 KiB
Python
145 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
|
from govoplan_core.api.v1.schemas import DeltaDeletedItem
|
|
|
|
|
|
class AuditAdminItem(BaseModel):
|
|
id: str
|
|
scope: Literal["tenant", "system"] = "tenant"
|
|
tenant_id: str | None = None
|
|
actor_email: str | None = None
|
|
action: str
|
|
object_type: str | None = None
|
|
object_id: str | None = None
|
|
details: dict[str, Any] = Field(default_factory=dict)
|
|
created_at: datetime
|
|
|
|
|
|
class AuditAdminListResponse(BaseModel):
|
|
items: list[AuditAdminItem]
|
|
total: int
|
|
page: int = 1
|
|
page_size: int = 100
|
|
pages: int = 1
|
|
cursor: str | None = None
|
|
next_cursor: str | None = None
|
|
|
|
|
|
class AuditAdminDeltaResponse(AuditAdminListResponse):
|
|
deleted: list[DeltaDeletedItem] = Field(default_factory=list)
|
|
watermark: str | None = None
|
|
has_more: bool = False
|
|
full: bool = False
|
|
|
|
|
|
class AuditLogItemResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
user_id: str | None = None
|
|
api_key_id: str | None = None
|
|
action: str
|
|
object_type: str | None = None
|
|
object_id: str | None = None
|
|
details: dict[str, Any] | None = None
|
|
created_at: datetime
|
|
|
|
|
|
class AuditLogListResponse(BaseModel):
|
|
items: list[AuditLogItemResponse]
|
|
|
|
|
|
class EventDeliveryMetricsResponse(BaseModel):
|
|
events: dict[str, int] = Field(default_factory=dict)
|
|
deliveries: dict[str, int] = Field(default_factory=dict)
|
|
consumers: dict[str, dict[str, int]] = Field(default_factory=dict)
|
|
oldest_due_at: datetime | None = None
|
|
|
|
|
|
class EventDeliveryReplayRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
reason: str = Field(min_length=1, max_length=2000)
|
|
|
|
|
|
class EventDeliveryReplayResponse(BaseModel):
|
|
event_id: str
|
|
consumer_id: str
|
|
delivery_key: str
|
|
status: str
|
|
attempts: int
|
|
replay_count: int
|
|
last_replayed_at: datetime | None = None
|
|
last_replayed_by: str | None = None
|
|
last_replay_reason: str | None = None
|
|
last_error: str | None = None
|
|
|
|
|
|
class EvidenceBundleReferenceRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
reference_id: str = Field(min_length=1, max_length=200)
|
|
kind: str = Field(min_length=1, max_length=80)
|
|
owner_module: str = Field(min_length=1, max_length=100)
|
|
locator: str = Field(min_length=1, max_length=2048)
|
|
content_sha256: str | None = Field(default=None, pattern=r"^[0-9A-Fa-f]{64}$")
|
|
required: bool = True
|
|
|
|
|
|
class EvidenceBundleExportRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
scope: Literal["tenant", "system", "all"] = "tenant"
|
|
tenant_id: str | None = Field(default=None, max_length=36)
|
|
since: datetime | None = None
|
|
until: datetime | None = None
|
|
record_ids: list[str] = Field(default_factory=list, max_length=500)
|
|
action: str | None = Field(default=None, max_length=100)
|
|
object_type: str | None = Field(default=None, max_length=100)
|
|
object_id: str | None = Field(default=None, max_length=100)
|
|
max_records: int = Field(default=500, ge=1, le=500)
|
|
references: list[EvidenceBundleReferenceRequest] = Field(
|
|
default_factory=list,
|
|
max_length=200,
|
|
)
|
|
sign: bool = False
|
|
|
|
@model_validator(mode="after")
|
|
def validate_window_and_selection(self):
|
|
for label, value in (("since", self.since), ("until", self.until)):
|
|
if value is not None and (value.tzinfo is None or value.utcoffset() is None):
|
|
raise ValueError(f"Evidence bundle {label} must include a timezone.")
|
|
if self.since and self.until and self.until < self.since:
|
|
raise ValueError("Evidence bundle until must not precede since.")
|
|
if len(self.record_ids) != len(set(self.record_ids)):
|
|
raise ValueError("Evidence bundle record ids must be unique.")
|
|
reference_ids = [item.reference_id for item in self.references]
|
|
if len(reference_ids) != len(set(reference_ids)):
|
|
raise ValueError("Evidence bundle reference ids must be unique.")
|
|
return self
|
|
|
|
|
|
class EvidenceBundleResponse(BaseModel):
|
|
id: str
|
|
scope: Literal["tenant", "system", "all"]
|
|
tenant_id: str | None = None
|
|
status: Literal["pending", "ready", "failed"]
|
|
bundle_sha256: str | None = None
|
|
record_count: int
|
|
reference_count: int
|
|
generated_at: datetime | None = None
|
|
downloaded_at: datetime | None = None
|
|
error_code: str | None = None
|
|
created_at: datetime
|
|
download_url: str | None = None
|
|
|
|
|
|
class EvidenceBundleDownloadResponse(BaseModel):
|
|
bundle: dict[str, Any]
|