222 lines
7.2 KiB
Python
222 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
|
|
|
class ConnectorMappingRule(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
source: str = Field(min_length=1, max_length=255)
|
|
target: str = Field(min_length=1, max_length=255)
|
|
required: bool = False
|
|
default: Any = None
|
|
|
|
|
|
class ConnectorMappingDefinition(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
version: str = Field(min_length=1, max_length=100)
|
|
rules: list[ConnectorMappingRule] = Field(default_factory=list, max_length=500)
|
|
|
|
|
|
class ConnectorValidationRule(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
kind: Literal["required", "one_of", "unique"]
|
|
field: str = Field(min_length=1, max_length=255)
|
|
values: list[Any] = Field(default_factory=list, max_length=500)
|
|
severity: Literal["warning", "error"] = "error"
|
|
code: str = Field(min_length=1, max_length=120)
|
|
message: str = Field(min_length=1, max_length=500)
|
|
|
|
@model_validator(mode="after")
|
|
def validate_values(self) -> "ConnectorValidationRule":
|
|
if self.kind == "one_of" and not self.values:
|
|
raise ValueError("one_of validation requires allowed values")
|
|
if self.kind != "one_of" and self.values:
|
|
raise ValueError("Only one_of validation accepts values")
|
|
return self
|
|
|
|
|
|
class ConnectorDryRunMetadata(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
supported: bool = True
|
|
simulation_supported: bool = True
|
|
sample_rows: list[dict[str, Any]] = Field(default_factory=list, max_length=500)
|
|
max_items: int = Field(default=500, ge=1, le=10_000)
|
|
redacted_fields: list[str] = Field(default_factory=list, max_length=100)
|
|
|
|
|
|
class ConnectorAuditMetadata(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
event_prefix: str = Field(min_length=1, max_length=120)
|
|
expected_events: list[str] = Field(default_factory=list, max_length=100)
|
|
evidence_fields: list[str] = Field(default_factory=list, max_length=100)
|
|
|
|
|
|
class GovernedConnectorSpecification(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
provider: str = Field(min_length=1, max_length=120)
|
|
protocol: str = Field(min_length=1, max_length=80)
|
|
capabilities: list[str] = Field(min_length=1, max_length=100)
|
|
input_schema: dict[str, Any]
|
|
output_schema: dict[str, Any]
|
|
mapping: ConnectorMappingDefinition
|
|
validation_rules: list[ConnectorValidationRule] = Field(
|
|
default_factory=list,
|
|
max_length=500,
|
|
)
|
|
dry_run: ConnectorDryRunMetadata
|
|
audit: ConnectorAuditMetadata
|
|
privacy_classification: Literal[
|
|
"public",
|
|
"internal",
|
|
"confidential",
|
|
"restricted",
|
|
] = "internal"
|
|
retention_class: str = Field(min_length=1, max_length=120)
|
|
operational_limits: dict[str, Any] = Field(default_factory=dict)
|
|
retry_policy: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class ConnectorDefinitionUpsertRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
definition_key: str = Field(pattern=r"^[a-z0-9][a-z0-9._-]{0,159}$")
|
|
name: str = Field(min_length=1, max_length=300)
|
|
description: str | None = Field(default=None, max_length=4000)
|
|
origin: Literal["package", "local"] = "local"
|
|
package_ref: str | None = Field(default=None, max_length=300)
|
|
specification: GovernedConnectorSpecification
|
|
|
|
@model_validator(mode="after")
|
|
def package_provenance(self) -> "ConnectorDefinitionUpsertRequest":
|
|
if self.origin == "package" and not self.package_ref:
|
|
raise ValueError("Package definitions require package_ref")
|
|
if self.origin == "local" and self.package_ref:
|
|
raise ValueError("Local definitions cannot claim package_ref")
|
|
return self
|
|
|
|
|
|
class ConnectorDefinitionItem(BaseModel):
|
|
id: str
|
|
tenant_id: str
|
|
definition_key: str
|
|
name: str
|
|
description: str | None = None
|
|
status: str
|
|
current_revision: int
|
|
source_package: str | None = None
|
|
local_definition: bool
|
|
revision_id: str
|
|
definition_hash: str
|
|
origin: str
|
|
package_ref: str | None = None
|
|
specification: GovernedConnectorSpecification
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ConnectorDefinitionListResponse(BaseModel):
|
|
items: list[ConnectorDefinitionItem]
|
|
|
|
|
|
class ConnectorConfigurationCreateRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
definition_id: str = Field(min_length=1, max_length=36)
|
|
name: str = Field(min_length=1, max_length=300)
|
|
endpoint_url: str | None = Field(default=None, max_length=1500)
|
|
credential_ref: str | None = Field(default=None, max_length=500)
|
|
local_overrides: dict[str, Any] = Field(default_factory=dict)
|
|
ambiguity_policy: Literal["manual_review", "quarantine", "reject"] = (
|
|
"manual_review"
|
|
)
|
|
status: Literal["draft", "active", "disabled"] = "draft"
|
|
|
|
|
|
class ConnectorConfigurationUpdateRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
expected_revision: int = Field(ge=1)
|
|
name: str | None = Field(default=None, min_length=1, max_length=300)
|
|
endpoint_url: str | None = Field(default=None, max_length=1500)
|
|
credential_ref: str | None = Field(default=None, max_length=500)
|
|
local_overrides: dict[str, Any] | None = None
|
|
ambiguity_policy: Literal["manual_review", "quarantine", "reject"] | None = None
|
|
status: Literal["draft", "active", "disabled"] | None = None
|
|
adopt_latest_definition: bool = False
|
|
|
|
|
|
class ConnectorConfigurationItem(BaseModel):
|
|
id: str
|
|
tenant_id: str
|
|
definition_id: str
|
|
definition_key: str
|
|
definition_name: str
|
|
name: str
|
|
status: str
|
|
endpoint_url: str | None = None
|
|
credential_ref: str | None = None
|
|
base_definition_revision: int
|
|
latest_definition_revision: int
|
|
update_available: bool
|
|
local_overrides: dict[str, Any]
|
|
protected_paths: list[str]
|
|
effective_configuration: dict[str, Any]
|
|
effective_hash: str
|
|
resource_revision: int
|
|
ambiguity_policy: str
|
|
updated_at: datetime
|
|
|
|
|
|
class ConnectorConfigurationListResponse(BaseModel):
|
|
items: list[ConnectorConfigurationItem]
|
|
|
|
|
|
class ConnectorRunRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
idempotency_key: str = Field(min_length=1, max_length=255)
|
|
input_rows: list[dict[str, Any]] | None = Field(default=None, max_length=500)
|
|
external_revision: str | None = Field(default=None, max_length=255)
|
|
|
|
|
|
class ConnectorRunItem(BaseModel):
|
|
id: str
|
|
tenant_id: str
|
|
configuration_id: str
|
|
mode: Literal["dry_run", "simulation"]
|
|
idempotency_key: str
|
|
status: str
|
|
review_state: str
|
|
definition_revision: int
|
|
configuration_revision: int
|
|
configuration_hash: str
|
|
input_hash: str
|
|
summary: dict[str, Any]
|
|
effects: list[dict[str, Any]]
|
|
diagnostics: list[dict[str, Any]]
|
|
provenance: dict[str, Any]
|
|
reviewed_by: str | None = None
|
|
reviewed_at: datetime | None = None
|
|
review_reason: str | None = None
|
|
created_at: datetime
|
|
|
|
|
|
class ConnectorRunListResponse(BaseModel):
|
|
items: list[ConnectorRunItem]
|
|
|
|
|
|
class ConnectorReviewRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
decision: Literal["approved", "rejected"]
|
|
reason: str = Field(min_length=5, max_length=1000)
|