Source code for calendaring_jmap.objects.attachment
# SPDX-FileCopyrightText: 2026 calendaring-jmap contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
JMAP event attachment object.
Wraps a JSCalendar Link (:rfc:`8984#section-1.4.11`) whose ``rel`` marks it
as an attachment, as returned in a CalendarEvent's ``links`` property.
"""
from __future__ import annotations
from dataclasses import dataclass
from calendaring_jmap.constants import LINK_REL_ENCLOSURE
[docs]
@dataclass
class JMAPAttachment:
"""An event attachment, backed by a JSCalendar Link object.
Attributes:
link_id: The key under which this Link appears in the event's
``links`` map.
href: URL from which the attachment can be fetched, or ``None``
if the Link carries a ``blobId`` instead (a calendars-draft
extension this client does not itself write, see
:meth:`~calendaring_jmap.client.JMAPClient.attach_to_event`).
blob_id: The referenced blob's id, when the server returns one.
title: Human-readable description of the attachment.
content_type: Media type of the attachment, if known.
size: Size in octets, if known.
"""
link_id: str
href: str | None = None
blob_id: str | None = None
title: str | None = None
content_type: str | None = None
size: int | None = None
[docs]
@classmethod
def from_jmap(cls, link_id: str, data: dict) -> JMAPAttachment:
"""Construct a JMAPAttachment from a raw JSCalendar Link dict.
Unknown keys in ``data`` are silently ignored so that forward
compatibility is maintained as the spec evolves.
"""
return cls(
link_id=link_id,
href=data.get("href"),
blob_id=data.get("blobId"),
title=data.get("title"),
content_type=data.get("contentType"),
size=data.get("size"),
)
[docs]
@staticmethod
def is_attachment(link_data: dict) -> bool:
"""Return whether a raw JSCalendar Link dict represents an attachment.
A Link is an attachment when its ``rel`` is ``"enclosure"``
(:rfc:`8984#section-1.4.11`); other ``links`` entries (e.g. a
conference URL) are not.
Known Cyrus limitation, confirmed live: Cyrus (3.13.7-545-gc04cece19)
accepts a Link with ``rel: "enclosure"`` with no error, but does not
reliably persist it (other ``rel`` values round-trip fine, isolating
this to that one value). An attachment written by
:meth:`~calendaring_jmap.client.JMAPClient.attach_to_event` may
therefore not be classified as one by this method when read back
from Cyrus. Stalwart has no such issue.
"""
return link_data.get("rel") == LINK_REL_ENCLOSURE