From 4d87beb54016917f8e718bac8e18c2ca9806b497 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 31 Jan 2026 18:25:32 -0500 Subject: [PATCH] test: add Bruno API collections for sync features - Add device registration and management API tests - Add conflicts resolution API tests - Add queue management API tests - Add KOReader sync protocol tests - Add Kobo sync protocol tests --- bruno/conflicts/api.bru | 127 ++++++++ bruno/devices/Approve Device Registration.bru | 48 +++ bruno/devices/List Pending Registrations.bru | 47 +++ bruno/devices/Reject Device Registration.bru | 47 +++ bruno/devices/api.bru | 134 +++++++++ bruno/queue/Clear Device Queue.bru | 47 +++ bruno/queue/Delete Queue Item.bru | 46 +++ bruno/queue/Get Device Queue Stats.bru | 49 ++++ bruno/queue/List All Queue Items (Admin).bru | 48 +++ bruno/queue/List Device Queue Items.bru | 53 ++++ bruno/queue/Retry Queue Item.bru | 47 +++ bruno/queue/api.bru | 85 ++++++ bruno/sync-kobo/api.bru | 230 +++++++++++++++ bruno/sync-koreader/api.bru | 276 ++++++++++++++++++ 14 files changed, 1284 insertions(+) create mode 100644 bruno/conflicts/api.bru create mode 100644 bruno/devices/Approve Device Registration.bru create mode 100644 bruno/devices/List Pending Registrations.bru create mode 100644 bruno/devices/Reject Device Registration.bru create mode 100644 bruno/devices/api.bru create mode 100644 bruno/queue/Clear Device Queue.bru create mode 100644 bruno/queue/Delete Queue Item.bru create mode 100644 bruno/queue/Get Device Queue Stats.bru create mode 100644 bruno/queue/List All Queue Items (Admin).bru create mode 100644 bruno/queue/List Device Queue Items.bru create mode 100644 bruno/queue/Retry Queue Item.bru create mode 100644 bruno/queue/api.bru create mode 100644 bruno/sync-kobo/api.bru create mode 100644 bruno/sync-koreader/api.bru diff --git a/bruno/conflicts/api.bru b/bruno/conflicts/api.bru new file mode 100644 index 0000000..200ce4f --- /dev/null +++ b/bruno/conflicts/api.bru @@ -0,0 +1,127 @@ +meta { + name: "Bookmann Conflicts API" + type: "collection" + environment: { + development: { + base_url: "http://localhost:8765/api" + }, + production: { + base_url: "https://your-domain.com/api" + } + } +} + +# List Conflicts +@name("List Unresolved Conflicts") +GET {{environment.base_url}}/conflicts?status=unresolved +Authorization: Bearer {{jwt_token}} + +@name("List All Conflicts") +GET {{environment.base_url}}/conflicts?status=all +Authorization: Bearer {{jwt_token}} + +@name("List Resolved Conflicts") +GET {{environment.base_url}}/conflicts?status=resolved +Authorization: Bearer {{jwt_token}} + +@name("List Progress Conflicts") +GET {{environment.base_url}}/conflicts?status=unresolved&type=progress +Authorization: Bearer {{jwt_token}} + +@name("List Note Conflicts") +GET {{environment.base_url}}/conflicts?status=unresolved&type=note +Authorization: Bearer {{jwt_token}} + +@name("List Highlight Conflicts") +GET {{environment.base_url}}/conflicts?status=unresolved&type=highlight +Authorization: Bearer {{jwt_token}} + +# Get Conflict Details +@name("Get Conflict Details") +GET {{environment.base_url}}/conflicts/{{conflict_id}} +Authorization: Bearer {{jwt_token}} + +# Resolve Conflict +@name("Resolve - Keep KOReader Progress") +POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "winner": "koreader", + "apply_to_all_future_conflicts": false, + "reason": "KOReader was most recently updated" +} + +@name("Resolve - Keep Kobo Progress") +POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "winner": "kobo", + "apply_to_all_future_conflicts": false, + "reason": "Kobo has more progress" +} + +@name("Resolve - Keep Web Progress") +POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "winner": "web", + "apply_to_all_future_conflicts": false, + "reason": "Web interface has most recent data" +} + +@name("Resolve - Manual Override") +POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "winner": "manual", + "manual_data": { + "percentage": 0.43, + "epubcfi": "epubcfi(/6/4/2:20)", + "chapter": 3 + }, + "apply_to_all_future_conflicts": false, + "reason": "User specified custom progress" +} + +@name("Resolve - Manual Override with Page") +POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "winner": "manual", + "manual_data": { + "percentage": 0.43, + "page": 89, + "epubcfi": "epubcfi(/6/4/2:20)" + }, + "apply_to_all_future_conflicts": false, + "reason": "User specified page number" +} + +@name("Resolve - Apply to All Future Conflicts") +POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "winner": "koreader", + "apply_to_all_future_conflicts": true, + "reason": "Always prefer KOReader for future conflicts" +} + +# Delete Conflict +@name("Delete Conflict") +DELETE {{environment.base_url}}/conflicts/{{conflict_id}} +Authorization: Bearer {{jwt_token}} + +@name("Dismiss Conflict") +DELETE {{environment.base_url}}/conflicts/{{conflict_id}} +Authorization: Bearer {{jwt_token}} + +# Batch Operations +@name("Dismiss All Resolved Conflicts") +DELETE {{environment.base_url}}/conflicts/dismiss-resolved +Authorization: Bearer {{jwt_token}} diff --git a/bruno/devices/Approve Device Registration.bru b/bruno/devices/Approve Device Registration.bru new file mode 100644 index 0000000..b9941c5 --- /dev/null +++ b/bruno/devices/Approve Device Registration.bru @@ -0,0 +1,48 @@ +meta { + name: Approve Device Registration + type: http + seq: 6 +} + +get { + url: {{base_url}}/api/devices/approve/{{registration_id}} + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## Approve Device Registration + + Approves a pending device registration request. + + **Method:** GET + + **Endpoint:** /api/devices/approve/:registration_id + + **Authentication:** Bearer token + + **Path Parameters:** + - `registration_id` (string): Registration request UUID + + **Response:** + - Success message with approved device details + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Registration not found + - 400: Invalid registration status + + **Example Response:** + ```json + { + "message": "Device registration approved", + "device_id": "uuid", + "device_name": "My Kobo" + } + ``` +} diff --git a/bruno/devices/List Pending Registrations.bru b/bruno/devices/List Pending Registrations.bru new file mode 100644 index 0000000..3309249 --- /dev/null +++ b/bruno/devices/List Pending Registrations.bru @@ -0,0 +1,47 @@ +meta { + name: List Pending Device Registrations + type: http + seq: 5 +} + +get { + url: {{base_url}}/api/devices/pending + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## List Pending Device Registrations + + Retrieves all pending device registration requests awaiting approval. + + **Method:** GET + + **Endpoint:** /api/devices/pending + + **Authentication:** Bearer token + + **Response:** + - Array of pending device registrations + + **Status Codes:** + - 200: Success + - 401: Unauthorized + + **Example Response:** + ```json + [ + { + "id": "uuid", + "device_name": "My Kobo", + "device_type": "kobo", + "user_id": "uuid", + "created_at": "2024-01-01T00:00:00Z" + } + ] + ``` +} diff --git a/bruno/devices/Reject Device Registration.bru b/bruno/devices/Reject Device Registration.bru new file mode 100644 index 0000000..c66104a --- /dev/null +++ b/bruno/devices/Reject Device Registration.bru @@ -0,0 +1,47 @@ +meta { + name: Reject Device Registration + type: http + seq: 7 +} + +post { + url: {{base_url}}/api/devices/reject/{{registration_id}} + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## Reject Device Registration + + Rejects a pending device registration request. + + **Method:** POST + + **Endpoint:** /api/devices/reject/:registration_id + + **Authentication:** Bearer token + + **Path Parameters:** + - `registration_id` (string): Registration request UUID + + **Response:** + - Success message confirming rejection + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Registration not found + - 400: Invalid registration status + + **Example Response:** + ```json + { + "message": "Device registration rejected", + "registration_id": "uuid" + } + ``` +} diff --git a/bruno/devices/api.bru b/bruno/devices/api.bru new file mode 100644 index 0000000..031e8cc --- /dev/null +++ b/bruno/devices/api.bru @@ -0,0 +1,134 @@ +meta { + name: "Bookmann Device Management API" + type: "collection" + environment: { + development: { + base_url: "http://localhost:8765/api" + }, + production: { + base_url: "https://your-domain.com/api" + } + } +} + +# Register Device +@name("Register Device") +POST {{environment.base_url}}/devices/register +Content-Type: application/json +{ + "device_name": "My Kobo Clara", + "device_type": "kobo", + "device_identifier": "N1234567890123" +} + +@name("Register KOReader Device") +POST {{environment.base_url}}/devices/register +Content-Type: application/json +{ + "device_name": "My Kindle Paperwhite", + "device_type": "koreader", + "device_identifier": "G090GP123456789" +} + +@name("Register Web Device") +POST {{environment.base_url}}/devices/register +Content-Type: application/json +{ + "device_name": "Chrome Browser", + "device_type": "web", + "device_identifier": "web-client-abc123" +} + +# Check Registration Status +@name("Check Pending Registration") +POST {{environment.base_url}}/devices/register/status +Content-Type: application/json +{ + "registration_id": "registration-uuid-here" +} + +@name("Check Approved Registration") +POST {{environment.base_url}}/devices/register/status +Content-Type: application/json +{ + "registration_id": "registration-uuid-here" +} + +# List Devices (requires authentication) +@name("List User Devices") +GET {{environment.base_url}}/devices +Authorization: Bearer {{jwt_token}} + +@name("Get Device Details") +GET {{environment.base_url}}/devices/{{device_id}} +Authorization: Bearer {{jwt_token}} + +# Update Device Settings +@name("Update Device Settings") +PUT {{environment.base_url}}/devices/{{device_id}} +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "device_name": "Updated Device Name", + "sync_enabled": true, + "auto_sync": true, + "sync_frequency_minutes": 10 +} + +@name("Disable Device Sync") +PUT {{environment.base_url}}/devices/{{device_id}} +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "device_name": "My Kobo Clara", + "sync_enabled": false, + "auto_sync": false, + "sync_frequency_minutes": 30 +} + +@name("Update Sync Frequency") +PUT {{environment.base_url}}/devices/{{device_id}} +Authorization: Bearer {{jwt_token}} +Content-Type: application/json +{ + "device_name": "My Kobo Clara", + "sync_enabled": true, + "auto_sync": true, + "sync_frequency_minutes": 15 +} + +# Delete/Revoke Device +@name("Delete Device") +DELETE {{environment.base_url}}/devices/{{device_id}} +Authorization: Bearer {{jwt_token}} + +# Get Pending Registrations +@name("Get Pending Registrations") +GET {{environment.base_url}}/devices/pending +Authorization: Bearer {{jwt_token}} + +# Approve Device Registration +@name("Approve Device Registration") +GET {{environment.base_url}}/devices/approve/{{registration_id}} +Authorization: Bearer {{jwt_token}} + +@name("Approve Registration - KOReader") +GET {{environment.base_url}}/devices/approve/reg-uuid-123 +Authorization: Bearer {{jwt_token}} + +@name("Approve Registration - Kobo") +GET {{environment.base_url}}/devices/approve/reg-uuid-456 +Authorization: Bearer {{jwt_token}} + +# Reject Device Registration +@name("Reject Device Registration") +POST {{environment.base_url}}/devices/reject/{{registration_id}} +Authorization: Bearer {{jwt_token}} + +@name("Reject Registration - KOReader") +POST {{environment.base_url}}/devices/reject/reg-uuid-123 +Authorization: Bearer {{jwt_token}} + +@name("Reject Registration - Kobo") +POST {{environment.base_url}}/devices/reject/reg-uuid-456 +Authorization: Bearer {{jwt_token}} diff --git a/bruno/queue/Clear Device Queue.bru b/bruno/queue/Clear Device Queue.bru new file mode 100644 index 0000000..df1f91d --- /dev/null +++ b/bruno/queue/Clear Device Queue.bru @@ -0,0 +1,47 @@ +meta { + name: Clear Device Queue + type: http + seq: 6 +} + +delete { + url: {{base_url}}/api/queue/devices/{{device_id}}/clear + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## Clear Device Queue + + Clears all queue items for a specific device. + + **Method:** DELETE + + **Endpoint:** /api/queue/devices/:device_id/clear + + **Authentication:** Bearer token + + **Path Parameters:** + - `device_id` (string): Device UUID + + **Response:** + - Success message with count of cleared items + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Device not found + + **Example Response:** + ```json + { + "message": "Queue cleared", + "device_id": "uuid", + "cleared_count": 10 + } + ``` +} diff --git a/bruno/queue/Delete Queue Item.bru b/bruno/queue/Delete Queue Item.bru new file mode 100644 index 0000000..5af7ae7 --- /dev/null +++ b/bruno/queue/Delete Queue Item.bru @@ -0,0 +1,46 @@ +meta { + name: Delete Queue Item + type: http + seq: 5 +} + +delete { + url: {{base_url}}/api/queue/items/{{item_id}} + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## Delete Queue Item + + Deletes a queue item from the sync queue. + + **Method:** DELETE + + **Endpoint:** /api/queue/items/:item_id + + **Authentication:** Bearer token + + **Path Parameters:** + - `item_id` (string): Queue item UUID + + **Response:** + - Success message confirming deletion + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Queue item not found + + **Example Response:** + ```json + { + "message": "Queue item deleted", + "item_id": "uuid" + } + ``` +} diff --git a/bruno/queue/Get Device Queue Stats.bru b/bruno/queue/Get Device Queue Stats.bru new file mode 100644 index 0000000..1b561fe --- /dev/null +++ b/bruno/queue/Get Device Queue Stats.bru @@ -0,0 +1,49 @@ +meta { + name: Get Device Queue Stats + type: http + seq: 2 +} + +get { + url: {{base_url}}/api/queue/devices/{{device_id}}/stats + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## Get Device Queue Stats + + Retrieves statistics for a specific device's sync queue. + + **Method:** GET + + **Endpoint:** /api/queue/devices/:device_id/stats + + **Authentication:** Bearer token + + **Path Parameters:** + - `device_id` (string): Device UUID + + **Response:** + - Queue statistics including pending, completed, and failed counts + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Device not found + + **Example Response:** + ```json + { + "device_id": "uuid", + "total_items": 10, + "pending": 3, + "completed": 5, + "failed": 2 + } + ``` +} diff --git a/bruno/queue/List All Queue Items (Admin).bru b/bruno/queue/List All Queue Items (Admin).bru new file mode 100644 index 0000000..c5f6ad3 --- /dev/null +++ b/bruno/queue/List All Queue Items (Admin).bru @@ -0,0 +1,48 @@ +meta { + name: List All Queue Items (Admin) + type: http + seq: 1 +} + +get { + url: {{base_url}}/api/queue/items + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## List All Queue Items (Admin) + + Retrieves all queue items across all devices (admin only). + + **Method:** GET + + **Endpoint:** /api/queue/items + + **Authentication:** Bearer token (admin role required) + + **Response:** + - Array of queue items with device and status information + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 403: Forbidden - admin role required + + **Example Response:** + ```json + [ + { + "id": "uuid", + "device_id": "uuid", + "item_type": "progress", + "status": "pending", + "created_at": "2024-01-01T00:00:00Z" + } + ] + ``` +} diff --git a/bruno/queue/List Device Queue Items.bru b/bruno/queue/List Device Queue Items.bru new file mode 100644 index 0000000..e7012bf --- /dev/null +++ b/bruno/queue/List Device Queue Items.bru @@ -0,0 +1,53 @@ +meta { + name: List Device Queue Items + type: http + seq: 3 +} + +get { + url: {{base_url}}/api/queue/devices/{{device_id}}/items + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## List Device Queue Items + + Retrieves all queue items for a specific device. + + **Method:** GET + + **Endpoint:** /api/queue/devices/:device_id/items + + **Authentication:** Bearer token + + **Path Parameters:** + - `device_id` (string): Device UUID + + **Response:** + - Array of queue items for the specified device + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Device not found + + **Example Response:** + ```json + [ + { + "id": "uuid", + "device_id": "uuid", + "item_type": "progress", + "status": "pending", + "data": {}, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } + ] + ``` +} diff --git a/bruno/queue/Retry Queue Item.bru b/bruno/queue/Retry Queue Item.bru new file mode 100644 index 0000000..a6831bc --- /dev/null +++ b/bruno/queue/Retry Queue Item.bru @@ -0,0 +1,47 @@ +meta { + name: Retry Queue Item + type: http + seq: 4 +} + +post { + url: {{base_url}}/api/queue/items/{{item_id}}/retry + body: none + auth: bearer +} + +headers: { + Authorization: Bearer {{token}} +} + +docs { + ## Retry Queue Item + + Retries a failed queue item. + + **Method:** POST + + **Endpoint:** /api/queue/items/:item_id/retry + + **Authentication:** Bearer token + + **Path Parameters:** + - `item_id` (string): Queue item UUID + + **Response:** + - Success message indicating retry initiated + + **Status Codes:** + - 200: Success + - 401: Unauthorized + - 404: Queue item not found + - 400: Invalid item status + + **Example Response:** + ```json + { + "message": "Queue item retry initiated", + "item_id": "uuid" + } + ``` +} diff --git a/bruno/queue/api.bru b/bruno/queue/api.bru new file mode 100644 index 0000000..41b91b4 --- /dev/null +++ b/bruno/queue/api.bru @@ -0,0 +1,85 @@ +meta { + name: "Bookmann Sync Queue API" + type: "collection" + environment: { + development: { + base_url: "http://localhost:8765/api" + }, + production: { + base_url: "https://your-domain.com/api" + } + } +} + +# List Queue Items +@name("List All Queue Items") +GET {{environment.base_url}}/queue/items +Authorization: Bearer {{jwt_token}} + +@name("List Queue Items - With Pagination") +GET {{environment.base_url}}/queue/items?limit=50&offset=0 +Authorization: Bearer {{jwt_token}} + +@name("Filter by Status - Pending") +GET {{environment.base_url}}/queue/items?status=pending +Authorization: Bearer {{jwt_token}} + +@name("Filter by Status - Failed") +GET {{environment.base_url}}/queue/items?status=failed +Authorization: Bearer {{jwt_token}} + +@name("Filter by Status - Completed") +GET {{environment.base_url}}/queue/items?status=completed +Authorization: Bearer {{jwt_token}} + +@name("Filter by Type - Progress") +GET {{environment.base_url}}/queue/items?status=all&type=progress +Authorization: Bearer {{jwt_token}} + +@name("Filter by Type - Notes") +GET {{environment.base_url}}/queue/items?status=all&type=note +Authorization: Bearer {{jwt_token}} + +@name("Filter by Type - Highlights") +GET {{environment.base_url}}/queue/items?status=all&type=highlight +Authorization: Bearer {{jwt_token}} + +@name("Filter by Type - Bookmarks") +GET {{environment.base_url}}/queue/items?status=all&type=bookmark +Authorization: Bearer {{jwt_token}} + +# Process Queue Item +@name("Process Queue Item") +POST {{environment.base_url}}/queue/items/{{queue_item_id}}/process +Authorization: Bearer {{jwt_token}} + +@name("Retry Queue Item") +POST {{environment.base_url}}/queue/items/{{queue_item_id}}/retry +Authorization: Bearer {{jwt_token}} + +# Delete Queue Item +@name("Delete Queue Item") +DELETE {{environment.base_url}}/queue/items/{{queue_item_id}} +Authorization: Bearer {{jwt_token}} + +# Clear All Queue +@name("Clear All Queue Items") +DELETE {{environment.base_url}}/queue/clear +Authorization: Bearer {{jwt_token}} + +@name("Clear Failed Items") +DELETE {{environment.base_url}}/queue/clear-failed +Authorization: Bearer {{jwt_token}} + +# Get Queue Stats +@name("Get Queue Statistics") +GET {{environment.base_url}}/queue/stats +Authorization: Bearer {{jwt_token}} + +@name("Get Device Queue Stats") +GET {{environment.base_url}}/queue/devices/{{device_id}}/stats +Authorization: Bearer {{jwt_token}} + +@name("Get Device Queue Items") +GET {{environment.base_url}}/queue/devices/{{device_id}}/items +Authorization: Bearer {{jwt_token}} diff --git a/bruno/sync-kobo/api.bru b/bruno/sync-kobo/api.bru new file mode 100644 index 0000000..5f6622b --- /dev/null +++ b/bruno/sync-kobo/api.bru @@ -0,0 +1,230 @@ +meta { + name: "Bookmann Kobo Sync API" + type: "collection" + environment: { + development: { + base_url: "http://localhost:8765/api" + }, + production: { + base_url: "https://your-domain.com/api" + } + } +} + +# Authentication Headers +@name("Kobo Device Token") +@AuthorizationHeader({ + name: "Authorization", + value: "Bearer {{kobo_device_token}}" +}) +{ + "token": "{{kobo_device_token}}" +} + +@name("Kobo Device Info Header") +@Request({ + name: "x-kobo-device", + value: '{"DeviceId":"{{kobo_device_id}}","Model":"Kobo Clara","SerialNumber":"{{kobo_serial}}"}' +}) +{ + "test": "data" +} + +# Sync Markup (Reading Progress + Annotations) +@name("Sync Reading Progress") +POST {{environment.base_url}}/sync/kobo/markup +Authorization: Bearer {{kobo_device_token}} +x-kobo-device: {"DeviceId":"{{kobo_device_id}}","Model":"Kobo Clara"} +Content-Type: application/json +{ + "ReadingSync": [ + { + "ContentId": "book-uuid-here", + "PercentRead": 45.6, + "EntitlementId": "entitlement-id-here", + "RemainingTimeMinutes": 120, + "FirstReadTime": "2026-01-25T10:00:00Z", + "LastModified": "2026-01-30T20:00:00Z" + } + ], + "BookmarkSync": [] +} + +@name("Sync Progress with Bookmarks") +POST {{environment.base_url}}/sync/kobo/markup +Authorization: Bearer {{kobo_device_token}} +x-kobo-device: {"DeviceId":"{{kobo_device_id}}","Model":"Kobo Clara"} +Content-Type: application/json +{ + "ReadingSync": [ + { + "ContentId": "book-uuid", + "PercentRead": 42.3, + "EntitlementId": "entitlement-id", + "RemainingTimeMinutes": 138, + "FirstReadTime": "2026-01-25T10:00:00Z", + "LastModified": "2026-01-30T20:00:00Z" + } + ], + "BookmarkSync": [ + { + "ContentId": "book-uuid", + "BookmarkText": "highlighted text passage", + "BookmarkType": "annotation", + "BookmarkTitle": "Chapter 3" + } + ] +} + +@name("Sync Multiple Books Progress") +POST {{environment.base_url}}/sync/kobo/markup +Authorization: Bearer {{kobo_device_token}} +x-kobo-device: {"DeviceId":"{{kobo_device_id}}","Model":"Kobo Aura"} +Content-Type: application/json +{ + "ReadingSync": [ + { + "ContentId": "book-1-uuid", + "PercentRead": 25.0, + "EntitlementId": "entitlement-1", + "RemainingTimeMinutes": 240, + "FirstReadTime": "2026-01-25T10:00:00Z", + "LastModified": "2026-01-30T18:00:00Z" + }, + { + "ContentId": "book-2-uuid", + "PercentRead": 78.5, + "EntitlementId": "entitlement-2", + "RemainingTimeMinutes": 45, + "FirstReadTime": "2026-01-25T14:00:00Z", + "LastModified": "2026-01-30T20:00:00Z" + } + ], + "BookmarkSync": [] +} + +@name("Sync with Annotations") +POST {{environment.base_url}}/sync/kobo/markup +Authorization: Bearer {{kobo_device_token}} +x-kobo-device: {"DeviceId":"{{kobo_device_id}}","Model":"Kobo Libra"} +Content-Type: application/json +{ + "ReadingSync": [ + { + "ContentId": "book-uuid", + "PercentRead": 55.0, + "EntitlementId": "entitlement-id", + "RemainingTimeMinutes": 120, + "FirstReadTime": "2026-01-25T10:00:00Z", + "LastModified": "2026-01-30T20:00:00Z" + } + ], + "BookmarkSync": [ + { + "ContentId": "book-uuid", + "BookmarkText": "Important quote", + "BookmarkType": "annotation", + "BookmarkTitle": "Chapter 4 - The Truth" + }, + { + "ContentId": "book-uuid", + "BookmarkText": "Another quote", + "BookmarkType": "annotation", + "BookmarkTitle": "Chapter 5" + }, + { + "ContentId": "book-uuid", + "BookmarkText": "Note to myself", + "BookmarkType": "note", + "BookmarkTitle": "Personal note" + } + ] +} + +# Sync Bookmark +@name("Sync Single Bookmark") +POST {{environment.base_url}}/sync/kobo/bookmark +Authorization: Bearer {{kobo_device_token}} +x-kobo-device: {"DeviceId":"{{kobo_device_id}}","Model":"Kobo Clara"} +Content-Type: application/json +{ + "ContentId": "book-uuid", + "BookmarkText": "Bookmarked passage", + "BookmarkType": "annotation", + "BookmarkTitle": "Chapter 3" +} + +# Get Library +@name("Get Kobo Library") +GET {{environment.base_url}}/sync/kobo/library +Authorization: Bearer {{kobo_device_token}} + +@name("Get Library for Book") +GET {{environment.base_url}}/sync/kobo/library +Authorization: Bearer {{kobo_device_token}} +Content-Type: application/json +{ + "ContentId": "book-uuid" +} + +# Analytics Get Tests +@name("Get Analytics Tests") +POST {{environment.base_url}}/sync/kobo/v1/analytics/gettests +Authorization: Bearer {{kobo_device_token}} +Content-Type: application/json +{ + "platform": "android" + "firmware_version": "4.30.19023" +} + +# Initialization +@name("Kobo Initialization") +GET {{environment.base_url}}/sync/kobo/v1/initialization +Authorization: Bearer {{kobo_device_token}} + +@name("Initialization with Device Info") +GET {{environment.base_url}}/sync/kobo/v1/initialization?Platform=android&FirmwareVersion=4.30.19023 +Authorization: Bearer {{kobo_device_token}} + +# Sync from Server +@name("Sync Books from Server to Kobo") +POST {{environment.base_url}}/sync/kobo/sync-from-server +Authorization: Bearer {{kobo_device_token}} +Content-Type: application/json +{ + "force_sync": true, + "books": ["book-uuid-1", "book-uuid-2"] +} + +@name("Sync Specific Book from Server") +POST {{environment.base_url}}/sync/kobo/sync-from-server +Authorization: Bearer {{kobo_device_token}} +Content-Type: application/json +{ + "ContentId": "book-uuid", + "EntitlementId": "entitlement-id" +} + +@name("Partial Sync - Since Date") +POST {{environment.base_url}}/sync/kobo/sync-from-server +Authorization: Bearer {{kobo_device_token}} +Content-Type: application/json +{ + "books": ["book-1", "book-2"], + "sync_options": { + "since_date": "2026-01-30T00:00:00Z", + "include_annotations": true + } +} + +@name("Sync with Conflict Resolution Preference") +POST {{environment.base_url}}/sync/kobo/sync-from-server +Authorization: Bearer {{kobo_device_token}} +Content-Type: application/json +{ + "books": ["book-1", "book-2", "book-3"], + "sync_options": { + "conflict_resolution": "most_recent", + "force_annotations": false + } +} diff --git a/bruno/sync-koreader/api.bru b/bruno/sync-koreader/api.bru new file mode 100644 index 0000000..62c8c59 --- /dev/null +++ b/bruno/sync-koreader/api.bru @@ -0,0 +1,276 @@ +meta { + name: "Bookmann KOReader Sync API" + type: "collection" + environment: { + development: { + base_url: "http://localhost:8765/api" + }, + production: { + base_url: "https://your-domain.com/api" + } + } +} + +# Authentication Headers +@name("KOReader Device Token") +@AuthorizationHeader({ + name: "Authorization", + value: "Bearer {{koreader_device_token}}" +}) +{ + "token": "{{koreader_device_token}}" +} + +# Sync Progress +@name("Sync Progress - Single Book") +POST {{environment.base_url}}/sync/koreader/progress +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-uuid-here", + "title": "Book Title", + "authors": ["Author Name"], + "progress": 0.45, + "percentage": 0.45, + "last_read": "2026-01-30T20:00:00Z", + "chapter": 3, + "epubcfi": "epubcfi(/6/4/2:15)", + "character": 15432 + } + ] +} + +@name("Sync Progress - Multiple Books") +POST {{environment.base_url}}/sync/koreader/progress +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-1-uuid", + "title": "Book One", + "authors": ["Author One"], + "progress": 0.25, + "percentage": 0.25, + "last_read": "2026-01-30T19:00:00Z", + "chapter": 1, + "epubcfi": "epubcfi(/6/4/2:10)" + }, + { + "uuid": "book-2-uuid", + "title": "Book Two", + "authors": ["Author Two"], + "progress": 0.75, + "percentage": 0.75, + "last_read": "2026-01-30T20:00:00Z", + "chapter": 8, + "epubcfi": "epubcfi(/6/4/2:50)" + } + ] +} + +@name("Sync Progress - With Bookmarks") +POST {{environment.base_url}}/sync/koreader/progress +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-uuid-here", + "title": "Book Title", + "authors": ["Author Name"], + "progress": 0.45, + "percentage": 0.45, + "last_read": "2026-01-30T20:00:00Z", + "bookmarks": [ + { + "chapter": 3, + "datetime": "2026-01-30T19:55:00Z", + "notes": "highlighted text", + "pos0": "epubcfi(/6/4/2:15)", + "pos1": "epubcfi(/6/4/2:20)", + "page": 45, + "text": "highlighted text excerpt", + "type": "highlight" + } + ] + } + ] +} + +@name("Sync Progress - With Highlights and Notes") +POST {{environment.base_url}}/sync/koreader/progress +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-uuid-here", + "title": "Book Title", + "authors": ["Author Name"], + "progress": 0.60, + "percentage": 0.60, + "last_read": "2026-01-30T20:00:00Z", + "highlights": [ + { + "datetime": "2026-01-30T19:50:00Z", + "text": "Important passage", + "chapter": 4, + "pos0": "epubcfi(/6/4/2:20)", + "pos1": "epubcfi(/6/4/2:30)", + "page_start": 78, + "page_end": 79 + } + ], + "notes": [ + { + "datetime": "2026-01-30T19:52:00Z", + "text": "My note about this chapter", + "chapter": 4 + } + ] + } + ] +} + +# Get Metadata +@name("Get Book Metadata") +GET {{environment.base_url}}/sync/koreader/metadata/{{book_uuid}} +Authorization: Bearer {{koreader_device_token}} + +@name("Get Metadata for Multiple Books") +GET {{environment.base_url}}/sync/koreader/metadata +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "books": ["book-uuid-1", "book-uuid-2", "book-uuid-3"] +} + +# Sync Bookmarks +@name("Sync Bookmarks") +POST {{environment.base_url}}/sync/koreader/bookmarks +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-uuid", + "bookmarks": [ + { + "chapter": 3, + "datetime": "2026-01-30T19:55:00Z", + "notes": "Marked this chapter as important", + "pos0": "epubcfi(/6/4/2:15)", + "pos1": "epubcfi(/6/4/2:20)", + "page": 45, + "text": "Important passage", + "type": "bookmark" + } + ] + } + ] +} + +@name("Sync Highlights") +POST {{environment.base_url}}/sync/koreader/highlights +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-uuid", + "highlights": [ + { + "datetime": "2026-01-30T19:50:00Z", + "text": "Important quote from book", + "chapter": 4, + "pos0": "epubcfi(/6/4/2:20)", + "pos1": "epubcfi(/6/4/2:30)", + "page_start": 78, + "page_end": 79 + } + ] + } + ] +} + +@name("Sync Notes") +POST {{environment.base_url}}/sync/koreader/notes +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "library_id": "optional-library-uuid", + "books": [ + { + "uuid": "book-uuid", + "notes": [ + { + "datetime": "2026-01-30T19:52:00Z", + "text": "My personal note about this chapter", + "chapter": 4 + } + ] + } + ] +} + +# Get Library +@name("Get User Library for KOReader") +GET {{environment.base_url}}/sync/koreader/library +Authorization: Bearer {{koreader_device_token}} + +@name("Get Library for Specific Book") +GET {{environment.base_url}}/sync/koreader/library +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "books": ["book-uuid-1", "book-uuid-2"] +} + +# Immediate Sync Mode +@name("Immediate Sync - Page Turn") +POST {{environment.base_url}}/sync/koreader/progress +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "sync_mode": "immediate", + "books": [ + { + "uuid": "book-uuid", + "percentage": 0.45678, + "chapter": 3, + "timestamp": "2026-01-30T20:00:00Z" + } + ] +} + +# Checkpoint Sync Mode +@name("Checkpoint Sync") +POST {{environment.base_url}}/sync/koreader/progress +Authorization: Bearer {{koreader_device_token}} +Content-Type: application/json +{ + "sync_mode": "checkpoint", + "checkpoint_id": "checkpoint-uuid", + "since_timestamp": "2026-01-30T19:00:00Z", + "books": [ + { + "uuid": "book-uuid-1", + "percentage": 0.45, + "chapter": 3 + }, + { + "uuid": "book-uuid-2", + "percentage": 0.75, + "chapter": 8 + } + ] +}