# Testing the FotoMagico Document REST API

A step-by-step "cooking recipe" for manually verifying the data-model REST API from Terminal using
`curl`. See [README.md](README.md) for the architecture; this file is the hands-on test procedure.

> **Keep this in sync.** Whenever the API gains endpoints or changes a DTO, amend this recipe so it
> always exercises the current surface end-to-end.

## What you need to know (30-second primer)

- **`curl`** sends a web request and prints the reply.
- Verbs: **GET** = read, **POST** = create, **PATCH** = change, **PUT** = create-or-replace,
  **DELETE** = remove.
- **`-H`** adds a header (we use it for the token and to declare a JSON body); **`-d`** is the JSON
  body; **`-X`** picks the verb.
- Each reply ends with `--> HTTP NNN` — the status code. **2xx = success**, **401 = bad/missing
  token**, **403 = needs folder authorization**, **404 = not found**, **400 = bad body**,
  **409 = conflict**.
- App bundle id: `com.boinx.FotoMagico6.macOS`. Server root: `http://127.0.0.1:50000`.
- The data-model API is **path-versioned** under `/v1` (e.g. `http://127.0.0.1:50000/v1/documents`).

The editing API is **off by default** and gated by a bearer token; the playback remote is unaffected.

---

## Part A — One-time setup (enable the API + token)

```bash
defaults write com.boinx.FotoMagico6.macOS FMWebServer-editingAPIEnabled -bool YES
defaults write com.boinx.FotoMagico6.macOS FMWebServer-apiToken "FOTOMAGICO-TEST-TOKEN"
echo "enabled = $(defaults read com.boinx.FotoMagico6.macOS FMWebServer-editingAPIEnabled)"
echo "token   = $(defaults read com.boinx.FotoMagico6.macOS FMWebServer-apiToken)"
```

> If any API command returns `--> HTTP 404`, the app hasn't picked up the flag — quit FotoMagico
> (⌘Q), reopen it, and retry.

Set up Terminal shortcuts for this session (`B` = server root, `API` = versioned API base):

```bash
B="http://127.0.0.1:50000"
API="$B/v1"
AUTH="Authorization: Bearer FOTOMAGICO-TEST-TOKEN"
JSON="Content-Type: application/json"
```

Check the server is alive (no token needed — hits the remote page on the root, not `/v1`; expect `200`):

```bash
curl -s -o /dev/null -w "server says HTTP %{http_code}\n" "$B/remote-iphone.html"
```

---

## Part B — Prove the password gate works

Without the token (expect `401`):

```bash
curl -s -w $'\n--> HTTP %{http_code}\n' "$API/documents"
```

With the token (expect `200` and a JSON list — `[]` if no documents are open yet):

```bash
curl -s -H "$AUTH" -w $'\n--> HTTP %{http_code}\n' "$API/documents"
```

✅ `401` then `200` means the security gate works.

---

## Part C — Create a document and build a slideshow

**1. Create a document** — a new FotoMagico window opens (expect `HTTP 201`):

```bash
curl -s -X POST "$API/documents" -H "$AUTH" -H "$JSON" \
  -d '{"title":"My Test Slideshow","width":1920,"height":1080,"backgroundColor":"#102030FF"}' \
  -w $'\n--> HTTP %{http_code}\n'
```

We refer to it as **`current`** from here on (the API resolves `current` to the most recently
active/created document, even while FotoMagico is in the background).

**2. Read it back** (pretty-printed; shows settings + empty `slides`):

```bash
curl -s -H "$AUTH" "$API/documents/current" | json_pp
```

**3. Change document settings** (watch the title + canvas background change):

```bash
curl -s -X PATCH "$API/documents/current/settings" -H "$AUTH" -H "$JSON" \
  -d '{"title":"Vacation 2026","author":"Peter","backgroundColor":"#000000FF"}' \
  -w $'\n--> HTTP %{http_code}\n'
```

**4. Add two slides** (watch the storyboard fill in — each `HTTP 201`):

```bash
curl -s -X POST "$API/documents/current/slides" -H "$AUTH" -H "$JSON" \
  -d '{"duration":5,"animated":true}' -w $'\n--> HTTP %{http_code}\n'

curl -s -X POST "$API/documents/current/slides" -H "$AUTH" -H "$JSON" \
  -d '{"duration":8,"animated":true}' -w $'\n--> HTTP %{http_code}\n'
```

**5. List the slides** and note each `uuid`:

```bash
curl -s -H "$AUTH" "$API/documents/current/slides" | json_pp
```

**6. Pick a slide to edit** — copy the **first** slide's `uuid` from the output above and paste it
below (replace `PASTE_UUID_HERE`):

```bash
SLIDE="PASTE_UUID_HERE"
echo "editing slide $SLIDE"
```

**7. Edit that slide** (name, notes, duration):

```bash
curl -s -X PATCH "$API/documents/current/slides/$SLIDE" -H "$AUTH" -H "$JSON" \
  -d '{"name":"Opening shot","notes":"Welcome everyone","duration":10}' \
  -w $'\n--> HTTP %{http_code}\n'
```

