Files
govoplan-connectors/src/govoplan_connectors/backend/schemas.py
T
zemion 2c2b11f860
Module Package Release / publish-packages (push) Successful in 12s
feat(connectors): add managed file and PostgreSQL origins
2026-08-21 19:29:58 +02:00

306 lines
8.8 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, Field, model_validator
class FeedAcquireRequest(BaseModel):
url: str = Field(min_length=1, max_length=2000)
max_entries: int = Field(default=2_000, ge=1, le=10_000)
class FeedImportRequest(FeedAcquireRequest):
name: str = Field(min_length=1, max_length=300)
source_name: str = Field(
min_length=1,
max_length=120,
pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
)
description: str | None = Field(default=None, max_length=4000)
class FeedEntryPayload(BaseModel):
id: str = Field(min_length=1, max_length=2000)
title: str = Field(min_length=1, max_length=1000)
url: str | None = Field(default=None, max_length=2000)
summary: str | None = None
content: str | None = None
author: str | None = Field(default=None, max_length=500)
published_at: datetime | None = None
updated_at: datetime | None = None
categories: list[str] = Field(default_factory=list, max_length=100)
enclosures: list[dict[str, Any]] = Field(default_factory=list, max_length=100)
visibility: Literal["public", "tenant", "private"] = "public"
metadata: dict[str, Any] = Field(default_factory=dict)
class FeedPublicationEntryPayload(FeedEntryPayload):
source_kind: Literal["event", "publication", "case", "report"]
source_module: str = Field(
min_length=1,
max_length=100,
pattern=r"^[a-z][a-z0-9_]*$",
)
source_ref: str = Field(min_length=1, max_length=500)
source_revision: str | None = Field(default=None, max_length=200)
class FeedDocumentResponse(BaseModel):
format: Literal["rss", "atom"]
title: str
source_url: str
description: str | None = None
home_url: str | None = None
language: str | None = None
updated_at: datetime | None = None
acquired_at: datetime | None = None
fresh_until: datetime | None = None
etag: str | None = None
last_modified: str | None = None
content_type: str | None = None
sha256: str
entries: list[FeedEntryPayload]
metadata: dict[str, Any] = Field(default_factory=dict)
class FeedRenderPayload(BaseModel):
format: Literal["rss", "atom"]
title: str = Field(min_length=1, max_length=1000)
feed_url: str = Field(min_length=1, max_length=2000)
home_url: str = Field(min_length=1, max_length=2000)
description: str | None = None
language: str | None = Field(default=None, max_length=100)
entries: list[FeedPublicationEntryPayload] = Field(
default_factory=list,
max_length=10_000,
)
audience: Literal["public", "tenant", "private"] = "public"
class SnapshotCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=300)
source_name: str = Field(
min_length=1,
max_length=120,
pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
)
description: str | None = Field(default=None, max_length=4000)
format: Literal["json", "csv"] = "json"
rows: list[dict[str, Any]] | None = Field(default=None, max_length=10_000)
csv_text: str | None = Field(default=None, max_length=5_000_000)
delimiter: Literal[",", ";", "\t", "|"] = ","
@model_validator(mode="after")
def validate_payload(self) -> "SnapshotCreateRequest":
if self.format == "json" and self.rows is None:
raise ValueError("JSON snapshots require rows.")
if self.format == "json" and self.csv_text is not None:
raise ValueError("JSON snapshots cannot include CSV text.")
if self.format == "csv" and not self.csv_text:
raise ValueError("CSV snapshots require CSV text.")
if self.format == "csv" and self.rows is not None:
raise ValueError("CSV snapshots cannot include JSON rows.")
return self
class ManagedFileSourceCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=300)
source_name: str = Field(
min_length=1,
max_length=120,
pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
)
description: str | None = Field(default=None, max_length=4000)
file_asset_id: str = Field(min_length=1, max_length=36)
file_version_id: str | None = Field(default=None, min_length=1, max_length=36)
delimiter: Literal[",", ";", "\t", "|"] = ","
sheet_name: str | None = Field(default=None, min_length=1, max_length=255)
class SqlSourceCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=300)
source_name: str = Field(
min_length=1,
max_length=120,
pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
)
description: str | None = Field(default=None, max_length=4000)
configuration_id: str = Field(min_length=1, max_length=36)
schema_name: str | None = Field(
default=None,
pattern=r"^[A-Za-z_][A-Za-z0-9_$-]{0,127}$",
)
table_name: str = Field(
min_length=1,
max_length=128,
pattern=r"^[A-Za-z_][A-Za-z0-9_$-]{0,127}$",
)
class TabularColumnResponse(BaseModel):
name: str
data_type: str
nullable: bool
class TabularPushdownResponse(BaseModel):
projections: bool
pagination: bool
filters: list[str]
aggregations: list[str]
sorting: list[str]
class TabularHealthResponse(BaseModel):
status: Literal["healthy", "warning", "error", "unknown"]
code: str
summary: str
checked_at: str | None
details: dict[str, Any]
class TabularPreviewDiagnosticResponse(BaseModel):
severity: Literal["info", "warning", "error"]
code: str
message: str
details: dict[str, Any]
class TabularSourceResponse(BaseModel):
ref: str
provider: str
source_name: str
name: str
description: str | None
columns: list[TabularColumnResponse]
schema_version: str
fingerprint: str
row_count: int | None
byte_count: int | None
updated_at: str | None
capabilities: list[str]
metadata: dict[str, Any]
source_mode: Literal["live", "cached", "file_backed", "static"]
pushdown: TabularPushdownResponse
health: TabularHealthResponse
class TabularSourceListResponse(BaseModel):
sources: list[TabularSourceResponse]
class TabularSourcePreviewResponse(BaseModel):
source: TabularSourceResponse
rows: list[dict[str, Any]]
total_rows: int
truncated: bool
returned_bytes: int
elapsed_ms: int
effective_row_limit: int
effective_byte_limit: int
effective_timeout_ms: int
diagnostics: list[TabularPreviewDiagnosticResponse]
class TabularSourceDeleteResponse(BaseModel):
deleted: bool
source_ref: str
class SanctionsSourceResponse(BaseModel):
provider_id: str
publisher: str
jurisdiction: str
list_type: str
source_id: str
source_url: str | None
parser_version: str
licence_notes: str
trust_notes: str
class SanctionsSourceListResponse(BaseModel):
sources: list[SanctionsSourceResponse]
class SanctionsSnapshotResponse(BaseModel):
ref: str
provider_id: str
publisher: str
jurisdiction: str
list_type: str
source_id: str
source_version: str
publication_at: datetime | None
effective_at: datetime | None
acquired_at: datetime
content_type: str
byte_count: int
sha256: str
parser_version: str
raw_evidence_ref: str
connector_run_id: str
signature_evidence: dict[str, Any]
licence_notes: str | None
trust_notes: str | None
transport_evidence: dict[str, Any]
class SanctionsSnapshotListResponse(BaseModel):
snapshots: list[SanctionsSnapshotResponse]
class SanctionsAcquisitionRunResponse(BaseModel):
id: str
provider_id: str
source_id: str
status: str
attempt_count: int
request_evidence: dict[str, Any]
response_evidence: dict[str, Any]
started_at: datetime
finished_at: datetime | None
snapshot_id: str | None
error: str | None
class SanctionsAcquisitionRunListResponse(BaseModel):
runs: list[SanctionsAcquisitionRunResponse]
class SanctionsRefreshResponse(BaseModel):
run_id: str
provider_id: str
status: str
snapshot: SanctionsSnapshotResponse | None
error: str | None
__all__ = [
"FeedAcquireRequest",
"FeedDocumentResponse",
"FeedEntryPayload",
"FeedImportRequest",
"FeedRenderPayload",
"ManagedFileSourceCreateRequest",
"SnapshotCreateRequest",
"SqlSourceCreateRequest",
"SanctionsAcquisitionRunListResponse",
"SanctionsAcquisitionRunResponse",
"SanctionsRefreshResponse",
"SanctionsSnapshotListResponse",
"SanctionsSnapshotResponse",
"SanctionsSourceListResponse",
"SanctionsSourceResponse",
"TabularColumnResponse",
"TabularHealthResponse",
"TabularPreviewDiagnosticResponse",
"TabularPushdownResponse",
"TabularSourceDeleteResponse",
"TabularSourceListResponse",
"TabularSourcePreviewResponse",
"TabularSourceResponse",
]