Skip to content

Comments

feat(jmap): add caldav/jmap — JMAP calendar and task client#625

Open
SashankBhamidi wants to merge 32 commits intomasterfrom
feature/jmap
Open

feat(jmap): add caldav/jmap — JMAP calendar and task client#625
SashankBhamidi wants to merge 32 commits intomasterfrom
feature/jmap

Conversation

@SashankBhamidi
Copy link
Collaborator

@SashankBhamidi SashankBhamidi commented Feb 19, 2026

Adds caldav/jmap/, a new module providing JMAP calendar and task support alongside the existing CalDAV client. Zero modifications to any existing file.

The module follows the same layered sans-I/O design as the CalDAV side: pure method builders/parsers in methods/, dataclasses in objects/, bidirectional iCalendar ↔ JSCalendar conversion in convert/, HTTP + session logic in client.py and async_client.py.

Usage documentation in docs/source/jmap.rst covers auth, event CRUD, search, incremental sync, tasks, async API, and error handling.

Session bootstrap (session.py): GET /.well-known/jmap, resolve relative apiUrl via urljoin (Cyrus returns a relative path), select account via primaryAccounts[CALENDAR_CAPABILITY] with a fallback scan of all accounts. Raises JMAPCapabilityError if no calendar-capable account is found.

Auth (client.py): Basic when username is supplied, Bearer when only password is given, or a pre-built auth object via the auth kwarg. No 401-challenge-retry — a 401/403 from session or API endpoint raises JMAPAuthError immediately. JMAPError extends DAVError so existing CalDAV exception handlers catch JMAP errors too.

Calendar operations on both JMAPClient and AsyncJMAPClient: get_calendars, create_event, get_event, update_event, delete_event, search_events, get_sync_token, get_objects_by_sync_token. search_events uses a single batched request — CalendarEvent/query + a result reference into CalendarEvent/get — one HTTP round-trip regardless of result size. get_objects_by_sync_token raises JMAPMethodError(error_type="serverPartialFail") when the server truncates the change list (hasMoreChanges: true).

Task operations (RFC 9553): get_task_lists, create_task, get_task, update_task, delete_task. Task methods send _TASK_USING = [CORE_CAPABILITY, TASK_CAPABILITY]; servers without urn:ietf:params:jmap:tasks return an error methodResponse which _request converts to JMAPMethodError. Cyrus does not implement RFC 9553, so task integration tests are deferred.

AsyncJMAPClient (async_client.py) mirrors every method as a coroutine. Each request opens its own niquests.AsyncSession — no long-lived connection is held.

iCalendar ↔ JSCalendar conversion (convert/): full bidirectional mapping covering DTSTART (all-day, floating, UTC, IANA-tz, non-IANA TZID passthrough), DTEND/DURATION, RRULE, EXRULE, EXDATE, RECURRENCE-ID overrides, ORGANIZER/ATTENDEE with roles and participation status, VALARM (relative and absolute triggers), CATEGORIES, LOCATION, CLASS, TRANSP, SEQUENCE, PRIORITY, COLOR. Shared duration/datetime primitives (_timedelta_to_duration, _duration_to_timedelta, _format_local_dt) live only in convert/_utils.py.

Entry points (__init__.py): get_jmap_client(**kwargs) and get_async_jmap_client(**kwargs) read from the same sources as get_davclient — explicit kwargs, env vars, config file — and return None when no configuration is found.

254 unit tests (zero network, all mocked). 17 integration tests against live Cyrus Docker (6 sync + 6 async event CRUD/search/sync, 5 session/calendar checks); auto-skipped if server unreachable.

Introduces caldav/jmap/ as a purely additive package providing JMAP
calendar support alongside the existing CalDAV client. No existing
files are modified.

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.


if time_zone:
try:
import pytz # type: ignore[import]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytz is deprecated

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking to replace it to use zoneinfo instead

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics are identical

@SashankBhamidi
Copy link
Collaborator Author

SashankBhamidi commented Feb 20, 2026

@tobixen Is this better now? I’ll pull it tomorrow and work on CI errors. I committed the changes as suggestions for now.

This comment was marked as resolved.

docs(jmap): JMAP usage documentation and autodoc stubs

This comment was marked as spam.

@SashankBhamidi SashankBhamidi marked this pull request as ready for review February 21, 2026 08:59
@SashankBhamidi SashankBhamidi changed the title feat(jmap): JMAP calendar support feat(jmap): add caldav/jmap — JMAP calendar and task client Feb 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants