Sync incrementally#
JMAP’s state-based sync lets you fetch only what changed since the last call, without scanning the full calendar.
Fetch a token, then the delta#
# Record the current state
token = client.get_sync_token()
# ... time passes, events are created, modified, or deleted ...
# Fetch only the delta
added, modified, deleted, token = client.get_objects_by_sync_token(token)
for obj in added:
print("New:", obj.get_icalendar_instance())
for obj in modified:
print("Updated:", obj.get_icalendar_instance())
for event_id in deleted:
print("Deleted ID:", event_id)
added and modified are lists of JMAPCalendarObject. deleted is a list of event IDs: those objects no longer exist on the server, so their data can’t be fetched. The fourth element is the server’s new sync token. Chaining straight from it avoids the race window a separate get_sync_token() round trip would open.
Handle a truncated change list#
get_objects_by_sync_token() raises JMAPMethodError (error_type="serverPartialFail") if the server truncated the change list (hasMoreChanges: true). If this happens, call get_sync_token() to establish a fresh baseline and re-sync from scratch. See Handle errors for other errors this can raise.
Persist the token between runs#
import json
import pathlib
TOKEN_FILE = pathlib.Path("sync_token.json")
def load_token():
if TOKEN_FILE.exists():
return json.loads(TOKEN_FILE.read_text())["token"]
return None
def save_token(token):
TOKEN_FILE.write_text(json.dumps({"token": token}))
token = load_token()
if token is None:
token = client.get_sync_token()
save_token(token)
else:
added, modified, deleted, token = client.get_objects_by_sync_token(token)
# process changes here
save_token(token)