**8. Change its transition** (`type` is a transition id: `0`=cut, `1`=fade, `2`=dissolve — full list
in [DOCUMENTATION.md](DOCUMENTATION.md)):

```bash
curl -s -X PATCH "$API/documents/current/slides/$SLIDE/transition" -H "$AUTH" -H "$JSON" \
  -d '{"type":2,"duration":2.0}' -w $'\n--> HTTP %{http_code}\n'
```

**9. Start a chapter** on that slide (watch the storyboard chapter marker):

```bash
curl -s -X PUT "$API/documents/current/slides/$SLIDE/chapter" -H "$AUTH" -H "$JSON" \
  -d '{"name":"Chapter One","color":"#FF8800FF"}' -w $'\n--> HTTP %{http_code}\n'
```

**9b. Tweak just one field of the existing chapter** (PATCH — merges; `404` if the slide has no
chapter, unlike PUT which would create one):

```bash
curl -s -X PATCH "$API/documents/current/slides/$SLIDE/chapter" -H "$AUTH" -H "$JSON" \
  -d '{"name":"Chapter One (renamed)"}' -w $'\n--> HTTP %{http_code}\n'
```

**10. Reorder the slides** — copy **both** slide uuids from step 5 and paste them (second one first,
to swap the order):

```bash
curl -s -X POST "$API/documents/current/slides/reorder" -H "$AUTH" -H "$JSON" \
  -d '{"order":["PASTE_SECOND_UUID","PASTE_FIRST_UUID"]}' \
  -w $'\n--> HTTP %{http_code}\n'
```

---

## Part D — Undo, then save

**11. Undo test.** Click the FotoMagico window and press **⌘Z** several times — each API change
reverses one step at a time (one command = one undo step). **⌘⇧Z** redoes them.

**12. Save (optional).** Sandbox note: the app can only write where you've granted access. Easiest:
press **⌘S** in FotoMagico once and pick a location, then save in place via the API:

```bash
curl -s -X POST "$API/documents/current/save" -H "$AUTH" -H "$JSON" \
  -d '{}' -w $'\n--> HTTP %{http_code}\n'
```

---

## Part E — Add a real image (optional; exercises the sandbox folder-grant)

**13. Authorize a folder.** Run this, then switch to FotoMagico — a folder chooser appears; pick a
folder containing images. (The command waits until you choose.)

```bash
curl -s -X POST "$API/media/authorize-folder" -H "$AUTH" -w $'\n--> HTTP %{http_code}\n'
```

**14. Turn an image into a `file://` URL.** ⚠️ Dragging a file into Terminal pastes the path with
**backslash-escaped spaces**. So assign it to a variable **without quotes** (the shell strips the
escapes), then build the URL from that clean variable:

```bash
# Type exactly "F=" then DRAG the image from Finder into the Terminal window (NO quotes), then Return:
F=/Volumes/YourDrive/Photos/example.jpg
echo "$F"     # must show clean spaces and NO backslashes

IMG=$(python3 -c "import sys,pathlib;print(pathlib.Path(sys.argv[1]).resolve().as_uri())" "$F")
echo "$IMG"   # should be file:///...%20... (spaces encoded as %20)
```

**15. Add the image as a layer** (watch it appear on the canvas):

```bash
curl -s -X POST "$API/documents/current/slides/$SLIDE/layers" -H "$AUTH" -H "$JSON" \
  -d "{\"kind\":\"image\",\"mediaURL\":\"$IMG\"}" -w $'\n--> HTTP %{http_code}\n'
```

> If you get **`403 authorization_required`**, the path is correct but that folder isn't covered by a
> grant — re-run step 13 and pick a parent folder of the image.

---

## Part E2 — Add a text layer (no media, no folder grant needed)

**16. Add a text layer** (expect `201`). Phase 1 text is a plain string rendered with FotoMagico's
default styling — font/size/color are not in the API yet:

```bash
curl -s -X POST "$API/documents/current/slides/$SLIDE/layers" -H "$AUTH" -H "$JSON" \
  -d '{"kind":"text","text":"Hello FotoMagico"}' -w $'\n--> HTTP %{http_code}\n'
```

**17. Verify the read-back** — the layer should come back with `"kind":"text"`, the `text` field set,
and `mediaURL` absent (`null`):

```bash
curl -s -H "$AUTH" "$API/documents/current/slides/$SLIDE/layers" | json_pp
```

