diff --git a/dlcs-docs-client/p02_registering/post.py b/dlcs-docs-client/p02_registering/post.py index aaf72b5..8b2e981 100644 --- a/dlcs-docs-client/p02_registering/post.py +++ b/dlcs-docs-client/p02_registering/post.py @@ -4,7 +4,10 @@ from settings import docs_space_id, docs_space_name -# Not yet supported. Returns HTTP 405 Method Not Allowed +# INTENTIONAL error demo - this sample is *supposed* to fail. +# Direct POST of a single asset to a space is not supported; the platform +# returns HTTP 405 Method Not Allowed. Register assets with PUT, or POST a +# collection to the queue instead (see registering-assets). def post_asset(): space = docs_space_id ensure_space(space, docs_space_name) diff --git a/dlcs-docs-client/p06_space/space_images.py b/dlcs-docs-client/p06_space/space_images.py index 0f0116b..014c817 100644 --- a/dlcs-docs-client/p06_space/space_images.py +++ b/dlcs-docs-client/p06_space/space_images.py @@ -28,7 +28,10 @@ def get_images_with_query(): print() -# Not yet supported. Returns HTTP 405 Method Not Allowed +# INTENTIONAL error demo - this sample is *supposed* to fail. +# Direct POST of a single asset to a space is not supported; the platform +# returns HTTP 405 Method Not Allowed. Register assets with PUT, or POST a +# collection to the queue instead (see registering-assets). def post_asset(): ensure_space(docs_space_id, docs_space_name) asset = { diff --git a/dlcs-docs-client/p08_queue/post_to_priority_queue.py b/dlcs-docs-client/p08_queue/post_to_priority_queue.py index ff98805..a5eb0e1 100644 --- a/dlcs-docs-client/p08_queue/post_to_priority_queue.py +++ b/dlcs-docs-client/p08_queue/post_to_priority_queue.py @@ -1,9 +1,20 @@ import settings -from iiif_cs import post_resource, pprint +from iiif_cs import get_cloud_services_resource, post_resource, pprint from p06_space.ensure_space import ensure_space from settings import docs_space_id, docs_space_name +def get_priority_queue(): + """GET the priority queue - a CustomerQueue resource with its own counts.""" + path = f"/customers/{settings.IIIF_CS_CUSTOMER_ID}/queue/priority" + r = get_cloud_services_resource(path) + print("GET Priority Queue returned:") + queue = r.json() + pprint(queue) + print() + return queue + + def post_to_priority_queue(): """POST a collection of assets to the priority queue for faster processing.""" ensure_space(docs_space_id, docs_space_name) @@ -30,4 +41,5 @@ def post_to_priority_queue(): if __name__ == '__main__': + get_priority_queue() post_to_priority_queue() diff --git a/dlcs-docs-client/p12_origin_strategies/update_credentials.py b/dlcs-docs-client/p12_origin_strategies/update_credentials.py deleted file mode 100644 index 9d6a9d5..0000000 --- a/dlcs-docs-client/p12_origin_strategies/update_credentials.py +++ /dev/null @@ -1,57 +0,0 @@ -import settings -from iiif_cs import get_cloud_services_resource, post_resource, put_resource, delete_resource, pprint - - -def setup_origin_strategy(): - """Create a CustomerOriginStrategy to use in this example.""" - path = f"/customers/{settings.IIIF_CS_CUSTOMER_ID}/originStrategies" - origin_strategy = { - "strategy": "basic-http-authentication", - "regex": "https\\:\\/\\/credentials-example\\.com\\/.*", - "credentials": '{ \"user\": \"original-user\", \"password\": \"original-password\" }', - "optimised": False, - "order": 1 - } - r = post_resource(path, origin_strategy) - result = r.json() - print("Created CustomerOriginStrategy:") - pprint(result) - print() - return result - -# TODO: This is not implemented yet -def update_credentials(origin_strategy_url): - """PUT to the credentials sub-resource to update stored credentials. - The request body is an escaped JSON string, not a JSON object.""" - credentials_url = origin_strategy_url + "/credentials" - new_credentials = '{ "user": "updated-user", "password": "updated-password" }' - r = put_resource(credentials_url, new_credentials) - print("PUT credentials returned:") - print(f"HTTP Status Code: {r.status_code}") - print() - - -def delete_credentials(origin_strategy_url): - """DELETE the stored credentials from a CustomerOriginStrategy.""" - credentials_url = origin_strategy_url + "/credentials" - delete_resource(credentials_url) - - -if __name__ == '__main__': - origin_strategy = setup_origin_strategy() - origin_strategy_url = origin_strategy["@id"] - - # Update credentials - note: no GET is available, credentials always show as "xxx" - update_credentials(origin_strategy_url) - - # GET the strategy to confirm it still works (credentials still shown as "xxx") - r = get_cloud_services_resource(origin_strategy_url) - print("GET after credentials update:") - pprint(r.json()) - print() - - # DELETE the credentials - delete_credentials(origin_strategy_url) - - # Clean up - delete_resource(origin_strategy_url) diff --git a/scratch/api-doc/asset-queries.md b/scratch/api-doc/asset-queries.md index 961fee2..01c8274 100644 --- a/scratch/api-doc/asset-queries.md +++ b/scratch/api-doc/asset-queries.md @@ -9,7 +9,7 @@ These examples belong in the `### Query object` section once supported: ?q={"id":"PHOTO.2.22.36.2.tif"} ``` -# Ordering — ⟳ IMPLEMENTED, restore (verified 2026-08-03, DIS-01) — **restore-candidate** +# Ordering — ✅ PROMOTED 2026-08-06 (session 0, DIS-01 ruling): `### Ordering` section restored to asset-queries.mdx with default = `created`. Kept below for the DIS-06 nuance (no field whitelist; unknown field → handled Hydra 500 not 400) which is NOT yet documented — that's DIS-06's call. You can order by the value of a field of an asset: diff --git a/scratch/api-doc/queues.md b/scratch/api-doc/queues.md index d2ce031..9ffda4b 100644 --- a/scratch/api-doc/queues.md +++ b/scratch/api-doc/queues.md @@ -103,3 +103,13 @@ release-gated; fix them with the same wording when the adjunct queue ships. **Disposition: probably-drop** (superseded by code reality). Restore only if batch activation ever becomes start-of-processing rather than submission. + +## XC-12 ruling — batch upsert status semantics (for the adjunct-queue docs when released) + +Session 0 (2026-08-06) ruled: the status describes the AGGREGATE outcome of the +request, not itemised per-member results — 201 only when every member was newly +created (RFC 9110 permits 201 for "one or more new resources"), 200 when any member +updated existing state. A 201 therefore always means everything in the request is +new. When the adjunct-queue sections are promoted (PRO-08 release gate), state this +on the queue POST rows using the same wording as adjuncts.mdx "Registering multiple +adjuncts". diff --git a/scratch/api-doc/single-asset-manifest.md b/scratch/api-doc/single-asset-manifest.md index 2ef251b..8f01759 100644 --- a/scratch/api-doc/single-asset-manifest.md +++ b/scratch/api-doc/single-asset-manifest.md @@ -59,4 +59,23 @@ The behaviour for file-only assets (placeholder image, `placeholder` and `origin removed from copy: -In addition to the above, the single asset manifest has an extension property `dlcs:channelOutputs`: an array of content resources and services, one per delivery channel output, each including the channel it belongs to. This is more consistent in structure than the regular manifest properties and can be used for quick programmatic access to channel outputs. \ No newline at end of file +In addition to the above, the single asset manifest has an extension property `dlcs:channelOutputs`: an array of content resources and services, one per delivery channel output, each including the channel it belongs to. This is more consistent in structure than the regular manifest properties and can be used for quick programmatic access to channel outputs. +## Replaced prose preserved (DIS-25, session 0, 2026-08-06) + +Original iiif-av bullet (single-asset-manifest.mdx:27), replaced per the DIS-25 ruling: + +> For the `iiif-av` delivery channel, the body of the painting annotation is a `Choice` resource listing all the transcoded outputs. + +And the audio-example prose: + +> The painting body is a `Choice` wrapping the single transcoded output. + +**Why changed:** ManifestV3Builder.HandleTimebasedAsset uses a bare `Sound`/`Video` +body when `transcodes.Length == 1` and `PaintingChoice` only for several; an asset +with no transcode metadata gets no canvas at all. The old doc's own "(?)" question +is answered by the code in the opposite direction. The audio example's `Choice` +wrapper was removed and its annotation id corrected to `.../page/image` (the +builder uses `{canvasId}/page/image` for timebased assets too). + +**Disposition: probably-drop** (superseded by code reality). Restore only if +always-Choice is ever made the contract (the room chose not to). diff --git a/src/src/content/docs/api-doc/adjuncts.mdx b/src/src/content/docs/api-doc/adjuncts.mdx index fa20d96..7447ee3 100644 --- a/src/src/content/docs/api-doc/adjuncts.mdx +++ b/src/src/content/docs/api-doc/adjuncts.mdx @@ -265,6 +265,8 @@ POST https://api.dlcs.example/customers/2/spaces/5/images/b2921371x_0001.jp2/adj ] ``` +The response status code describes the aggregate outcome of the request — per-member results are not itemised. A multi-adjunct POST returns `201 Created` when every member was newly created, and `200 OK` when any member updated an existing adjunct; a `201` from this endpoint therefore always means everything in the request is new. (This follows [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.2), which defines `201` as a request that resulted in "one or more new resources" being created.) + If an adjunct needs to be access-controlled, it specifies [roles](../asset#roles) in the same way assets do, and the platform will emit IIIF Auth services when referencing the adjunct in a manifest.