Skip to content

Attachments API

The AttachmentsAPI client reads attachments (list, detail, visibility) and uploads new files that can then be attached to posts, comments and tasks.

Resource grouping

All three endpoints are grouped under client.attachments regardless of their URL prefix. In particular, get() calls communication/posts/data/attachment-detail-by-id/{id} — a posts/data/ URL — but logically belongs to the attachments resource group (D-v0.3-1).

Usage

from pynteracta.client import InteractaClient

with InteractaClient(base_url="https://tenant.example.com", credentials=...) as client:
    # List all attachments for a post (first page)
    page = client.attachments.list_for_post(21269)
    for att in page.items_typed:
        print(att.id, att.name, att.content_mime_type)

    # Iterate all pages lazily
    for att in client.attachments.iterate_for_post(21269, page_size=20):
        print(att.id, att.name)

    # Fetch a single attachment with parent-post info
    detail = client.attachments.get(3001)
    print(detail.name, detail.post.id if detail.post else None)

    # Check which attachment IDs are visible
    visibility = client.attachments.check_visibility([3001, 3002, 3099])
    print(visibility.ids)  # only visible IDs

Uploading files

Interacta uploads a file in two steps: the API returns a temporary storage URL with a signed upload policy, then the file is sent there as a multipart POST form. upload() does both and returns an UploadedAttachment (content_ref, name, mime_type, temporary_download_url, .raw), which the write methods of posts, comments and tasks accept directly.

from pathlib import Path

from pynteracta.client import InteractaClient

with InteractaClient(base_url="https://tenant.example.com", credentials=...) as client:
    report = client.attachments.upload(Path("report.pdf"))           # name and MIME from the file
    data = client.attachments.upload(b"a,b\n1,2\n", name="data.csv")  # bytes need a name
    client.posts.create(79, title="Q4 report", attachments=[report, data])

    # a new version of an existing attachment (id 9466)
    v2 = client.attachments.upload(Path("report-v2.pdf"))
    client.posts.edit_attachments(21269, update=[v2.as_version_of(9466)])
  • upload(file, *, name=None, mime_type=None) accepts a Path or a path string (read in streaming and closed by the library), bytes, or a binary file object you opened (left open). With bytes or a file object name is required. The MIME type is guessed from the name's extension (application/octet-stream when unknown) unless mime_type is given. A missing path, a directory or a missing name raise before any request.
  • UploadedAttachment goes wherever a write accepts attachments (attachments, add_attachments, add) and is sent as {"name", "contentRef"}; as_version_of(id) gives {"attachmentId", "contentRef", "name"} for update_attachments / update. The server applies the new name to the new version (verified against a tenant). Dicts and DTOs already accepted keep working, also mixed with uploaded files.
  • upload(..., downloadable=False) marks the file as not downloadable. The flag never goes to the storage: the UploadedAttachment carries it and adds "downloadable" to the dicts of as_write_input() and as_version_of(), only when you set it (omitted, the server default is downloadable). On the test tenant the flag is stored on post attachments (custom and event posts: create, edit, copy, edit_attachments) and read back as PostAttachment.downloadable. On comments and tasks the server has no such field and rejects the write with 400 "Unrecognized field downloadable": upload files for comments and tasks without the flag. In the CLI pass it in the attachments of --json.
from pathlib import Path

from pynteracta.client import InteractaClient

with InteractaClient(base_url="https://tenant.example.com", credentials=...) as client:
    contract = client.attachments.upload(Path("contract.pdf"), downloadable=False)
    client.posts.create(79, title="Contract", attachments=[contract])
  • request_upload_url() performs only the first step and returns an UploadTicket (content_ref, upload_url, form_params, temporary_download_url), for callers that upload the file with another tool.

