feat: govern file lifecycle and connector writes

This commit is contained in:
2026-08-20 22:06:57 +02:00
parent 95aef18955
commit 6c3cf1c55e
27 changed files with 2721 additions and 65 deletions
+88
View File
@@ -37,6 +37,7 @@ class FileConnectorSpaceCreateRequest(BaseModel):
library_id: str | None = None
remote_path: str = ""
sync_mode: Literal["manual"] = "manual"
read_only: bool = True
metadata: dict[str, Any] = Field(default_factory=dict)
@@ -45,6 +46,7 @@ class FileConnectorSpaceUpdateRequest(BaseModel):
library_id: str | None = None
remote_path: str | None = None
sync_mode: Literal["manual"] | None = None
read_only: bool | None = None
is_active: bool | None = None
metadata: dict[str, Any] | None = None
@@ -192,6 +194,10 @@ class FileAssetResponse(BaseModel):
created_at: str
updated_at: str
deleted_at: str | None = None
retained_until: str | None = None
legal_hold: bool = False
lifecycle_revision: int = 1
lifecycle_reason: str | None = None
audit_relevant: bool = False
metadata: dict[str, Any] | None = None
source_provenance: FileSourceProvenance | None = None
@@ -477,6 +483,7 @@ class FileConnectorProviderResponse(BaseModel):
installed: bool
browse_supported: bool
import_supported: bool
write_supported: bool = False
optional_dependency: str | None = None
permission_model: str
sync_strategy: str
@@ -535,6 +542,24 @@ class FileConnectorSyncResponse(BaseModel):
current_version_id: str
class FileConnectorWriteRequest(BaseModel):
file_id: str
remote_path: str
idempotency_key: str = Field(min_length=8, max_length=160)
expected_revision: str | None = Field(default=None, max_length=500)
class FileConnectorWriteResponse(BaseModel):
recovery_operation_id: str
status: str
replayed: bool = False
provider: str
remote_path: str
revision: str | None = None
checksum_sha256: str
size_bytes: int
class BulkDeleteRequest(BaseModel):
file_ids: list[str]
@@ -543,6 +568,69 @@ class BulkDeleteResponse(BaseModel):
deleted_count: int
class FileLifecycleUpdateRequest(BaseModel):
retained_until: datetime | None = None
legal_hold: bool = False
reason: str = Field(min_length=1, max_length=500)
expected_revision: int = Field(ge=1)
class FileRestoreResponse(BaseModel):
restored_count: int
class FileFolderRestoreResponse(BaseModel):
restored_folders: int
restored_files: int
class FilePurgePreviewRequest(BaseModel):
file_ids: list[str] = Field(min_length=1, max_length=100)
class FilePurgePreviewItem(BaseModel):
file_id: str
filename: str
lifecycle_revision: int
deleted_at: str | None = None
retained_until: str | None = None
legal_hold: bool = False
blockers: list[str] = Field(default_factory=list)
blob_ids: list[str] = Field(default_factory=list)
class FilePurgePreviewResponse(BaseModel):
preview_sha256: str
eligible: bool
items: list[FilePurgePreviewItem]
class FilePurgeExecuteRequest(FilePurgePreviewRequest):
preview_sha256: str = Field(min_length=64, max_length=64)
idempotency_key: str = Field(min_length=8, max_length=160)
approval_reference: str = Field(min_length=3, max_length=1000)
confirmation: Literal["PURGE"]
class FilePurgeResponse(BaseModel):
recovery_operation_id: str
status: str
replayed: bool = False
purged_files: int = 0
released_blobs: int = 0
class FileBlobGcRequest(BaseModel):
limit: int = Field(default=25, ge=1, le=100)
approval_reference: str = Field(min_length=3, max_length=1000)
class FileBlobGcResponse(BaseModel):
inspected_blobs: int
deleted_blobs: int
unresolved_operation_ids: list[str] = Field(default_factory=list)
class ConflictResolutionRequest(BaseModel):
target_path: str
action: Literal["overwrite", "rename", "skip"]