Keep a mirror or index in sync with a drive: delta queries (root/delta), a sync-changes loop that walks every page to the new delta link, 410 Gone resync handling, and change-notification subscriptions for the drive root.

Sync loop

  1. Once: link ::delta/latest-delta-link() ("changes from now on"), or sync-changes(null) to enumerate everything first.
  2. Each run (a schedule, or when a notification arrives): set ::delta/sync-changes(link); upsert changed-items(set), drop deleted-ids(set), store set.delta-link.
  3. When set.resync is true, Graph threw the old link away (410) and set is a full enumeration: with resync-mode "resyncChangesApplyDifferences" replace local state with it (delete local ids not in the set: stale-ids); with "resyncChangesUploadDifferences" keep local-only items and reconcile them yourself.

Rules from Graph. Track items by id (delta omits paths, and an item can appear more than once: the last one wins). Deleted items carry a deleted facet and may have no name. On OneDrive for work or school and SharePoint, delta works only from the drive root.

Push. subscribe asks Graph to POST to your HTTPS endpoint when anything in the drive changes (updated on the root is the only option for drives). Notifications carry no item data: treat one as "run sync-changes now". Subscriptions last at most 42,300 minutes (under 30 days); renew from a schedule with renew-subscription.

Permissions. Files.Read / Files.Read.All (delegated), Files.Read.All or Sites.Read.All (app-only). Prefer headers such as hierarchicalsharing or deltashowsharingchanges go in opts.prefer.

Values

DEFAULT_MAX_PAGES

DEFAULT_MAX_PAGES: Int 5000

DELTA_LOCAL_KEYS

DELTA_LOCAL_KEYS: Vec [
  "delta-token",
  "prefer",
  "max-pages"
]

RESYNC_CODES

RESYNC_CODES: Vec [
  "resyncChangesApplyDifferences",
  "resyncChangesUploadDifferences",
  "resyncRequired"
]

Functions

build-delta-set

fn (items: Vec, delta-link: Str, pages: Int, resync-mode: Str?): DeltaSet

Assemble a DeltaSet from collected items (pure).

changed-files

fn (set: Any): Vec

changed-items without folders and packages (pure).

changed-items

fn (set: Any): Vec

The live (not deleted) items of a DeltaSet (or a Vec of driveItems) as DocFiles, folders included; filter on is-folder for files only (pure).

classify-delta-response

fn (response: Result): DeltaPage

Interpret a delta HTTP response (pure): a DeltaPage, with gone for 410; an Err for other failures.

deleted-ids

fn (set: Any): Vec

The ids a DeltaSet (or a Vec of driveItems) deleted (pure). Delete a folder locally only once it is empty after applying the set.

delta-headers

fn (opts: Map): Map

delta-page

fn (link: Str?): DeltaPage
fn (link: Str?, opts: Map): DeltaPage

Fetch one delta page: GET link (a nextLink or deltaLink) when given, else the drive's first delta URL (delta-path). Returns a DeltaPage; a 410 is returned as gone: true rather than an Err.

delta-path

fn (opts: Map): Str

The first delta URL for a drive (pure): {drive}/root/delta, with token=latest / a token / a timestamp from opts.delta-token, and OData options (select, top, expand).

is-deleted

fn (item: Map): Bool

True when a delta item carries a deleted facet (pure).

latest-by-id

fn (items: Vec): Vec

Keep the last occurrence of each item id, in order of last appearance (pure). Items without an id are dropped.

fn (): Str
fn (opts: Map): Str

GET {drive}/root/delta?token=latest - a delta link for "changes from now on", without enumerating the drive. Store it and pass it to sync-changes.

renew-subscription

fn (subscription-id: Str): Map
fn (subscription-id: Str, opts: Map): Map

Extend a drive subscription to the maximum lifetime (minus an hour), or opts.expires-in-minutes. Run it from a schedule well inside the lifetime, e.g. weekly.

renew-onedrive-subscription
meta {schedule: "0 5 * * 1"}
fn (event) {
  sub-id ::hot::store/get(::hot::store/Map({name: "onedrive-sync"}), "subscription-id")
  ::microsoft::onedrive::delta/renew-subscription(sub-id)
}

stale-ids

fn (local-ids: Vec, set: Map): Vec

For a resync set (resync-mode resyncChangesApplyDifferences): the local-ids that the full enumeration no longer contains, which you should delete locally (pure). Empty for a normal incremental set.

subscribe

fn (notification-url: Str): Map
fn (notification-url: Str, opts: Map): Map

Subscribe notification-url to changes anywhere in a drive (updated on its root; the only change type drives support). opts takes drive, client-state (recommended; at most 128 characters), lifecycle-url, expires-in-minutes (default the 42,300-minute cap minus an hour) and credentials. Returns the subscription; keep its id.

sub ::microsoft::onedrive::delta/subscribe("https://api.example.com/onedrive/hook", {
  client-state: ::hot::ctx/get("microsoft.graph.client-state")
})

subscription-resource

fn (opts: Map): Str

The subscription resource for a drive's root (pure): me/drive/root, drives/{id}/root, users/{id}/drive/root, ...

sync-changes

fn (delta-link: Str?): DeltaSet
fn (delta-link: Str?, opts: Map): DeltaSet

Read every delta page since delta-link (null: enumerate the whole drive) and return a DeltaSet with the link to use next time. A 410 Gone restarts the enumeration from Graph's Location and marks the set resync (see the namespace doc). opts takes drive, select, top, prefer, max-pages (default 5000) and credentials.

::delta ::microsoft::onedrive::delta

set ::delta/sync-changes(saved-link)
upserts ::delta/changed-items(set)    // DocFiles to (re)index
deletes ::delta/deleted-ids(set)      // ids to drop
saved-link set.delta-link

sync-loop

fn (link: Str?, opts: Map, acc: Vec, pages: Int, resync-mode: Str?, max-pages: Int): DeltaSet

Types

DeltaPage

DeltaPage type {
    items: Vec,
    next-link: Str?,
    delta-link: Str?,
    gone: Bool,
    resync-code: Str?,
    restart-link: Str?
}

One delta response: items (value), next-link, delta-link, or for a 410 Gone: gone: true, resync-code (Graph's error code) and restart-link (the Location header to enumerate from scratch, null when Graph sent none).

DeltaSet

DeltaSet type {
    items: Vec,
    deleted-ids: Vec,
    delta-link: Str,
    pages: Int,
    resync: Bool,
    resync-mode: Str?
}

Everything that changed since a delta link.

  • items - driveItems in feed order, latest occurrence per id
  • deleted-ids - ids of items with a deleted facet (not seen alive later)
  • delta-link - store it and pass it to the next sync-changes
  • pages - how many pages were read
  • resync - true when Graph answered 410 and this is a full enumeration
  • resync-mode - Graph's code (resyncChangesApplyDifferences, resyncChangesUploadDifferences), when resync