Errors and retries. Each step sends one request and never retries. A storage response other than 2xx (expired policy, file too large) raises UploadError with status_code, response_body (the storage's XML, as is), the redacted request_url and file_name; a timeout or network error raises TransportError, with an unknown outcome. timeout_seconds applies to each network operation of the upload, not to its total duration: raise it for large files.

Secrets. The storage request never carries the Interacta token. It goes through hooks and the audit log with its URL and status only: neither the file content nor the policy fields are ever logged, even with audit_log_bodies. The policy and signature fields and the Signature of signed URLs are redacted everywhere (see Audit Logging).

PostAttachment and AttachmentDetail expose temporaryContent*Link properties (temporary_content_view_link, temporary_content_download_link, etc.). These are short-lived signed URLs returned by the API; they are not shown in the default CLI table (D-v0.3-3a) but are accessible via --full, --fields, or --export in the CLI and directly on the facade object in Python.

Filter surface for list_for_post

Curated explicit kwargs: types, entity_types, mime_types, mime_type_category, order_by, order_desc.

Valid order_by values: 'name', 'mimeType', 'size', 'creatorUserId', 'creationTimestamp', 'entityType'.

Niche filters (aiSupportedFilter, postFilePickerFieldId, wfScreenFilePickerFieldId, language) are reachable only via the list_for_post_raw(req) escape hatch.

API Reference

pynteracta.api.attachments.AttachmentsAPI

Bases: ResourceClient

Client degli allegati: elenco, dettaglio e visibilità; upload di nuovi file (spec 04).

list_for_post(post_id, *, page_token=None, page_size=None, types=None, entity_types=None, mime_types=None, mime_type_category=None, order_by=None, order_desc=None)

POST /communication/attachments/data/posts/{postId}/attachments-list.

Parameters:

Name Type Description Default
post_id int

The post whose attachments to list.

required
page_token str | None

Pagination cursor from a previous response.

None
page_size int | None

Maximum items per page.

None
types list[int] | None

Filter by attachment type (1=STORAGE, 2=DRIVE).

None
entity_types list[int] | None

Filter by entity type (1=POST, 2=TASK, 3=COMMENT, 4=POST_FILE_PICKER, 5=SCREEN_FILE_PICKER).

None
mime_types list[str] | None

Filter by MIME type strings.

None
mime_type_category str | None

Filter by MIME category ('multimedia' or 'other').

None
order_by str | None

Sort field. Valid values: 'name', 'mimeType', 'size', 'creatorUserId', 'creationTimestamp', 'entityType'.

None
order_desc bool | None

True for descending, False for ascending (default: True).

None

list_for_post_raw(post_id, req)

POST attachment list with a pre-built request DTO (escape hatch).

iterate_for_post(post_id, *, page_size=None, types=None, entity_types=None, mime_types=None, mime_type_category=None, order_by=None, order_desc=None)

Lazy iterator over all pages of :meth:list_for_post.

get(attachment_id)

GET /communication/posts/data/attachment-detail-by-id/{attachmentId}.

Note: this endpoint lives under the posts/data/ URL prefix but belongs to the attachments resource group (D-v0.3-1).

check_visibility(attachment_ids)

POST /communication/attachments/data/check-visibility.

check_visibility_raw(req)

POST check-visibility with a pre-built request DTO (escape hatch).

upload(file, *, name=None, mime_type=None, downloadable=None)

Carica un file nello storage temporaneo del tenant, in due passi.

Prima chiede l'URL con :meth:request_upload_url, poi invia il file con un form multipart POST firmato, senza token Interacta. Il risultato si passa agli allegati dei metodi di scrittura di post, commenti e task. Una sola richiesta per passo: un errore risale al chiamante, mai un nuovo tentativo.

Parameters:

Name Type Description Default
file UploadSource

Un percorso (Path o stringa), dei bytes o un file binario aperto. Un percorso è letto in streaming e chiuso dalla libreria; un file aperto dal chiamante resta aperto.

required
name str | None

Nome del file; da un percorso è il nome del file, con bytes o un file aperto è obbligatorio.

None
mime_type str | None

MIME del file; se manca è dedotto dall'estensione del nome (application/octet-stream se ignota).

None
downloadable bool | None

Se l'allegato sarà scaricabile. Non va allo storage: il risultato lo porta negli allegati delle scritture ("downloadable"), previsto dal server per i post custom ed evento. None (default) non lo invia e decide il server.

None

Returns:

Name Type Description
Un UploadedAttachment

class:UploadedAttachment con content_ref, name, mime_type e

UploadedAttachment

temporary_download_url.

Raises:

Type Description
FileNotFoundError

Il percorso non esiste (nessuna richiesta parte).

IsADirectoryError

Il percorso è una cartella (nessuna richiesta parte).

ValueError

bytes o file aperto senza name (nessuna richiesta parte).

UploadError

Lo storage ha risposto con un errore.

TransportError

Timeout o errore di rete: l'esito è sconosciuto.

request_upload_url()

POST /core/storage/upload-new-attachment: il primo passo dell'upload.

Una sola richiesta, senza corpo. Il risultato porta l'URL dello storage, i campi della policy firmata e il content_ref; serve a chi carica il file con altri strumenti, altrimenti :meth:upload fa entrambi i passi.

Facade Reference

pynteracta.models.facade.attachments.PostAttachment

Narrow facade over :class:~generated.ListPostAttachmentsElementDTOModel.

Attributes:

Name Type Description
raw

The underlying generated DTO; access additional fields via this escape hatch.

Source code in src/pynteracta/models/facade/attachments.py
class PostAttachment:
    """Narrow facade over :class:`~generated.ListPostAttachmentsElementDTOModel`.

    Attributes:
        raw: The underlying generated DTO; access additional fields via this escape hatch.
    """

    def __init__(self, raw: generated.ListPostAttachmentsElementDTOModel) -> None:
        self.raw = raw

    @property
    def id(self) -> int | None:
        return self.raw.id

    @property
    def name(self) -> str | None:
        return self.raw.name

    @property
    def content_mime_type(self) -> str | None:
        return self.raw.contentMimeType

    @property
    def size(self) -> int | None:
        return self.raw.size

    @property
    def type(self) -> int | None:
        return self.raw.type

    @property
    def downloadable(self) -> bool | None:
        return self.raw.downloadable

    @property
    def version_number(self) -> int | None:
        return self.raw.versionNumber

    @property
    def creator_user(self) -> generated.UserDTO | None:
        return self.raw.creatorUser

    @property
    def creation_timestamp(self) -> int | None:
        return self.raw.creationTimestamp

    @property
    def hashtags(self) -> list[generated.HashtagDTOModel] | None:
        return self.raw.hashtags

    @property
    def temporary_content_view_link(self) -> str | None:
        return self.raw.temporaryContentViewLink

    @property
    def temporary_content_download_link(self) -> str | None:
        return self.raw.temporaryContentDownloadLink

    @property
    def temporary_content_preview_image_link(self) -> str | None:
        return self.raw.temporaryContentPreviewImageLink

    @property
    def temporary_content_preview_image_animated_link(self) -> str | None:
        return self.raw.temporaryContentPreviewImageAnimatedLink

    @property
    def temporary_content_preview_image_hi_res_link(self) -> str | None:
        return self.raw.temporaryContentPreviewImageHiResLink

    @property
    def temporary_content_preview_image_hi_res_animated_link(self) -> str | None:
        return self.raw.temporaryContentPreviewImageHiResAnimatedLink

    @classmethod
    def from_dict(cls, data: dict) -> PostAttachment:  # type: ignore[type-arg]
        """Parse from a raw API response dict element.

        Args:
            data: A single item dict from the ``items`` list.

        Returns:
            A new :class:`PostAttachment`.
        """
        raw = generated.ListPostAttachmentsElementDTOModel.model_validate(data)
        return cls(raw)

from_dict(data) classmethod

Parse from a raw API response dict element.

Parameters:

Name Type Description Default
data dict

A single item dict from the items list.

required

Returns:

Type Description
PostAttachment

A new :class:PostAttachment.

Source code in src/pynteracta/models/facade/attachments.py
@classmethod
def from_dict(cls, data: dict) -> PostAttachment:  # type: ignore[type-arg]
    """Parse from a raw API response dict element.

    Args:
        data: A single item dict from the ``items`` list.

    Returns:
        A new :class:`PostAttachment`.
    """
    raw = generated.ListPostAttachmentsElementDTOModel.model_validate(data)
    return cls(raw)

pynteracta.models.facade.attachments.PostAttachmentList

Narrow facade over :class:~generated.ListPostAttachmentsResponseDTO.

The items list holds :class:~generated.ListPostAttachmentsElementDTO objects, which are generated as RootModel[Any] stubs. Call :meth:items_typed to re-validate each item against the fully-typed :class:~generated.ListPostAttachmentsElementDTOModel.

Attributes:

Name Type Description
raw

The underlying generated DTO; access additional fields via this escape hatch.

Source code in src/pynteracta/models/facade/attachments.py
class PostAttachmentList:
    """Narrow facade over :class:`~generated.ListPostAttachmentsResponseDTO`.

    The ``items`` list holds :class:`~generated.ListPostAttachmentsElementDTO` objects, which are
    generated as ``RootModel[Any]`` stubs.  Call :meth:`items_typed` to re-validate each item
    against the fully-typed :class:`~generated.ListPostAttachmentsElementDTOModel`.

    Attributes:
        raw: The underlying generated DTO; access additional fields via this escape hatch.
    """

    def __init__(self, raw: generated.ListPostAttachmentsResponseDTO) -> None:
        self.raw = raw

    @property
    def next_page_token(self) -> str | None:
        return self.raw.nextPageToken

    @property
    def total_items_count(self) -> int | None:
        return self.raw.totalItemsCount

    @property
    def can_add_attachment(self) -> bool | None:
        return self.raw.canAddAttachment

    @property
    def image_attachments_count(self) -> int | None:
        return self.raw.imageAttachmentsCount

    @property
    def video_attachments_count(self) -> int | None:
        return self.raw.videoAttachmentsCount

    @property
    def audio_attachments_count(self) -> int | None:
        return self.raw.audioAttachmentsCount

    @property
    def documents_attachments_count(self) -> int | None:
        return self.raw.documentsAttachmentsCount

    @property
    def items_typed(self) -> list[PostAttachment]:
        """Re-validate each opaque list item into :class:`PostAttachment`.

        Returns:
            List of :class:`PostAttachment` instances.
        """
        if not self.raw.items:
            return []
        result = []
        for item in self.raw.items:
            root = item.root if hasattr(item, "root") else item
            if isinstance(root, dict):
                result.append(
                    PostAttachment(
                        generated.ListPostAttachmentsElementDTOModel.model_validate(root)
                    )
                )
        return result

    @classmethod
    def from_dict(cls, data: dict) -> PostAttachmentList:  # type: ignore[type-arg]
        """Parse from a raw API response dict.

        Args:
            data: Parsed JSON response body.

        Returns:
            A new :class:`PostAttachmentList`.
        """
        raw = generated.ListPostAttachmentsResponseDTO.model_validate(data)
        return cls(raw)

items_typed property

Re-validate each opaque list item into :class:PostAttachment.

Returns:

Type Description
list[PostAttachment]

List of :class:PostAttachment instances.

from_dict(data) classmethod

Parse from a raw API response dict.

Parameters:

Name Type Description Default
data dict

Parsed JSON response body.

required

Returns:

Type Description
PostAttachmentList

A new :class:PostAttachmentList.

Source code in src/pynteracta/models/facade/attachments.py
@classmethod
def from_dict(cls, data: dict) -> PostAttachmentList:  # type: ignore[type-arg]
    """Parse from a raw API response dict.

    Args:
        data: Parsed JSON response body.

    Returns:
        A new :class:`PostAttachmentList`.
    """
    raw = generated.ListPostAttachmentsResponseDTO.model_validate(data)
    return cls(raw)

pynteracta.models.facade.attachments.AttachmentDetail

Narrow facade over :class:~generated.GetPostAttachmentDetailResponseDTO.

attachmentData is typed as PostAttachmentDataDTO (a RootModel[Any] stub) in the generated code; we re-validate it against the typed sibling :class:~generated.PostAttachmentDataDTO1.

Attributes:

Name Type Description
raw

The underlying generated DTO; access additional fields via this escape hatch.

Source code in src/pynteracta/models/facade/attachments.py
class AttachmentDetail:
    """Narrow facade over :class:`~generated.GetPostAttachmentDetailResponseDTO`.

    ``attachmentData`` is typed as ``PostAttachmentDataDTO`` (a ``RootModel[Any]`` stub) in the
    generated code; we re-validate it against the typed sibling
    :class:`~generated.PostAttachmentDataDTO1`.

    Attributes:
        raw: The underlying generated DTO; access additional fields via this escape hatch.
    """

    def __init__(self, raw: generated.GetPostAttachmentDetailResponseDTO) -> None:
        self.raw = raw
        self._attachment_data: generated.PostAttachmentDataDTO1 | None = None
        if raw.attachmentData is not None:
            ad = raw.attachmentData
            root = ad.root if hasattr(ad, "root") else ad
            if isinstance(root, dict):
                self._attachment_data = generated.PostAttachmentDataDTO1.model_validate(root)
        self._post: generated.PostBaseInfoDTO1 | None = None
        if raw.post is not None:
            pr = raw.post
            proot = pr.root if hasattr(pr, "root") else pr
            if isinstance(proot, dict):
                self._post = generated.PostBaseInfoDTO1.model_validate(proot)

    @property
    def attachment_data(self) -> generated.PostAttachmentDataDTO1 | None:
        """Typed attachment data DTO."""
        return self._attachment_data

    @property
    def post(self) -> generated.PostBaseInfoDTO1 | None:
        """Parent post base info (typed variant of the RootModel stub)."""
        return self._post

    @property
    def id(self) -> int | None:
        return self._attachment_data.id if self._attachment_data else None

    @property
    def name(self) -> str | None:
        return self._attachment_data.name if self._attachment_data else None

    @property
    def content_mime_type(self) -> str | None:
        return self._attachment_data.contentMimeType if self._attachment_data else None

    @property
    def size(self) -> int | None:
        return self._attachment_data.size if self._attachment_data else None

    @property
    def type(self) -> int | None:
        return self._attachment_data.type if self._attachment_data else None

    @property
    def downloadable(self) -> bool | None:
        return self._attachment_data.downloadable if self._attachment_data else None

    @property
    def temporary_content_view_link(self) -> str | None:
        return self._attachment_data.temporaryContentViewLink if self._attachment_data else None

    @property
    def temporary_content_download_link(self) -> str | None:
        return self._attachment_data.temporaryContentDownloadLink if self._attachment_data else None

    @classmethod
    def from_dict(cls, data: dict) -> AttachmentDetail:  # type: ignore[type-arg]
        """Parse from a raw API response dict.

        Args:
            data: Parsed JSON response body.

        Returns:
            A new :class:`AttachmentDetail`.
        """
        raw = generated.GetPostAttachmentDetailResponseDTO.model_validate(data)
        return cls(raw)

attachment_data property

Typed attachment data DTO.

post property

Parent post base info (typed variant of the RootModel stub).

from_dict(data) classmethod

Parse from a raw API response dict.

Parameters:

Name Type Description Default
data dict

Parsed JSON response body.

required

Returns:

Type Description
AttachmentDetail

A new :class:AttachmentDetail.

Source code in src/pynteracta/models/facade/attachments.py
@classmethod
def from_dict(cls, data: dict) -> AttachmentDetail:  # type: ignore[type-arg]
    """Parse from a raw API response dict.

    Args:
        data: Parsed JSON response body.

    Returns:
        A new :class:`AttachmentDetail`.
    """
    raw = generated.GetPostAttachmentDetailResponseDTO.model_validate(data)
    return cls(raw)

pynteracta.models.facade.attachments.AttachmentVisibility

Thin facade over :class:~generated.CheckVisibilityResponseDTO.

Returns which attachment IDs are visible to the current user. Kept separate from the v0.2 :class:~pynteracta.models.facade.posts.VisibilityResult.

Attributes:

Name Type Description
raw

The underlying generated DTO; access additional fields via this escape hatch.

Source code in src/pynteracta/models/facade/attachments.py
class AttachmentVisibility:
    """Thin facade over :class:`~generated.CheckVisibilityResponseDTO`.

    Returns which attachment IDs are visible to the current user.
    Kept separate from the v0.2 :class:`~pynteracta.models.facade.posts.VisibilityResult`.

    Attributes:
        raw: The underlying generated DTO; access additional fields via this escape hatch.
    """

    def __init__(self, raw: generated.CheckVisibilityResponseDTO) -> None:
        self.raw = raw

    @property
    def ids(self) -> list[int]:
        """List of visible attachment IDs."""
        return self.raw.ids or []

    @classmethod
    def from_dict(cls, data: dict) -> AttachmentVisibility:  # type: ignore[type-arg]
        """Parse from a raw API response dict.

        Args:
            data: Parsed JSON response body.

        Returns:
            A new :class:`AttachmentVisibility`.
        """
        raw = generated.CheckVisibilityResponseDTO.model_validate(data)
        return cls(raw)

ids property

List of visible attachment IDs.

from_dict(data) classmethod

Parse from a raw API response dict.

Parameters:

Name Type Description Default
data dict

Parsed JSON response body.

required

Returns:

Type Description
AttachmentVisibility

A new :class:AttachmentVisibility.

Source code in src/pynteracta/models/facade/attachments.py
@classmethod
def from_dict(cls, data: dict) -> AttachmentVisibility:  # type: ignore[type-arg]
    """Parse from a raw API response dict.

    Args:
        data: Parsed JSON response body.

    Returns:
        A new :class:`AttachmentVisibility`.
    """
    raw = generated.CheckVisibilityResponseDTO.model_validate(data)
    return cls(raw)

pynteracta.models.facade.attachments.UploadedAttachment

Un file caricato nello storage temporaneo, pronto da allegare (spec 04).

Si passa direttamente agli attachments (o add_attachments, add) dei metodi di scrittura di post, commenti e task, che lo inviano come {name, contentRef}; per caricare una nuova versione di un allegato esistente si usa :meth:as_version_of. Con downloadable indicato (spec 09) i due riferimenti portano anche "downloadable", che il server prevede negli allegati dei post custom ed evento; senza, decide il server (default scaricabile).

Attributes:

Name Type Description
raw

Il GetTemporaryImageUploadUrlBaseResponseDTO del primo passo.

Source code in src/pynteracta/models/facade/attachments.py
class UploadedAttachment:
    """Un file caricato nello storage temporaneo, pronto da allegare (spec 04).

    Si passa direttamente agli ``attachments`` (o ``add_attachments``, ``add``) dei metodi di
    scrittura di post, commenti e task, che lo inviano come ``{name, contentRef}``; per caricare
    una nuova versione di un allegato esistente si usa :meth:`as_version_of`. Con
    ``downloadable`` indicato (spec 09) i due riferimenti portano anche ``"downloadable"``, che il
    server prevede negli allegati dei post custom ed evento; senza, decide il server (default
    scaricabile).

    Attributes:
        raw: Il ``GetTemporaryImageUploadUrlBaseResponseDTO`` del primo passo.
    """

    def __init__(
        self,
        ticket: UploadTicket,
        *,
        name: str,
        mime_type: str,
        downloadable: bool | None = None,
    ) -> None:
        self.raw = ticket.raw
        self._ticket = ticket
        self._name = name
        self._mime_type = mime_type
        self._downloadable = downloadable

    @property
    def content_ref(self) -> str | None:
        """Riferimento del file nello storage."""
        return self._ticket.content_ref

    @property
    def name(self) -> str:
        """Nome con cui il file è stato caricato."""
        return self._name

    @property
    def mime_type(self) -> str:
        """MIME con cui il file è stato caricato."""
        return self._mime_type

    @property
    def temporary_download_url(self) -> str | None:
        """URL firmato da cui rileggere il file caricato."""
        return self._ticket.temporary_download_url

    @property
    def downloadable(self) -> bool | None:
        """Se l'allegato sarà scaricabile; ``None`` se non indicato al caricamento."""
        return self._downloadable

    def as_write_input(self) -> dict[str, Any]:
        """Il riferimento da allegare: ``{"name", "contentRef"}``, con ``downloadable`` se dato."""
        return self._with_downloadable({"name": self._name, "contentRef": self.content_ref})

    def as_version_of(self, attachment_id: int) -> dict[str, Any]:
        """Il riferimento come nuova versione dell'allegato ``attachment_id``.

        Va in ``update_attachments`` (o ``update`` di ``edit_attachments``); porta
        ``downloadable`` se indicato.
        """
        return self._with_downloadable(
            {"attachmentId": attachment_id, "contentRef": self.content_ref, "name": self._name}
        )

    def _with_downloadable(self, ref: dict[str, Any]) -> dict[str, Any]:
        if self._downloadable is not None:
            ref["downloadable"] = self._downloadable
        return ref

content_ref property

Riferimento del file nello storage.

downloadable property

Se l'allegato sarà scaricabile; None se non indicato al caricamento.

mime_type property

MIME con cui il file è stato caricato.

name property

Nome con cui il file è stato caricato.

temporary_download_url property

URL firmato da cui rileggere il file caricato.

as_version_of(attachment_id)

Il riferimento come nuova versione dell'allegato attachment_id.

Va in update_attachments (o update di edit_attachments); porta downloadable se indicato.

Source code in src/pynteracta/models/facade/attachments.py
def as_version_of(self, attachment_id: int) -> dict[str, Any]:
    """Il riferimento come nuova versione dell'allegato ``attachment_id``.

    Va in ``update_attachments`` (o ``update`` di ``edit_attachments``); porta
    ``downloadable`` se indicato.
    """
    return self._with_downloadable(
        {"attachmentId": attachment_id, "contentRef": self.content_ref, "name": self._name}
    )

as_write_input()

Il riferimento da allegare: {"name", "contentRef"}, con downloadable se dato.

Source code in src/pynteracta/models/facade/attachments.py
def as_write_input(self) -> dict[str, Any]:
    """Il riferimento da allegare: ``{"name", "contentRef"}``, con ``downloadable`` se dato."""
    return self._with_downloadable({"name": self._name, "contentRef": self.content_ref})

pynteracta.models.facade.attachments.UploadTicket

Façade sulla risposta di core/storage/upload-new-attachment (spec 04).

È il primo passo dell'upload: il riferimento del file nello storage temporaneo, l'URL a cui caricarlo e i campi della policy firmata da inviare nel form multipart.

Attributes:

Name Type Description
raw

Il GetTemporaryImageUploadUrlBaseResponseDTO generato.

Source code in src/pynteracta/models/facade/attachments.py
class UploadTicket:
    """Façade sulla risposta di ``core/storage/upload-new-attachment`` (spec 04).

    È il primo passo dell'upload: il riferimento del file nello storage temporaneo, l'URL a cui
    caricarlo e i campi della policy firmata da inviare nel form multipart.

    Attributes:
        raw: Il ``GetTemporaryImageUploadUrlBaseResponseDTO`` generato.
    """

    def __init__(self, raw: generated.GetTemporaryImageUploadUrlBaseResponseDTO) -> None:
        self.raw = raw
        self._form_params = dict(raw.uploadMultipartRequestBodyParams or {})

    @property
    def content_ref(self) -> str | None:
        """Riferimento del file nello storage, da passare nelle scritture come ``contentRef``."""
        return self.raw.contentRef

    @property
    def upload_url(self) -> str | None:
        """URL dello storage a cui inviare il form multipart."""
        return self.raw.uploadUrl

    @property
    def form_params(self) -> dict[str, str]:
        """Campi della policy firmata, da inviare prima del file (una copia: ``{}`` se assenti).

        Contengono credenziali temporanee: non vanno loggati.
        """
        return self._form_params

    @property
    def temporary_download_url(self) -> str | None:
        """URL firmato da cui rileggere il file caricato, finché la firma non scade."""
        return self.raw.temporaryDownloadUrl

    @classmethod
    def from_dict(cls, data: dict) -> UploadTicket:  # type: ignore[type-arg]
        """Legge la risposta di ``upload-new-attachment``."""
        return cls(generated.GetTemporaryImageUploadUrlBaseResponseDTO.model_validate(data))

content_ref property

Riferimento del file nello storage, da passare nelle scritture come contentRef.

form_params property

Campi della policy firmata, da inviare prima del file (una copia: {} se assenti).

Contengono credenziali temporanee: non vanno loggati.

temporary_download_url property

URL firmato da cui rileggere il file caricato, finché la firma non scade.

upload_url property

URL dello storage a cui inviare il form multipart.

from_dict(data) classmethod

Legge la risposta di upload-new-attachment.

Source code in src/pynteracta/models/facade/attachments.py
@classmethod
def from_dict(cls, data: dict) -> UploadTicket:  # type: ignore[type-arg]
    """Legge la risposta di ``upload-new-attachment``."""
    return cls(generated.GetTemporaryImageUploadUrlBaseResponseDTO.model_validate(data))

pynteracta.exceptions.UploadError

Bases: InteractaError

Lo storage ha rifiutato il caricamento di un file (risposta non 2xx, spec 04).

response_body è il corpo della risposta dello storage così com'è (di solito XML); request_url è l'URL di storage redatto; request_id è assente. file_name è il nome del file che non è stato caricato. Timeout ed errori di rete restano :class:TransportError.

Source code in src/pynteracta/exceptions.py
class UploadError(InteractaError):
    """Lo storage ha rifiutato il caricamento di un file (risposta non 2xx, spec 04).

    ``response_body`` è il corpo della risposta dello storage così com'è (di solito XML);
    ``request_url`` è l'URL di storage redatto; ``request_id`` è assente. ``file_name`` è il nome
    del file che non è stato caricato. Timeout ed errori di rete restano :class:`TransportError`.
    """

    def __init__(self, message: str = "", *, file_name: str | None = None, **kwargs: Any) -> None:
        super().__init__(message, **kwargs)
        self.file_name = file_name