# Patch Image

Partially updates an existing image identified by its filename (`reference`), which is unique per tenant — substitute its file via a new `url` and/or replace its `tags`. At least one of these fields must be supplied.
The replacement `url` can point at a file with any name — the image is **always stored under its
current `reference`**, because that filename is how the rest of the platform points at the image and
a substitution cannot rename it. What the replacement *cannot* change is the image's **file type**,
or the image would end up served under a name that misdescribes it.
That type is decided by **the content of the file**, not by the extension in the `url`: the extension
is yours to write and any file can be published under any name, so it proves nothing. The extension
still has to be one `POST /images` accepts, but the bytes are what settle the type.

```
PATCH /images(product-123.jpg)
{ "url": "https://cdn.example.com/products/v2/product-123.jpg" }   OK   same name, JPEG content
{ "url": "https://cdn.example.com/uploads/a7f3c9e2-render.jpg" }   OK   any name, JPEG content
{ "url": "https://cdn.example.com/products/product-123.jpeg" }    OK   jpg and jpeg are one type
{ "url": "https://cdn.example.com/products/product-123.png" }     400  content is a PNG
{ "url": "https://cdn.example.com/products/product-123.jpg" }     400  named .jpg, but not a JPEG
```
### Changing an image's file type
Not possible through a substitution, because the filename — and so the extension — is fixed. Do it
client-side in three steps:
1. **Create the new image** — `POST /images` with the new file's `url`.
2. **Repoint the references** — use the Catalog API to find every entity (product, variant, category, …)
that uses the old filename and update it to the new one.
3. **Delete the old image** — `DELETE /images({old-reference})` once nothing references it any more.

## Fields
- **url** (optional): public URL of the new image. Triggers a full image substitution. May be called
anything, but must serve an image of the current file type — see above.
- **tags** (optional): array of strings, validated exactly as on `POST /images`.
  - Absent → tags are not modified, **including when `url` is supplied**.
  - `[]` → all tags are cleared.
  - `["a","b"]` → tags are replaced with these values.

## Valid combinations
| Body | Operation |
|  --- | --- |
| `{ "url": "https://.../{any-filename}" }` | Substitute image — **tags are kept** |
| `{ "url": "https://.../{any-filename}", "tags": ["a"] }` | Substitute image and set tags |
| `{ "tags": ["a"] }` | Update tags only, image file unchanged |

## Invalid combinations
- Empty body `{}` → 400.
- `url` serving content that is not an image of the current file type → 400 (see above).

## Response (204 No Content)
Success returns no body.
## Notes
- If the image is currently being processed, returns 409.
- **A substitution resets the image's `status` to `Up`** and re-enqueues it for processing, so the
thumbnails are regenerated. Until the worker finishes, `GET /images({reference})` reports `Up`
and the rendition URLs still point at the previous file. Poll `status` for `Ok` (or `Er`) before
treating the new file as live.
- A tags-only update does not touch `status` and takes effect immediately.
- Either way `modifiedOn` is refreshed.
- A substitution does not change the image's `reference` or `fileType`, so entities referencing it
keep working.

## Error Responses
- **400 Bad Request**: Invalid request body (e.g., both fields missing), a replacement whose content
is not an image of the current `fileType`, or any of the `url`/`tags` validation failures
documented on `POST /images` (format, accessibility, Content-Type, size, dimensions). Same
`{ "validationFailures": { "error": [...] } }` envelope
- **401 Unauthorized**: Missing or invalid API key. Returned by the API gateway as
`{ "message": "Unauthorized", "request_id": "d8aafa5b8f3e400b60bea0123dd33317" }`
- **403 Forbidden**: The API key does not have write permissions for this operation. Same body
shape as the 401
- **404 Not Found**: No image with the given reference exists for this tenant. The body is the
message string, e.g. `"Image with reference 'missing.jpg' not found"`
- **409 Conflict**: The image is currently being processed and cannot be modified —
`{ "error": "Image 'product-123.jpg' is currently being processed and cannot be modified" }`
- **500 Internal Server Error**: Technical error (network failure fetching the replacement file,
storage or database unavailable). The already-uploaded replacement object is cleaned up, so the
image is left on its previous file

Endpoint: PATCH /images({reference})
Version: 2.0.0

## Path parameters:

  - `reference` (string, required)
    Image filename (reference), unique per tenant (e.g. `product-main-front.jpg`)

## Header parameters:

  - `X-API-KEY` (string, required)
    Tenant's API key (required)

## Request fields (application/json):

  - `reference` (string)
    Taken from the URL path, not from the request body — any value sent in the body is ignored.

  - `url` (string)
    Publicly accessible URL of the replacement image, which triggers a full substitution. Optional,
but at least one of `url` or `tags` must be supplied. Validated exactly as on create.
The replacement may be called anything at its own address — it is stored under the image's
current Sl.Apps.Dam.Application.Images.PatchImage.PatchImageRequest.Reference — but it must be an image of the same file type.

  - `tags` (array)
    Tags to set on the image. Optional. Omit to leave the tags untouched, send `[]` to clear them,
or send values to replace them — including when the image itself is substituted via `url`,
which never touches the tags on its own.

## Response 401 fields (application/json):

  - `message` (string)
    Error description, e.g. `Unauthorized`.

  - `request_id` (string)
    Gateway request identifier, to quote when contacting support.

## Response 403 fields (application/json):

  - `message` (string)

  - `request_id` (string)

## Response 409 fields (application/json):

  - `error` (string)

## Response 500 fields (application/json):

  - `error` (string)

  - `details` (any)

