Lolly
App öffnen

Canto live-verify runbook (kind: canto)

Notiz: Lolly Work

This guide covers the optional organisation service. Configuration, console and API paths refer to your Work instance. The packaged console keeps its release-matched copy of this guide.

The tenant-day checklist for the canto driver. Every endpoint path and field name in that driver comes from Canto's public documentation and none of it has been confirmed against a real tenant; this pass confirms the driver against your tenant. Steps 1 and 2 store nothing; step 3 writes only into your own store. About 30 minutes.

Before you start

Step 1: auth

export LW_BASE=https://lolly.example
lw login --cookie 'lw_session=…'          # dev instances: lw login --email you@acme.example

lw providers preview --kind canto --options '{"tenant":"acme","domain":"com"}'

The credential prompt is hidden; paste the blob on one line. health ok - canto followed by a mapped sample table means the token exchange and the first list call both worked. Exit code 2 means health failed.

SymptomWhat it meansFix
health FAILED: oauth token refresh failed (400)The token endpoint read the form and rejected the request SHAPE: a different grant, the client secret wanted in an Authorization header, a required scope or audience param. Re-capturing the credential cannot helpRow 2a: getAccessToken in providers/oauth.ts
health FAILED: oauth token refresh failed (401)The credential: App ID/Secret wrong, refresh token wrong, or the grant revokedRe-capture the blob with lw providers credential
health FAILED: oauth token refresh failed (404) or (405)The token URL is wrongRow 2: options.tokenUrl
health FAILED: canto api 401 for /image?limit=100&start=0The token minted and Canto refused the token. 403 here usually means API access is not on this contract tier: settle that before spending the sessionThe grant or the contract, not the code
health FAILED: canto api 404 for /image?limit=100&start=0The REST base or the scheme list path is wrongRows 1 and 3
health FAILED: canto url outside allowed hostsAn options.baseUrl or options.tokenUrl override points off the canto.com / .global / .de familyThe override, or ALLOWED_HOSTS

Step 2: capture the shape

lw providers preview --kind canto --shape \
    --options '{"tenant":"acme","domain":"com"}' > canto-shape.txt

Shape mode lists no sample and returns no values: key names and types only, so that file is sendable as it stands (the credential prompt goes to stderr). It runs only when health is ok.

canto  GET /api/v1/image?limit=100&start=0
  envelope: results: object[] (100) · found: number · limit: number · start: number
  record: (100 under "results", keys unioned)
    id: string · scheme: string · name: string · size: number · lastModified: string
    tag: string[] · album: string · additional: { Expiry Date: string, Campaign: string }
    thumbnailUrl: string
  MAPPED BY THIS DRIVER: results · id · name · scheme · size · lastModified · tag · album
    additional
  IN THE RESPONSE, NOT MAPPED: found · limit · start · thumbnailUrl
  EXPECTED BY THIS DRIVER, ABSENT: approvalStatus (APPROVAL_STATE_KEYS)

--shape --remote-id <id> answers that this driver makes no per-asset detail call: Canto's bytes stream from a path built out of the list record, so rows 17 and 18 are settled in step 3.

The assumption table

One row per LIVE-VERIFY bullet in the driver header (server/src/catalog/providers/canto.ts), with the record-field bullet expanded per constant. If a row and that header disagree, the header is right and this table is stale.

