Lolly
Uygulamayı Başlat

Image Relay live-verify runbook (kind: imagerelay)

Not: 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 imagerelay driver. Every endpoint path and field name in that driver comes from Image Relay's public v2 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. Canto owns the platform now, so check the fork matrix first and confirm this is the driver you exit through at all.

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 imagerelay

The credential prompt is hidden; paste the blob on one line. health ok - imagerelay followed by a mapped sample table means the token exchange and the first list call both worked, and that table is where you pick the file id step 2 needs. 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 param. Re-capturing the credential cannot helpRow 5a: getAccessToken in providers/oauth.ts
health FAILED: oauth token refresh failed (401)The credential: client 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 5: options.tokenUrl
health FAILED: imagerelay api 401 for /files?per_page=100&page=1The token minted and Image Relay refused the token. 403 means the app's scopes do not cover the files you asked forThe app's scopes, not the code
health FAILED: imagerelay api 404 for /files?per_page=100&page=1The REST base or the list path is wrongRows 1 and 2
health FAILED: imagerelay api 404 for /folders/… with a good folder idThe folder-scoped path is wrongRow 3

Step 2: capture the shape

lw providers preview --kind imagerelay --shape --remote-id 55 > imagerelay-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. --remote-id (any id from the step 1 table) adds a second report, on the call resolveBlob reads: it re-reads one file and follows the link in that response, so the wrapper and the download-link keys are guesses no list page can answer. That is rows 17 and 18. Still read-only: JSON about one file, never its bytes.

imagerelay  GET /api/v2/files?per_page=100&page=1
  envelope: files: object[] (100) · meta: { next_page: null }
  record: (100 under "files", keys unioned)
    id: number · filename: string · name: string · extension: string · size: number
    updated_at: string · keywords: string[] · deleted: boolean
    folder: { id: number, name: string } · custom_fields: { Expiry Date: string }
    quick_link: string
  MAPPED BY THIS DRIVER: files · meta · id · filename · name · extension · size · updated_at
    keywords · deleted · folder · custom_fields
  IN THE RESPONSE, NOT MAPPED: quick_link
  EXPECTED BY THIS DRIVER, ABSENT: (none)

imagerelay  GET /api/v2/files/55
  envelope: file: { id: number, filename: string, size: number, download_url: string, … }
  record: (the one record this call returned, wrapped in "file")
    id: number · filename: string · size: number · download_url: string · quick_link: string
  MAPPED BY THIS DRIVER: file · download_url · size
  IN THE RESPONSE, NOT MAPPED: id · filename · quick_link
  EXPECTED BY THIS DRIVER, ABSENT: (none)

The assumption table