**18. Change the text** (PATCH; paste the text layer's `uuid` from step 17):

```bash
LAYER="PASTE_TEXT_LAYER_UUID"
curl -s -X PATCH "$API/documents/current/slides/$SLIDE/layers/$LAYER" -H "$AUTH" -H "$JSON" \
  -d '{"text":"Updated caption"}' -w $'\n--> HTTP %{http_code}\n'
```

---

## Part E3 — Layer & slide effects

Effects are fixed slots (no create/delete) — `PATCH` to enable + configure, `{"enabled":false}` to
turn off. Same shape for layers and slides.

**19. Enable a grayscale effect on the text layer** (watch it desaturate; expect `200`):

```bash
curl -s -X PATCH "$API/documents/current/slides/$SLIDE/layers/$LAYER/effects/grayscale" \
  -H "$AUTH" -H "$JSON" -d '{"enabled":true,"amount":1.0}' -w $'\n--> HTTP %{http_code}\n'
```

**20. Add a white stroke around the layer:**

```bash
curl -s -X PATCH "$API/documents/current/slides/$SLIDE/layers/$LAYER/effects/stroke" \
  -H "$AUTH" -H "$JSON" -d '{"enabled":true,"width":12,"placement":"outside","color":"#FFFFFFFF"}' \
  -w $'\n--> HTTP %{http_code}\n'
```

**21. List all effects on the layer** (each shows `enabled` + its params):

```bash
curl -s -H "$AUTH" "$API/documents/current/slides/$SLIDE/layers/$LAYER/effects" | json_pp
```

**22. Apply a slide-wide sepia effect** (effects also live on the slide itself):

```bash
curl -s -X PATCH "$API/documents/current/slides/$SLIDE/effects/sepia" \
  -H "$AUTH" -H "$JSON" -d '{"enabled":true,"tone":0.6}' -w $'\n--> HTTP %{http_code}\n'
```

> See the full effect catalog (every `type` and its parameters) in [DOCUMENTATION.md](DOCUMENTATION.md).

---

## Part F — Export the slideshow to a movie

Export runs for minutes, so the API cannot do it in one request: you **start** a job and then **poll**
it. This replaces clicking through the Share assistant, which makes export testing scriptable.

**23. Pick a destination inside the app container** — always writable, no folder grant needed:

```bash
DEST="$HOME/Library/Containers/com.boinx.FotoMagico6.macOS/Data/Documents/api-export-test.mov"
echo "$DEST"
```

**24. Start the export** (expect `202`). Small and short on purpose — this is a smoke test, not a
deliverable:

```bash
curl -s -X POST "$API/documents/current/export" -H "$AUTH" -H "$JSON" \
  -d "{\"path\":\"$DEST\",\"render\":{\"width\":640,\"height\":360,\"framerate\":30},\"video\":{\"codec\":\"h264\"}}" \
  -w $'\n--> HTTP %{http_code}\n'
```

Switch to FotoMagico while it runs: after about a second a small progress window appears
("Exporting …", a bar, and a **⊗ Cancel** button). Clicking that button is exactly equivalent to the
`DELETE` in step 28 — the job comes back as `"canceled"` on the next poll. Add `"showProgress":false`
to the body to suppress the window.

**25. Note the job id** from the reply and watch it progress. Re-run this every few seconds — `step`
walks `prepare → renderAudio → renderVideo → move`, `progress` climbs during `renderVideo`, and `state`
ends up `succeeded`:

```bash
JOB="PASTE_JOB_ID_HERE"
curl -s -H "$AUTH" "$API/exports/$JOB" | json_pp
```

Or let the shell wait for it:

```bash
while :; do
  S=$(curl -s -H "$AUTH" "$API/exports/$JOB")
  echo "$S"
  echo "$S" | grep -q '"state" *: *"running"' || break
  sleep 3
done
```

**26. Check the file** — it should exist, have a non-zero size and play:

```bash
ls -l "$DEST"
afinfo "$DEST" | head -5      # audio track summary
open "$DEST"                  # plays in QuickTime
```

**27. Fast variant — export a single slide.** Perfect for regression checks, since it renders in
seconds. Use a slide uuid from step 5:

```bash
curl -s -X POST "$API/documents/current/export" -H "$AUTH" -H "$JSON" \
  -d "{\"path\":\"$DEST\",\"range\":{\"slideIds\":[\"$SLIDE\"]},\"render\":{\"width\":640,\"height\":360}}" \
  -w $'\n--> HTTP %{http_code}\n'
```

**28. Cancel a running export** (start one, then immediately):

```bash
curl -s -X DELETE "$API/exports/$JOB" -H "$AUTH" -w $'\n--> HTTP %{http_code}\n'
```

The reply shows `"state":"canceled"`. A finished job is left alone (cancelling it is a no-op).

**29. List all jobs of this app session** (the last 20 are kept):

```bash
curl -s -H "$AUTH" "$API/exports" | json_pp
```

> **Error cases worth trying:** a second `POST` while one export runs → `409` (only one at a time);
> a `path` in a folder FotoMagico may not write to (e.g. `/Users/Shared/x.mov`) → `403`
> `authorization_required`; `"codec":"bogus"` → `400`; a document with missing media files → `409`.
> Full endpoint and preset reference in [DOCUMENTATION.md](DOCUMENTATION.md).

---

## Part G — Cleanup (when done)

```bash
defaults write com.boinx.FotoMagico6.macOS FMWebServer-editingAPIEnabled -bool NO
```

---

## Endpoint reference

The full endpoint + DTO reference is in **[DOCUMENTATION.md](DOCUMENTATION.md)** (the canonical public
contract) — not duplicated here, to avoid drift. DTO field definitions live in `FotoMagico/API/DTO/`.