#The assumptionHow the report answers itFix it in providers/canto.ts
1The REST base is https://<tenant>.canto.<domain>/api/v1Not in the diff: the report's first line prints the path it called. A wrong base fails step 1 as canto api 404 or a DNS erroroptions.baseUrl; the default is built in createCantoProvider
2The token endpoint is https://oauth.canto.<domain>/oauth/api/oauth2/tokenNot in the report: a wrong one fails before any API calloptions.tokenUrl; the host stays pinned to the canto family either way
2aThe token EXCHANGE is an RFC 6749 form POST: grant_type=refresh_token with client_id/client_secret in the bodyNot in the report either. A different grant, or the secret wanted in a header, or an extra scope param, fails step 1 with oauth token refresh failed (400)getAccessToken in providers/oauth.ts, shared with every OAuth kind; a Canto-only quirk goes in its extraParams, not in the shared body
3Canto lists by scheme (image, video, audio, document, presentation, other) with limit/start pagingThe first line identifies the exact call; envelope: results: object[] (N) says how many came back. A scheme Canto does not have fails as an api error; a paging param it ignores shows as an N you did not ask forSCHEMES and PAGE_SIZE
4Album scoping is /album/<id>?limit&start; search is /search?keyword=Re-run step 2 with --options '{"tenant":"acme","albumId":"AB12C"}' and the report identifies the album path. Search is not exercised by previewlistPath for the album arm, searchAssets for the keyword param
5The record array rides results, then assets, dataThe record: line identifies the key that held it, or says no record array was found and lists `results\assets\data (LIST_ENVELOPE_KEYS)` under ABSENT with the real key in NOT MAPPEDLIST_ENVELOPE_KEYS
6The id is id, and ids stay inside [A-Za-z0-9._-]ABSENT id (RECORD_ID_KEYS) when the name is wrong. The charset is a value, so it never reaches the report: it shows in step 1 as N record(s) SKIPPEDRECORD_ID_KEYS; the charset lives in the REMOTE_ID regex
7Every record carries its own scheme, search hits and album records includedABSENT scheme (SCHEME_KEYS). Album records carrying no scheme are dropped rather than falling back on the walked one, so re-run step 1 with albumId set and watch SKIPPEDSCHEME_KEYS; the walked-scheme fallback is in toAsset
8The filename is name and the title is displayNameABSENT `name\displayName (FILENAME_KEYS / DISPLAY_NAME_KEYS)`, with the real names in NOT MAPPEDFILENAME_KEYS / DISPLAY_NAME_KEYS
9The byte size is sizeABSENT size (SIZE_KEYS). Whether the number is bytes is not in the report: compare it against what Canto shows for the file in step 3SIZE_KEYS
10The change stamp is lastModified, then lastUploaded, timeABSENT `lastModified\lastUploaded\time (UPDATED_AT_KEYS). lw providers drift` compares this field, so a wrong guess reads as "nothing ever changes upstream"UPDATED_AT_KEYS
11Approval rides approvalStatusABSENT approvalStatus (APPROVAL_STATE_KEYS)APPROVAL_STATE_KEYS
12Tags fold tag and keywordThese fold rather than fall back, so ABSENT `tag\keyword (TAG_KEYS)` appears only when neither is thereTAG_KEYS
13Sections fold album and folderSame folding rule as row 12: ABSENT `album\folder (SECTION_KEYS)` only when neither existsSECTION_KEYS
14The approved states are approved, against pending / restrictedNot in the report: these are values. Step 1 prints the note canto treated all N asset(s) on this page as not approved (live-verify: the approval-state VALUES …) when nothing matchedoptions.approvedStates; APPROVAL_STATE_KEYS only if the key is wrong too
15Custom fields ride additional, then customFieldsThe report descends one level and identifies the bag and the fields inside it: additional: { Expiry Date: string, … }. ABSENT `additional\customFields (CUSTOM_FIELD_BAG_KEYS)` when neither existsCUSTOM_FIELD_BAG_KEYS; the field name inside the bag is mapping.availabilityFields on the entry
16Canto exposes no native scheduled-expiry field, so availability is mapped by handThe record: block answers it: a native expiry or release date would show in NOT MAPPED. Report one if it is thereReport first. The pattern to copy is AVAILABLE_UNTIL_KEYS in intelligencebank.ts
17Original bytes stream from /api_binary/v1/<scheme>/<id>, and they are the true original for that scheme rather than a renditionNot in the report, which lists and never fetches: it prints that path as a note. Step 3 answers itBINARY_PATH
18Previews and downloads never ride a host outside the canto.com / .global / .de familyNot in the report. A CDN host outside it fails a blob fetch with canto url outside allowed hostsALLOWED_HOSTS: add the host explicitly, never as a wildcard
19Trashed records either stay out of listings or carry a positive markerLook for a deleted / trashed / status key in NOT MAPPED. Nothing in this driver drops trashed records todayAdd a DELETED_KEYS constant and the positive drop, copying DELETED_KEYS in imagerelay.ts
20About 3 requests/second stays under Canto's unpublished rate limitNot in the report. A 429 during the step 3 walk is the answeroptions.minGapMs (default DEFAULT_GAP_MS, 350ms)

Step 3: bytes

Only a blob fetch proves the export capability the exit depends on, so this step creates a real provider record.

lw providers add acme-canto --kind canto --label "Acme Canto" \
    --options '{"tenant":"acme","domain":"com","albumId":"AB12C"}' \
    --exposure '{"groups":["admin"]}'   # verification pass: admins only, not the whole org
lw providers credential acme-canto      # same blob as step 1, prompt hidden
lw providers enable acme-canto          # owner-only
lw providers sync acme-canto            # federation proof: read the count and any notes
lw providers materialize acme-canto --remote-id image:AB12C --json

--remote-id filters the walk rather than skipping it, so options.albumId is what keeps the pass short. Every per-asset failure prints under the summary as <remoteId>: <message>, and each message identifies the assumption that broke, the constant to edit and this page.

The fidelity check (true original, or a recompressed rendition?):

curl -sS -b "$(cat ~/.config/lolly-work/session)" \
    "$LW_BASE/catalog/ext/acme-canto/image:AB12C/download" -o /tmp/canto-lolly.bin
shasum -a 256 /tmp/canto-lolly.bin
file /tmp/canto-lolly.bin

Download the same asset from the Canto UI as the original and checksum that too. A match means api_binary served the true original for that scheme: strike row 17 for that scheme. A smaller file, different pixel dimensions or stripped EXIF/ICC means the path served a rendition, which is row 17 and not a materialize bug: materialize checksums whatever it is given. The path is scheme-scoped, so repeat this per scheme you plan to exit. A driver failure on this route surfaces as 502 PROVIDER_UNAVAILABLE; the diagnosis is in the materialize output.

Last, lw providers drift acme-canto. Every copy that could not be compared is counted under its own reason, so a zero cannot read as a clean bill of health: no readable change stamp means row 10 is wrong, and an unparsable one prints its shape (digits N, letters A) to report.

Then take the pass down. Materialized copies are instance-owned and survive the delete:

lw providers disable acme-canto
lw providers rm acme-canto

Step 4: sign off

In the driver (server/src/catalog/providers/canto.ts): strike the LIVE-VERIFY bullets this pass actually exercised and only those. An album arm you never ran and a scheme you never fetched stay on the list. Name the date and the tenant class (native Canto, or an Image Relay tenant the vendor migration carried across).

Where a guess was wrong, widen rather than replace: add the real key and keep the old ones. Then pin it with a fixture case in tests/provider-shape.test.ts, and one in tests/provider-live-verify.test.ts if the failure path changed.

In the guides: the Notes / limits caveat in canto.md, the fixture-verified paragraph in providers/README.md and the readiness row in offboarding.md all say this driver is fixture-verified only. All three should now say what was confirmed, on what date, and what remains open.

Send back: canto-shape.txt, the verbatim text of any driver error you hit, which constants you edited and to what, and the step 3 fidelity result per scheme.

See also: the Canto guide · off-boarding · the CLI · the other three runbooks: imagerelay · intelligencebank · acquia-dam.