One row per LIVE-VERIFY bullet in the driver header (server/src/catalog/providers/imagerelay.ts), with the field-names 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/imagerelay.ts
1The REST base is https://api.imagerelay.com/api/v2Not in the diff: the report's first line prints the path it called. A wrong base fails step 1 as imagerelay api 404 or a DNS erroroptions.baseUrl; the default is DEFAULT_BASE
2Listing is GET /files?per_page=100&page=<n>The first line identifies the exact call; envelope: files: object[] (N) says how many came back. A page param the API ignores shows as the same records on page 2listPath and PAGE_SIZE
3Folder scoping is GET /folders/<id>/files, with recursive=true for descendantsRe-run step 2 with --options '{"folderId":"1234","recursive":true}' and compare the counts. A folder id the API does not know fails as imagerelay api 404 for /folders/…listPath
4Search is GET /files?query=<text>Not exercised by preview: searchAssets is reachable only through catalog search once the provider is enabledsearchAssets
5The token endpoint is the base host's /oauth/tokenNot in the report: a wrong one fails before any API calloptions.tokenUrl; the default is derived from options.baseUrl
5aThe 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; an Image Relay quirk goes in its extraParams, not in the shared body
6The file array rides files, then dataThe record: line identifies the key that held it, or says no record array was found and lists `files\data (LIST_ENVELOPE_KEYS)` under ABSENT with the real key in NOT MAPPEDLIST_ENVELOPE_KEYS
7The next-page cursor is meta.next_pageenvelope: meta: { next_page: … } shows both the wrapper and the key. ABSENT meta (META_KEYS) means the envelope calls it something else; a meta with a differently named cursor inside stops paging after page 1, silentlyMETA_KEYS / NEXT_PAGE_KEYS
8The file id is id (string or number, both accepted)ABSENT id (RECORD_ID_KEYS). A file with no readable id does not federate silently: it throws, naming this constantRECORD_ID_KEYS
9The filename is filename and the title is nameABSENT `filename\name (FILENAME_KEYS / DISPLAY_NAME_KEYS)`, with the real names in NOT MAPPEDFILENAME_KEYS / DISPLAY_NAME_KEYS
10The format rides extension, then file_typeABSENT `extension\file_type (FORMAT_KEYS)`. Absent is recoverable: the driver falls back to the filename extensionFORMAT_KEYS
11The byte size is sizeABSENT size (SIZE_KEYS). Whether the number is bytes is not in the report: compare it against what Image Relay shows for the file in step 3SIZE_KEYS
12The change stamp is updated_atABSENT updated_at (UPDATED_AT_KEYS). lw providers drift compares this field, so a wrong guess reads as "nothing ever changes upstream"UPDATED_AT_KEYS
13Tags fold keywords and tagsThese fold rather than fall back, so ABSENT `keywords\tags (TAG_KEYS)` appears only when neither is thereTAG_KEYS
14Deletion is reported positively as deleteddeleted: boolean in the record: block confirms the deletion flag. ABSENT deleted (DELETED_KEYS) means deleted files are either absent from listings or flagged some other way, and a differently named flag means deleted files federateDELETED_KEYS (the driver also accepts the string "true")
15The folder is folder, and its name is folder.namefolder: { id: number, name: string } shows both levels. ABSENT folder (FOLDER_KEYS) means no section will ever be readFOLDER_KEYS / FOLDER_NAME_KEYS
16Custom metadata rides custom_fieldsThe report descends one level and identifies the bag and the fields inside it: custom_fields: { Expiry Date: string, … }. ABSENT custom_fields (CUSTOM_FIELD_BAG_KEYS) when it does not existCUSTOM_FIELD_BAG_KEYS; the field name inside the bag is mapping.availabilityFields on the entry
17The single-file call is GET /files/<id>, and it wraps the record in file (or data, or nothing)The detail report identifies the wrapper this tenant used, or says the record came back unwrapped. Left unfixed it fails in step 3 with imagerelay file has no download url in the response to GET /files/<id> (live-verify: …)DETAIL_WRAPPER_KEYS
18The signed link is download_url, and it serves the original bytesThe NAME is answered by the detail report: ABSENT download_url (DOWNLOAD_URL_KEYS) with a quick_link in NOT MAPPED means the driver has no link at all on this tenant. Whether that link serves the ORIGINAL is a value question no report can answer: step 3's checksum doesDOWNLOAD_URL_KEYS
19Every API, token, download and quick-link host is inside imagerelay.comNot in the report. A host outside it fails with imagerelay url outside allowed hosts - the guard covers the API base and token URL as well as the blob fetchALLOWED_HOSTS: add the host explicitly, never as a wildcard
20The API requires a User-Agent and caps at 5 requests/secondNot in the report. A 429 during the step 3 walk, or a 403 naming the user agent, is the answerUSER_AGENT and MIN_GAP_MS (this kind has no minGapMs option; the cap is fixed)

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-imagerelay --kind imagerelay --label "Acme Image Relay" \
    --options '{"folderId":"1234","recursive":true}' \
    --exposure '{"groups":["admin"]}'     # verification pass: admins only, not the whole org
lw providers credential acme-imagerelay   # same blob as step 1, prompt hidden
lw providers enable acme-imagerelay       # owner-only
lw providers sync acme-imagerelay         # federation proof: read the count and any notes
lw providers materialize acme-imagerelay --remote-id 55 --json

--remote-id filters the walk rather than skipping it, so options.folderId is what keeps the pass short and inside the 5 req/s cap. 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-imagerelay/55/download" -o /tmp/imagerelay-lolly.bin
shasum -a 256 /tmp/imagerelay-lolly.bin
file /tmp/imagerelay-lolly.bin

Download the same file from the Image Relay UI as the original and checksum that too. A match means download_url served the true original: strike row 18. A smaller file, different pixel dimensions or stripped EXIF/ICC means the link is a rendition or a quick link, which is row 18 and not a materialize bug: materialize checksums whatever it is given. Repeat per file type you plan to exit, since one type may come from a different link. A driver failure on this route surfaces as 502 PROVIDER_UNAVAILABLE; the diagnosis is in the materialize output.

Last, lw providers drift acme-imagerelay. 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 12 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-imagerelay
lw providers rm acme-imagerelay

Step 4: sign off

In the driver (server/src/catalog/providers/imagerelay.ts): strike the LIVE-VERIFY bullets this pass actually exercised and only those. A folder-scoped listing you never ran and a file type you never fetched stay on the list. Name the date, and say whether the tenant was still on the legacy platform or mid-migration to Canto.

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 fixture-verified paragraph in providers/README.md and the readiness row in offboarding.md are where the "live confirmation open" claim lives. Both should now say what was confirmed, on what date, and what remains open. No field name here carries over to the canto driver, a different API; the tenant knowledge does, and mapping.availabilityFields moves across with only the field name changed.

Send back: imagerelay-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 file type.

See also: the Image Relay guide · off-boarding · the CLI · the other three runbooks: canto · intelligencebank · acquia-dam.