feat: integrate files and portal surfaces
This commit is contained in:
@@ -24,6 +24,10 @@ from govoplan_core.core.postbox import (
|
||||
PostboxTargetRef,
|
||||
PostboxWrappedKeyRef,
|
||||
)
|
||||
from govoplan_core.core.files import (
|
||||
PostboxFileReferenceRequest,
|
||||
postbox_file_reference_provider,
|
||||
)
|
||||
from govoplan_core.db.session import get_session
|
||||
from govoplan_postbox.backend.manifest import (
|
||||
ACKNOWLEDGE_SCOPE,
|
||||
@@ -34,9 +38,11 @@ from govoplan_postbox.backend.manifest import (
|
||||
SEND_SCOPE,
|
||||
TEMPLATE_ADMIN_SCOPE,
|
||||
)
|
||||
from govoplan_postbox.backend.runtime import get_service
|
||||
from govoplan_postbox.backend.runtime import get_registry, get_service
|
||||
from govoplan_postbox.backend.schemas import (
|
||||
PostboxAccessDecisionResponse,
|
||||
PostboxAttachmentResolutionItem,
|
||||
PostboxAttachmentResolutionResponse,
|
||||
PostboxDeliveryCreateRequest,
|
||||
PostboxDeliveryResponse,
|
||||
PostboxDirectoryItem,
|
||||
@@ -237,6 +243,7 @@ def _template_item(template) -> PostboxTemplateItem:
|
||||
"address_pattern": revision.address_pattern,
|
||||
"classification": revision.classification,
|
||||
"allow_vacant_delivery": revision.allow_vacant_delivery,
|
||||
"portal_visible": revision.portal_visible,
|
||||
"encryption_profile": revision.encryption_profile,
|
||||
"encryption_vault_id": revision.encryption_vault_id,
|
||||
"history_policy": dict(revision.history_policy or {}),
|
||||
@@ -486,6 +493,114 @@ def api_get_postbox_message(
|
||||
return _message_item(message)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/messages/{message_id}/attachment-resolutions",
|
||||
response_model=PostboxAttachmentResolutionResponse,
|
||||
)
|
||||
def api_resolve_postbox_message_attachments(
|
||||
message_id: str,
|
||||
assignment_context_id: str | None = None,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> PostboxAttachmentResolutionResponse:
|
||||
_require(principal, READ_SCOPE)
|
||||
try:
|
||||
message = get_service().get_message(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
message_id=message_id,
|
||||
actor=_actor(
|
||||
principal,
|
||||
assignment_context_id=assignment_context_id,
|
||||
),
|
||||
)
|
||||
except PostboxError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
if message is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Postbox message not found.",
|
||||
)
|
||||
|
||||
provider = postbox_file_reference_provider(get_registry())
|
||||
file_types = {"file", "file_asset", "files:file", "file_version", "files:file_version"}
|
||||
requests = tuple(
|
||||
PostboxFileReferenceRequest(
|
||||
reference_type=attachment.reference_type,
|
||||
reference_id=attachment.reference_id,
|
||||
postbox_id=message.postbox_id,
|
||||
message_id=message.id,
|
||||
)
|
||||
for attachment in message.attachments
|
||||
if attachment.reference_type.strip().casefold() in file_types
|
||||
)
|
||||
resolved = (
|
||||
provider.resolve_postbox_references(
|
||||
session,
|
||||
principal,
|
||||
tenant_id=principal.tenant_id,
|
||||
requests=requests,
|
||||
)
|
||||
if provider is not None and requests
|
||||
else ()
|
||||
)
|
||||
by_reference = {
|
||||
(item.reference_type, item.reference_id): item for item in resolved
|
||||
}
|
||||
items: list[PostboxAttachmentResolutionItem] = []
|
||||
for attachment in message.attachments:
|
||||
resolution = by_reference.get(
|
||||
(attachment.reference_type, attachment.reference_id)
|
||||
)
|
||||
is_file = attachment.reference_type.strip().casefold() in file_types
|
||||
attachment_payload = asdict(attachment)
|
||||
attachment_payload.update(
|
||||
{
|
||||
"name": (
|
||||
resolution.filename
|
||||
if resolution and resolution.filename
|
||||
else attachment.name
|
||||
),
|
||||
"media_type": (
|
||||
resolution.content_type
|
||||
if resolution and resolution.content_type
|
||||
else attachment.media_type
|
||||
),
|
||||
"size_bytes": (
|
||||
resolution.size_bytes
|
||||
if resolution and resolution.size_bytes is not None
|
||||
else attachment.size_bytes
|
||||
),
|
||||
"digest": (
|
||||
resolution.sha256
|
||||
if resolution and resolution.sha256
|
||||
else attachment.digest
|
||||
),
|
||||
}
|
||||
)
|
||||
items.append(
|
||||
PostboxAttachmentResolutionItem(
|
||||
**attachment_payload,
|
||||
available=bool(resolution and resolution.available),
|
||||
reason_code=(
|
||||
resolution.reason_code
|
||||
if resolution is not None
|
||||
else (
|
||||
"files_provider_unavailable"
|
||||
if is_file
|
||||
else "reference_provider_unavailable"
|
||||
)
|
||||
),
|
||||
file_asset_id=resolution.file_asset_id if resolution else None,
|
||||
file_version_id=resolution.file_version_id if resolution else None,
|
||||
download_path=resolution.download_path if resolution else None,
|
||||
provenance=dict(resolution.provenance) if resolution else {},
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
return PostboxAttachmentResolutionResponse(attachments=items)
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/messages/{message_id}/state",
|
||||
response_model=PostboxMessageItem,
|
||||
|
||||
Reference in New Issue
Block a user