fix(bruno): convert drifted body keys to body.data — CLI scenario runs now carry bodies
Follow-up to the sweep route audit, correcting its diagnosis: the CLI
DOES read .yml bodies — via OpenCollection's `body.data` string form
(the NewDevDBSetup script always worked because it used it). The
failing scenario POSTs used `body.json` (structured) / `body.jsonBody`
(string) — keys the CLI silently ignores, sending Content-Length: 0
with form-urlencoded content-type, so every server bind failed.
- 81 bodies converted to data-form (json maps serialized; jsonBody
strings kept verbatim, {{vars}} intact).
- 6 pre-existing TRUNCATED payloads repaired: the original generator
cut bodies at the first {{var}} (every SHA-256-identified scenario
plus Sync Progress/Sync Bookmarks shipped mid-JSON and 400'd from
day one). Rewritten whole, faithful to each file's documented
purpose; book_sha256/book_uuid added to the environment.
- environments: opds_base_url correction from the sweep retained.
Verified live against the rebuilt dev container: the full koreader
scenario tree runs green through `bru run` — 14/14 (202s across
immediate/checkpoint/SHA-256 progress, 200s bookmarks, highlights,
notes; SHA-256 book identification exercised against a real
file_sha256).
This commit is contained in:
1 parent
63a20dcc95
commit
09b6693505
83 files changed
+386
-2769
No files matched your search
@@ -3,18 +3,8 @@ docs: "## Checkpoint Sync\n\nIncremental sync using checkpoint-based change trac
|
||||
\ Repeat from step 3\n\n**Sync Optimization:**\n- Only books with progress changes\n- Skips unmodified books\n- Delta transfer\n- Compression for large payloads\n- Batch processing\n\n**Failure Handling:**\n- Checkpoint ID preserved on failure\n- Retry with same checkpoint\n- Full sync if checkpoint expired\n- Checkpoint validity: 24 hours\n- Auto-fallback to full sync\n\n**Performance:**\n- Small changes (1-10 books): 100-300ms\n- Medium changes (10-50 books): 300ms-1s\n- Large changes (50-100 books): 1-3s\n- Typical: 5-10x faster than full sync\n\n**When to Use:**\n- Default sync mode for most users\n- Periodic background sync\n- Large library management\n- Mobile/network-constrained environments\n- Battery-powered devices\n\n**Configuration:**\n- Checkpoint expiration: 24 hours\n- Max history: 100 checkpoints\n- Auto-cleanup of old checkpoints\n- Configurable sync interval"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- chapter: 3
|
||||
percentage: 0.45
|
||||
uuid: '{{media_item_id}}'
|
||||
- chapter: 8
|
||||
percentage: 0.75
|
||||
uuid: '{{media_item_id}}'
|
||||
checkpoint_id: checkpoint-uuid
|
||||
since_timestamp: '2026-01-30T19:00:00Z'
|
||||
sync_mode: checkpoint
|
||||
type: json
|
||||
data: "{\n \"books\": [\n {\n \"chapter\": 3,\n \"percentage\": 0.45,\n \"uuid\": \"{{media_item_id}}\"\n },\n {\n \"chapter\": 8,\n \"percentage\": 0.75,\n \"uuid\": \"{{media_item_id}}\"\n }\n ],\n \"checkpoint_id\": \"checkpoint-uuid\",\n \"since_timestamp\": \"2026-01-30T19:00:00Z\",\n \"sync_mode\": \"checkpoint\"\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -3,14 +3,8 @@ docs: "## Immediate Sync - Page Turn\n\nReal-time progress sync triggered immedi
|
||||
- Research and study\n- Testing sync functionality\n- When power source available\n\n**When NOT to Use:**\n- Battery conservation needed\n- Unstable network\n- Extended reading sessions\n- Background sync preferred"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- chapter: 3
|
||||
percentage: 0.45678
|
||||
timestamp: '2026-01-30T20:00:00Z'
|
||||
uuid: '{{media_item_id}}'
|
||||
sync_mode: immediate
|
||||
type: json
|
||||
data: "{\n \"books\": [\n {\n \"chapter\": 3,\n \"percentage\": 0.45678,\n \"timestamp\": \"2026-01-30T20:00:00Z\",\n \"uuid\": \"{{media_item_id}}\"\n }\n ],\n \"sync_mode\": \"immediate\"\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
docs: "## KOReader Sync Annotations - Per-Book SHA-256\n\n Synchronizes annotations (highlights) from KOReader with per-annotation SHA-256 hashes for multi-book sync.\n\n **Method:** POST\n\n **Endpoint:** /api/v1/koreader/sync/bookmarks\n\n **Authentication:** Bearer token (KOReader device token)\n\n **Request Body:**\n - `book_uuid` (string): Primary book UUID\n - `highlights` (array): Array of highlight objects\n - `text` (string): Highlighted text\n - `pos0`, `pos1` (string): EPUB CFI positions\n - `color` (string): Highlight color (hex)\n - `page` (number): Page number\n - `book_sha256` (string): SHA-256 hash for this specific book\n\n **Response:**\n - `highlights_synced` (number): Number of highlights synced\n\n **Status Codes:**\n - 200: Success\n - 401: Unauthorized\n - 500: Internal server error\n\n **Note:** Each highlight can include its own book_sha256, allowing annotations from multiple books in a single request."
|
||||
http:
|
||||
body:
|
||||
jsonBody: "{\n \"book_uuid\": \"{{book_uuid"
|
||||
type: json
|
||||
data: "{\n \"book_sha256\": \"{{book_sha256}}\",\n \"bookmarks\": [\n {\n \"chapter\": 3,\n \"datetime\": \"2026-01-30T19:55:00Z\",\n \"page\": 45,\n \"pos0\": \"p1\",\n \"text\": \"Per-book annotation\",\n \"type\": \"bookmark\"\n }\n ]\n}"
|
||||
headers: []
|
||||
method: POST
|
||||
url: '{{base_url}}/api/sync/koreader/bookmarks'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
docs: "## KOReader Sync Bookmarks - SHA-256\n\n Synchronizes bookmarks, notes, and highlights from KOReader using SHA-256 hash for book identification.\n\n **Method:** POST\n\n **Endpoint:** /api/v1/koreader/sync/bookmarks\n\n **Authentication:** Bearer token (KOReader device token)\n\n **Request Body:**\n - `book_sha256` (string): SHA-256 hash of book file\n - `bookmarks` (array): Array of bookmarks\n - `notes` (array): Array of notes\n - `highlights` (array): Array of highlights\n\n **Response:**\n - `sync_status` (string): Sync status\n - `total_synced` (number): Total items synced\n\n **Status Codes:**\n - 200: Success\n - 401: Unauthorized\n - 500: Internal server error"
|
||||
http:
|
||||
body:
|
||||
jsonBody: "{\n \"book_sha256\": \"{{book_sha256"
|
||||
type: json
|
||||
data: "{\n \"book_sha256\": \"{{book_sha256}}\",\n \"bookmarks\": [\n {\n \"chapter\": 3,\n \"datetime\": \"2026-01-30T19:55:00Z\",\n \"page\": 45,\n \"pos0\": \"p1\",\n \"text\": \"SHA-256 bookmark\",\n \"type\": \"bookmark\"\n }\n ]\n}"
|
||||
headers: []
|
||||
method: POST
|
||||
url: '{{base_url}}/api/sync/koreader/bookmarks'
|
||||
|
||||
@@ -3,20 +3,8 @@ docs: "## Sync KOReader Bookmarks\n\nSynchronizes bookmarks separately from prog
|
||||
\ bookmark\" menu\n- Shown in \"Bookmarks\" panel\n- Quick access via \"Go to bookmark\"\n- Exportable to JSON\n- Can be edited/deleted\n\n**Use Cases:**\n- Quick navigation aids\n- Chapter markers\n- Important passages\n- Reading progress points\n- Study session markers"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- bookmarks:
|
||||
- chapter: 3
|
||||
datetime: '2026-01-30T19:55:00Z'
|
||||
notes: Marked this chapter as important
|
||||
page: 45
|
||||
pos0: epubcfi(/6/4/2:15)
|
||||
pos1: epubcfi(/6/4/2:20)
|
||||
text: Important passage
|
||||
type: bookmark
|
||||
uuid: '{{media_item_id}}'
|
||||
library_id: optional-library-uuid
|
||||
type: json
|
||||
data: "{\n \"book_uuid\": \"{{media_item_id}}\",\n \"bookmarks\": [\n {\n \"chapter\": 3,\n \"datetime\": \"2026-01-30T19:55:00Z\",\n \"page\": 45,\n \"pos0\": \"epubcfi(/6/4/2:15)\",\n \"pos1\": \"epubcfi(/6/4/2:20)\",\n \"text\": \"Important passage\",\n \"type\": \"bookmark\"\n }\n ]\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -4,10 +4,7 @@ docs: "## Sync KOReader Highlights\n\nSynchronizes text highlights separately fr
|
||||
http:
|
||||
body:
|
||||
type: json
|
||||
jsonBody: "{\n \"book_uuid\": \"786b9c39-e8c3-47ab-a48a-c7f6cd769930\",\n \"highlights\": [\n {\n \"datetime\": \"2026-01-30T19:50:00Z\",\n \"text\": \"bruno sweep highlight\",\n \"chapter\": 1,\n \"page\": 1,\n \"pos0\": \"1\",\n \"pos1\": \"9\",\n \"color\": \"#ffff00\"\n }\n ]\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
data: "{\n \"book_uuid\": \"786b9c39-e8c3-47ab-a48a-c7f6cd769930\",\n \"highlights\": [\n {\n \"datetime\": \"2026-01-30T19:50:00Z\",\n \"text\": \"bruno sweep highlight\",\n \"chapter\": 1,\n \"page\": 1,\n \"pos0\": \"1\",\n \"pos1\": \"9\",\n \"color\": \"#ffff00\"\n }\n ]\n}"
|
||||
method: POST
|
||||
url: '{{base_url}}/api/sync/koreader/bookmarks'
|
||||
auth:
|
||||
|
||||
@@ -3,8 +3,8 @@ docs: "## Sync KOReader Notes\n\nSynchronizes user notes separately from highlig
|
||||
\ via \"Add note\" option\n- Edited in note editor\n- Shown in \"Notes\" panel\n- Can be organized by chapter\n- Export functionality available\n- Search support\n\n**Advanced Features:**\n- **Markdown**: Bold, italic, lists (some versions)\n- **Tags**: User-defined tags (via plugins)\n- **Links**: Internal/external links\n- **Formatting**: Rich text in newer versions\n\n**Use Cases:**\n- Study notes\n- Personal reflections\n- Academic annotations\n- Research insights\n- Book club discussion prep\n- Knowledge management"
|
||||
http:
|
||||
body:
|
||||
jsonBody: "{\n \"book_uuid\": \"{{media_item_id}}\",\n \"notes\": [\n {\n \"datetime\": \"2026-01-30T19:52:00Z\",\n \"notes\": \"A personal note about this chapter\",\n \"chapter\": 4,\n \"page\": 57,\n \"pos0\": \"3\"\n }\n ]\n}"
|
||||
type: json
|
||||
data: "{\n \"book_uuid\": \"{{media_item_id}}\",\n \"notes\": [\n {\n \"datetime\": \"2026-01-30T19:52:00Z\",\n \"notes\": \"A personal note about this chapter\",\n \"chapter\": 4,\n \"page\": 57,\n \"pos0\": \"3\"\n }\n ]\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
docs: "## KOReader Sync Progress - SHA-256 Only\n\n Synchronizes reading progress using only SHA-256 hash for book identification (when UUID is not available).\n\n **Method:** POST\n\n **Endpoint:** /api/v1/koreader/sync/progress\n\n **Authentication:** Bearer token (KOReader device token)\n\n **Request Body:**\n - `sync_mode` (string): Sync mode (immediate, deferred)\n - `books` (array): Array of book progress objects\n - `sha256` (string): SHA-256 hash of book file\n - `file_path` (string): Path to book file\n - `percentage` (number): Progress percentage\n - `page` (number): Current page\n - `total_pages` (number): Total pages\n\n **Response:**\n - `sync_status` (string): Sync status\n - `books_synced` (number): Number of books synced\n\n **Status Codes:**\n - 200: Success\n - 202: Accepted\n - 401: Unauthorized\n - 500: Internal server error\n\n **Note:** Use this when book UUID is not available, falling back to SHA-256 hash for identification."
|
||||
http:
|
||||
body:
|
||||
jsonBody: "{\n \"sync_mode\": \"immediate\",\n \"books\": [\n {\n \"sha256\": \"{{book_sha256"
|
||||
type: json
|
||||
data: "{\n \"sync_mode\": \"immediate\",\n \"books\": [\n {\n \"sha256\": \"{{book_sha256}}\",\n \"percentage\": 0.5\n }\n ]\n}"
|
||||
headers: []
|
||||
method: POST
|
||||
url: '{{base_url}}/api/sync/koreader/progress'
|
||||
|
||||
@@ -2,8 +2,8 @@ docs: "## KOReader Sync Progress - SHA-256\n\n Synchronizes reading progress fr
|
||||
\ (number): Number of books synced\n\n **Status Codes:**\n - 200: Success\n - 202: Accepted - processing\n - 401: Unauthorized\n - 500: Internal server error"
|
||||
http:
|
||||
body:
|
||||
jsonBody: "{\n \"sync_mode\": \"immediate\",\n \"books\": [\n {\n \"uuid\": \"{{book_uuid"
|
||||
type: json
|
||||
data: "{\n \"sync_mode\": \"immediate\",\n \"books\": [\n {\n \"sha256\": \"{{book_sha256}}\",\n \"percentage\": 0.5\n }\n ]\n}"
|
||||
headers: []
|
||||
method: POST
|
||||
url: '{{base_url}}/api/sync/koreader/progress'
|
||||
|
||||
@@ -2,28 +2,8 @@ docs: "## Sync KOReader Progress - Multiple Books\n\nSynchronizes reading progre
|
||||
\ Batch Behavior:**\n- Triggered on device wake from sleep\n- Occurs during \"Sync now\" operation\n- Runs on WiFi connection\n- Limited to 100 books per request\n- Automatic retry on failed books\n\n**Performance:**\n- Small batch (2-10 books): 200-500ms\n- Medium batch (10-50 books): 500ms-2s\n- Large batch (50-100 books): 2-5s\n- Timeout: 30 seconds\n\n**Use Cases:**\n- Device initialization sync\n- Periodic background sync\n- Post-offline catch-up sync\n- Library-wide progress update\n- Before firmware update"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- authors:
|
||||
- Author One
|
||||
chapter: 1
|
||||
epubcfi: epubcfi(/6/4/2:10)
|
||||
last_read: '2026-01-30T19:00:00Z'
|
||||
percentage: 0.25
|
||||
progress: 0.25
|
||||
title: Book One
|
||||
uuid: '{{media_item_id}}'
|
||||
- authors:
|
||||
- Author Two
|
||||
chapter: 8
|
||||
epubcfi: epubcfi(/6/4/2:50)
|
||||
last_read: '2026-01-30T20:00:00Z'
|
||||
percentage: 0.75
|
||||
progress: 0.75
|
||||
title: Book Two
|
||||
uuid: '{{media_item_id}}'
|
||||
library_id: optional-library-uuid
|
||||
type: json
|
||||
data: "{\n \"books\": [\n {\n \"authors\": [\n \"Author One\"\n ],\n \"chapter\": 1,\n \"epubcfi\": \"epubcfi(/6/4/2:10)\",\n \"last_read\": \"2026-01-30T19:00:00Z\",\n \"percentage\": 0.25,\n \"progress\": 0.25,\n \"title\": \"Book One\",\n \"uuid\": \"{{media_item_id}}\"\n },\n {\n \"authors\": [\n \"Author Two\"\n ],\n \"chapter\": 8,\n \"epubcfi\": \"epubcfi(/6/4/2:50)\",\n \"last_read\": \"2026-01-30T20:00:00Z\",\n \"percentage\": 0.75,\n \"progress\": 0.75,\n \"title\": \"Book Two\",\n \"uuid\": \"{{media_item_id}}\"\n }\n ],\n \"library_id\": \"optional-library-uuid\"\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -3,20 +3,8 @@ docs: "## Sync KOReader Progress - Single Book\n\nSynchronizes reading progress
|
||||
- Real-time page turn sync\n- Progress backup\n- Cross-device continuity\n- Reading time tracking\n- Chapter completion detection"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- authors:
|
||||
- Author Name
|
||||
chapter: 3
|
||||
character: 15432
|
||||
epubcfi: epubcfi(/6/4/2:15)
|
||||
last_read: '2026-01-30T20:00:00Z'
|
||||
percentage: 0.45
|
||||
progress: 0.45
|
||||
title: Book Title
|
||||
uuid: '{{media_item_id}}'
|
||||
library_id: optional-library-uuid
|
||||
type: json
|
||||
data: "{\n \"books\": [\n {\n \"authors\": [\n \"Author Name\"\n ],\n \"chapter\": 3,\n \"character\": 15432,\n \"epubcfi\": \"epubcfi(/6/4/2:15)\",\n \"last_read\": \"2026-01-30T20:00:00Z\",\n \"percentage\": 0.45,\n \"progress\": 0.45,\n \"title\": \"Book Title\",\n \"uuid\": \"{{media_item_id}}\"\n }\n ],\n \"library_id\": \"optional-library-uuid\"\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -3,26 +3,8 @@ docs: "## Sync Progress with Bookmarks\n\nSynchronizes reading progress with boo
|
||||
- Searchable by content\n\n**Use Cases:**\n- Study and research\n- Content review\n- Passage tracking\n- Quick navigation\n- Cross-device bookmark access"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- authors:
|
||||
- Author Name
|
||||
bookmarks:
|
||||
- chapter: 3
|
||||
datetime: '2026-01-30T19:55:00Z'
|
||||
notes: highlighted text
|
||||
page: 45
|
||||
pos0: epubcfi(/6/4/2:15)
|
||||
pos1: epubcfi(/6/4/2:20)
|
||||
text: highlighted text excerpt
|
||||
type: highlight
|
||||
last_read: '2026-01-30T20:00:00Z'
|
||||
percentage: 0.45
|
||||
progress: 0.45
|
||||
title: Book Title
|
||||
uuid: '{{media_item_id}}'
|
||||
library_id: optional-library-uuid
|
||||
type: json
|
||||
data: "{\n \"books\": [\n {\n \"authors\": [\n \"Author Name\"\n ],\n \"bookmarks\": [\n {\n \"chapter\": 3,\n \"datetime\": \"2026-01-30T19:55:00Z\",\n \"notes\": \"highlighted text\",\n \"page\": 45,\n \"pos0\": \"epubcfi(/6/4/2:15)\",\n \"pos1\": \"epubcfi(/6/4/2:20)\",\n \"text\": \"highlighted text excerpt\",\n \"type\": \"highlight\"\n }\n ],\n \"last_read\": \"2026-01-30T20:00:00Z\",\n \"percentage\": 0.45,\n \"progress\": 0.45,\n \"title\": \"Book Title\",\n \"uuid\": \"{{media_item_id}}\"\n }\n ],\n \"library_id\": \"optional-library-uuid\"\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -3,29 +3,8 @@ docs: "## Sync Progress with Highlights and Notes\n\nSynchronizes reading progre
|
||||
\ passages\n- Blue: Key concepts\n- Red: Critical information\n- Orange: Interesting quotes\n- Custom colors available\n\n**Sync Advantages:**\n- Separation allows granular control\n- Highlights sync without notes\n- Notes sync independently\n- Better conflict resolution\n- Efficient for large annotation sets\n\n**Use Cases:**\n- Academic research\n- Study groups\n- Content analysis\n- Personal knowledge management\n- Sharing insights"
|
||||
http:
|
||||
body:
|
||||
json:
|
||||
books:
|
||||
- authors:
|
||||
- Author Name
|
||||
highlights:
|
||||
- chapter: 4
|
||||
datetime: '2026-01-30T19:50:00Z'
|
||||
page_end: 79
|
||||
page_start: 78
|
||||
pos0: epubcfi(/6/4/2:20)
|
||||
pos1: epubcfi(/6/4/2:30)
|
||||
text: Important passage
|
||||
last_read: '2026-01-30T20:00:00Z'
|
||||
notes:
|
||||
- chapter: 4
|
||||
datetime: '2026-01-30T19:52:00Z'
|
||||
text: My note about this chapter
|
||||
percentage: 0.6
|
||||
progress: 0.6
|
||||
title: Book Title
|
||||
uuid: '{{media_item_id}}'
|
||||
library_id: optional-library-uuid
|
||||
type: json
|
||||
data: "{\n \"books\": [\n {\n \"authors\": [\n \"Author Name\"\n ],\n \"highlights\": [\n {\n \"chapter\": 4,\n \"datetime\": \"2026-01-30T19:50:00Z\",\n \"page_end\": 79,\n \"page_start\": 78,\n \"pos0\": \"epubcfi(/6/4/2:20)\",\n \"pos1\": \"epubcfi(/6/4/2:30)\",\n \"text\": \"Important passage\"\n }\n ],\n \"last_read\": \"2026-01-30T20:00:00Z\",\n \"notes\": [\n {\n \"chapter\": 4,\n \"datetime\": \"2026-01-30T19:52:00Z\",\n \"text\": \"My note about this chapter\"\n }\n ],\n \"percentage\": 0.6,\n \"progress\": 0.6,\n \"title\": \"Book Title\",\n \"uuid\": \"{{media_item_id}}\"\n }\n ],\n \"library_id\": \"optional-library-uuid\"\n}"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
|
||||
@@ -2,8 +2,8 @@ docs: "## KOReader Sync Progress\n\n Synchronizes reading progress from a KORea
|
||||
\ model identifier\n\n **Response:**\n - `sync_status` (string): Sync status (accepted, processing)\n - `books_synced` (number): Number of books synced\n\n **Status Codes:**\n - 202: Accepted - sync queued\n - 401: Unauthorized\n - 500: Internal server error"
|
||||
http:
|
||||
body:
|
||||
jsonBody: "{\n \"library_id\": null,\n \"books\": [\n {\n \"uuid\": \"{{book_uuid"
|
||||
type: json
|
||||
data: "{\n \"sync_mode\": \"immediate\",\n \"books\": [\n {\n \"uuid\": \"{{media_item_id}}\",\n \"percentage\": 0.45,\n \"chapter\": 3\n }\n ]\n}"
|
||||
headers: []
|
||||
method: POST
|
||||
url: '{{base_url}}/api/sync/koreader/progress'
|
||||
|
||||
Reference in new issue
Block a user