test(bruno): Add API tests for Kobo, Koreader, OPDS, collections
- Add Kobo bookmark/markup sync tests - Add Koreader progress/bookmark sync tests - Add OPDS feed tests - Add collection tests - Add sync tests for book linking
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
meta {
|
||||||
|
name: Add Books to Collection
|
||||||
|
type: http
|
||||||
|
seq: 6
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/collections/{{collection_id}}/books
|
||||||
|
body: json
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"book_ids": [
|
||||||
|
"{{book_id_1}}",
|
||||||
|
"{{book_id_2}}",
|
||||||
|
"{{book_id_3}}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
meta {
|
||||||
|
name: Create Collection
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/collections
|
||||||
|
body: json
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"name": "Science Fiction",
|
||||||
|
"description": "My favorite sci-fi books",
|
||||||
|
"color": "#ff0000",
|
||||||
|
"icon": "🚀",
|
||||||
|
"auto_assign_rules": [
|
||||||
|
{
|
||||||
|
"id": "rule-1",
|
||||||
|
"field": "genre",
|
||||||
|
"operator": "equals",
|
||||||
|
"value": "Science Fiction",
|
||||||
|
"priority": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"view_settings": {
|
||||||
|
"sort_by": "title",
|
||||||
|
"view_mode": "grid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
meta {
|
||||||
|
name: Create Device Mapping
|
||||||
|
type: http
|
||||||
|
seq: 9
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/devices/{{device_id}}/collections
|
||||||
|
body: json
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"collection_id": "{{collection_id}}",
|
||||||
|
"device_shelf_name": "Sci-Fi",
|
||||||
|
"sync_direction": "bidirectional"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Delete Collection
|
||||||
|
type: http
|
||||||
|
seq: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
url: {{base_url}}/api/collections/{{collection_id}}
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Delete Device Mapping
|
||||||
|
type: http
|
||||||
|
seq: 11
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
url: {{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Book Collections
|
||||||
|
type: http
|
||||||
|
seq: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/collections/books/{{book_id}}
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Collection
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/collections/{{collection_id}}
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Collections
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/collections?include_auto=true&sort_by=name
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Device Mappings
|
||||||
|
type: http
|
||||||
|
seq: 8
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/devices/{{device_id}}/collections
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
meta {
|
||||||
|
name: Remove Book from Collection
|
||||||
|
type: http
|
||||||
|
seq: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
url: {{base_url}}/api/collections/{{collection_id}}/books/{{book_id}}
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
meta {
|
||||||
|
name: Update Collection
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
url: {{base_url}}/api/collections/{{collection_id}}
|
||||||
|
body: json
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"name": "Sci-Fi Favorites",
|
||||||
|
"description": "Updated description",
|
||||||
|
"color": "#00ff00",
|
||||||
|
"icon": "⭐",
|
||||||
|
"auto_assign_rules": [
|
||||||
|
{
|
||||||
|
"id": "rule-2",
|
||||||
|
"field": "series",
|
||||||
|
"operator": "equals",
|
||||||
|
"value": "Foundation",
|
||||||
|
"priority": 9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"view_settings": {
|
||||||
|
"sort_by": "author",
|
||||||
|
"view_mode": "list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Update Device Mapping
|
||||||
|
type: http
|
||||||
|
seq: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
url: {{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}
|
||||||
|
body: json
|
||||||
|
auth: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{token}}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"device_shelf_name": "Science Fiction",
|
||||||
|
"sync_direction": "book_to_device"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
meta {
|
||||||
|
name: "Kobo Bookmark Sync - Enhanced with ContentId Mapping"
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/v1/kobo/bookmark
|
||||||
|
body: json({
|
||||||
|
"BookmarkSync": [
|
||||||
|
{
|
||||||
|
"BookmarkId": "bookmark_2",
|
||||||
|
"ContentId": "kobo_xyz789",
|
||||||
|
"BookmarkText": "Important note",
|
||||||
|
"BookmarkType": "bookmark",
|
||||||
|
"DateCreated": "2026-01-31T12:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.Status == "Success"
|
||||||
|
response.body.BookmarksSynced >= 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
meta {
|
||||||
|
name: "Kobo Initialization - Enhanced with ContentId Mapping"
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/v1/kobo/initialization
|
||||||
|
body: none
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.ContentId exists()
|
||||||
|
response.body.Categories exists()
|
||||||
|
response.body.BookmannUUID exists()
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
meta {
|
||||||
|
name: "Kobo Markup Sync - Enhanced with ContentId Mapping"
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/v1/kobo/markup
|
||||||
|
body: json({
|
||||||
|
"ReadingSync": [
|
||||||
|
{
|
||||||
|
"ContentId": "kobo_abc123def456",
|
||||||
|
"PercentRead": 60.0,
|
||||||
|
"RemainingTimeMin": 120,
|
||||||
|
"ReadingEvent": "BookRead",
|
||||||
|
"LastModified": "2026-01-31T12:00:00Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BookmarkSync": [
|
||||||
|
{
|
||||||
|
"BookmarkId": "bookmark_1",
|
||||||
|
"ContentId": "kobo_abc123def456",
|
||||||
|
"BookmarkText": "Great quote",
|
||||||
|
"BookmarkType": "annotation",
|
||||||
|
"DateCreated": "2026-01-31T12:00:00Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Metadata": true
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.Status == "Success" || response.body.Status == "Partial"
|
||||||
|
response.body.MarkupsSynced >= 0
|
||||||
|
response.body.BookmarksSynced >= 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
meta {
|
||||||
|
name: "KOReader Sync Annotations - Per-Annotation SHA-256"
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/v1/koreader/sync/bookmarks
|
||||||
|
body: json({
|
||||||
|
"book_uuid": "{{book_uuid}}",
|
||||||
|
"highlights": [
|
||||||
|
{
|
||||||
|
"text": "Quote from book 1",
|
||||||
|
"pos0": "/6/4[chap1ref]!/4/2/1:0",
|
||||||
|
"pos1": "/6/4[chap1ref]!/4/2/1:50",
|
||||||
|
"color": "#ffff00",
|
||||||
|
"page": 10,
|
||||||
|
"book_sha256": "{{book_sha256}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Quote from book 2 (different book)",
|
||||||
|
"pos0": "/6/4[chap1ref]!/4/2/1:0",
|
||||||
|
"pos1": "/6/4[chap1ref]!/4/2/1:50",
|
||||||
|
"color": "#00ff00",
|
||||||
|
"page": 15,
|
||||||
|
"book_sha256": "{{another_book_sha256}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{koreader_device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.highlights_synced >= 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
meta {
|
||||||
|
name: "KOReader Sync Bookmarks - Enhanced with SHA-256"
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/v1/koreader/sync/bookmarks
|
||||||
|
body: json({
|
||||||
|
"book_sha256": "{{book_sha256}}",
|
||||||
|
"bookmarks": [
|
||||||
|
{
|
||||||
|
"text": "Important passage about chapter 3",
|
||||||
|
"pos0": "/6/4[chap3ref]!/4/2/1:0",
|
||||||
|
"page": 45,
|
||||||
|
"type": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"notes": "My note about this section",
|
||||||
|
"pos0": "/6/4[chap3ref]!/4/2/1:100",
|
||||||
|
"page": 47,
|
||||||
|
"text": "Quoted text from the book"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"highlights": [
|
||||||
|
{
|
||||||
|
"text": "This is highlighted text",
|
||||||
|
"pos0": "/6/4[chap3ref]!/4/2/1:50",
|
||||||
|
"pos1": "/6/4[chap3ref]!/4/2/1:100",
|
||||||
|
"color": "#ffff00",
|
||||||
|
"page": 50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{koreader_device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.sync_status == "completed"
|
||||||
|
response.body.total_synced >= 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
meta {
|
||||||
|
name: "KOReader Sync Progress - Priority Matching (SHA-256 only)"
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/v1/koreader/sync/progress
|
||||||
|
body: json({
|
||||||
|
"sync_mode": "immediate",
|
||||||
|
"books": [
|
||||||
|
{
|
||||||
|
"sha256": "{{book_sha256}}",
|
||||||
|
"file_path": "/mnt/onboard/Unknown%20Book.epub",
|
||||||
|
"percentage": 0.45,
|
||||||
|
"page": 89,
|
||||||
|
"total_pages": 200
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{koreader_device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200 || response.status == 202
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
meta {
|
||||||
|
name: "KOReader Sync Progress - Enhanced with SHA-256"
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/v1/koreader/sync/progress
|
||||||
|
body: json({
|
||||||
|
"sync_mode": "immediate",
|
||||||
|
"books": [
|
||||||
|
{
|
||||||
|
"uuid": "{{book_uuid}}",
|
||||||
|
"sha256": "{{book_sha256}}",
|
||||||
|
"file_path": "/mnt/onboard/The%20Hobbit.epub",
|
||||||
|
"percentage": 0.65,
|
||||||
|
"chapter": 5,
|
||||||
|
"page": 142,
|
||||||
|
"total_pages": 310,
|
||||||
|
"epubcfi": "/6/4[chap1ref]!/4/2/1:0",
|
||||||
|
"last_read": "2026-01-31T12:00:00Z",
|
||||||
|
"title": "The Hobbit",
|
||||||
|
"authors": ["J.R.R. Tolkien"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{koreader_device_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200 || response.status == 202
|
||||||
|
response.body.sync_status exists()
|
||||||
|
response.body.books_synced >= 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Download Book (EPUB)
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/download/{{book_id}}
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, book_id, opds_base_url
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Download Book (KEPUB)
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/download/{{book_id}}?format=kepub
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, book_id, opds_base_url
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Cover Image
|
||||||
|
type: http
|
||||||
|
seq: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/cover/{{book_id}}
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, book_id, opds_base_url
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Device Catalog
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/catalog?page=1&per_page=50
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, opds_base_url
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Device Navigation
|
||||||
|
type: http
|
||||||
|
seq: 6
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/nav
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, opds_base_url
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: List Formats
|
||||||
|
type: http
|
||||||
|
seq: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/formats/{{book_id}}
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, book_id, opds_base_url
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Search Device Catalog
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{opds_base_url}}/devices/{{device_id}}/search?q=hobbit
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:device_id, opds_base_url
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
meta {
|
||||||
|
name: "Download Device Sidecar File"
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/devices/{{device_id}}/sidecar/download
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{user_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.headers["Content-Type"] contains "application/json"
|
||||||
|
response.headers["Content-Disposition"] contains ".bookmann.json"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
meta {
|
||||||
|
name: "Get Device Sidecar Config"
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/devices/{{device_id}}/sidecar
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{user_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.version == "1.0"
|
||||||
|
response.body.bookmann exists()
|
||||||
|
response.body.books exists()
|
||||||
|
response.body.collections exists()
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
meta {
|
||||||
|
name: "Get System Configuration"
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/system/config
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{admin_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.base_url exists()
|
||||||
|
response.body.opds_base_url exists()
|
||||||
|
response.body.api_base_url exists()
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
# Phase 8: Sidecar Configuration System - API Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This document describes the sidecar configuration system that enables easy device setup for Kobo and KOReader devices.
|
||||||
|
|
||||||
|
## What is a Sidecar File?
|
||||||
|
|
||||||
|
A sidecar file (`.bookmann.json`) is a configuration file that contains all the information a device needs to connect to Bookmann, including:
|
||||||
|
- OPDS catalog URL for wireless book browsing
|
||||||
|
- Sync API endpoints for progress sync
|
||||||
|
- Book inventory with SHA-256 hashes
|
||||||
|
- Collection metadata with shelf mappings
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
All endpoints require authentication:
|
||||||
|
- User endpoints: Use user JWT token (Authorization: Bearer {{user_token}})
|
||||||
|
- Admin endpoints: Use admin JWT token (Authorization: Bearer {{admin_token}})
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
### 1. Get Device Sidecar Config
|
||||||
|
**Endpoint**: `GET /api/devices/:device_id/sidecar`
|
||||||
|
|
||||||
|
**Description**: Returns sidecar configuration for a device as JSON.
|
||||||
|
|
||||||
|
**Request Headers**:
|
||||||
|
```
|
||||||
|
Authorization: Bearer {{user_token}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response** (200 OK):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"bookmann": {
|
||||||
|
"opds_catalog": "http://192.168.1.100:8765/opds/devices/kobo-id/catalog",
|
||||||
|
"sync_api": "http://192.168.1.100:8765/api/sync/kobo",
|
||||||
|
"opds_base_url": "http://192.168.1.100:8765/opds",
|
||||||
|
"api_base_url": "http://192.168.1.100:8765/api",
|
||||||
|
"device_id": "kobo-device-uuid",
|
||||||
|
"device_token": "dev_xxxxx..."
|
||||||
|
},
|
||||||
|
"books": {
|
||||||
|
"sha256:abc123...": {
|
||||||
|
"bookmann_uuid": "uuid-123",
|
||||||
|
"title": "The Hobbit",
|
||||||
|
"author": "J.R.R. Tolkien",
|
||||||
|
"available_formats": ["epub", "kepub"],
|
||||||
|
"sha256": "abc123...",
|
||||||
|
"file_path": "/path/to/book.epub"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"collections": [
|
||||||
|
{
|
||||||
|
"name": "Sci-Fi",
|
||||||
|
"shelf_mapping": "Science Fiction",
|
||||||
|
"book_ids": ["uuid-1", "uuid-2", "uuid-3"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"opds_enabled": true,
|
||||||
|
"sidecar_enabled": true,
|
||||||
|
"last_updated": "2026-01-31T12:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Download Device Sidecar File
|
||||||
|
**Endpoint**: `GET /api/devices/:device_id/sidecar/download`
|
||||||
|
|
||||||
|
**Description**: Downloads a `.bookmann.json` configuration file for device setup.
|
||||||
|
|
||||||
|
**Request Headers**:
|
||||||
|
```
|
||||||
|
Authorization: Bearer {{user_token}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response** (200 OK):
|
||||||
|
```
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Disposition: attachment; filename="MyKoboClara.bookmann.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
File contains formatted JSON (pretty-printed) suitable for:
|
||||||
|
- Manual device configuration
|
||||||
|
- Backup and restore
|
||||||
|
- Transfer via USB
|
||||||
|
|
||||||
|
### 3. Get System Configuration
|
||||||
|
**Endpoint**: `GET /api/system/config`
|
||||||
|
|
||||||
|
**Description**: Returns system-wide configuration settings (admin only).
|
||||||
|
|
||||||
|
**Request Headers**:
|
||||||
|
```
|
||||||
|
Authorization: Bearer {{admin_token}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response** (200 OK):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"base_url": "https://bookmann.example.com",
|
||||||
|
"opds_base_url": "https://bookmann.example.com/opds",
|
||||||
|
"api_base_url": "https://bookmann.example.com/api"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Update System Configuration
|
||||||
|
**Endpoint**: `PUT /api/system/config`
|
||||||
|
|
||||||
|
**Description**: Updates system-wide configuration settings (admin only).
|
||||||
|
|
||||||
|
**Request Headers**:
|
||||||
|
```
|
||||||
|
Authorization: Bearer {{admin_token}}
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"base_url": "https://bookmann.example.com",
|
||||||
|
"opds_base_url": "https://bookmann.example.com/opds",
|
||||||
|
"api_base_url": "https://bookmann.example.com/api"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response** (200 OK):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "System configuration updated"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sidecar File Format
|
||||||
|
|
||||||
|
### Version
|
||||||
|
Always "1.0" - enables future format changes
|
||||||
|
|
||||||
|
### Bookmann Section
|
||||||
|
Contains device connection information:
|
||||||
|
- **opds_catalog**: Full URL to device's OPDS catalog
|
||||||
|
- **sync_api**: Sync API endpoint
|
||||||
|
- **opds_base_url**: Base URL for all OPDS operations
|
||||||
|
- **api_base_url**: Base URL for all API operations
|
||||||
|
- **device_id**: Device's unique identifier
|
||||||
|
- **device_token**: Device authentication token
|
||||||
|
|
||||||
|
### Books Section
|
||||||
|
Map of book identifiers to book metadata:
|
||||||
|
- **Key**: SHA-256 hash (preferred) or Bookmann UUID
|
||||||
|
- **bookmann_uuid**: Canonical Bookmann UUID
|
||||||
|
- **title**: Book title
|
||||||
|
- **author**: Book author
|
||||||
|
- **available_formats**: Array of formats ("epub", "kepub")
|
||||||
|
- **sha256**: SHA-256 hash of book file
|
||||||
|
- **file_path**: Original file path
|
||||||
|
|
||||||
|
### Collections Section
|
||||||
|
Array of collection definitions:
|
||||||
|
- **name**: Collection name in Bookmann
|
||||||
|
- **shelf_mapping**: Device-specific shelf name (e.g., "Science Fiction")
|
||||||
|
- **book_ids**: Array of Bookmann UUIDs in collection
|
||||||
|
|
||||||
|
## Device Setup Workflow
|
||||||
|
|
||||||
|
### Kobo E-Reader
|
||||||
|
|
||||||
|
1. **Download Configuration**
|
||||||
|
- Log into Bookmann web UI
|
||||||
|
- Navigate to Device Management
|
||||||
|
- Click "Download Configuration" for your Kobo device
|
||||||
|
- File saves as `MyKoboClara.bookmann.json`
|
||||||
|
|
||||||
|
2. **Manual Configuration** (if needed)
|
||||||
|
- Copy `.bookmann.json` to Kobo device
|
||||||
|
- Kobo can import configuration automatically
|
||||||
|
|
||||||
|
3. **OPDS Setup** (Recommended)
|
||||||
|
- Use `opds_catalog` URL from sidecar
|
||||||
|
- Add as new content catalog in Kobo settings
|
||||||
|
- Browse and download books wirelessly
|
||||||
|
|
||||||
|
### KOReader
|
||||||
|
|
||||||
|
1. **Download Configuration**
|
||||||
|
- Same process as Kobo above
|
||||||
|
|
||||||
|
2. **Configure Wireless Sync**
|
||||||
|
- In KOReader, set Calibre wireless URL to `sync_api` from sidecar
|
||||||
|
- Enable password and use `device_token`
|
||||||
|
- Set sync frequency to desired interval
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Set these in your Bruno collection:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"base_url": "http://localhost:8765/api",
|
||||||
|
"user_token": "your-user-jwt-token",
|
||||||
|
"admin_token": "your-admin-jwt-token",
|
||||||
|
"device_id": "uuid-of-device"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Codes
|
||||||
|
|
||||||
|
- `200`: Success
|
||||||
|
- `400`: Bad Request (invalid device ID, invalid JSON)
|
||||||
|
- `401`: Unauthorized (missing or invalid token)
|
||||||
|
- `403`: Forbidden (admin access required)
|
||||||
|
- `404`: Not Found (device not found)
|
||||||
|
- `500`: Internal Server Error (database error, generation failure)
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
1. **Easy Setup**: One file contains all configuration
|
||||||
|
2. **No Manual Entry**: URLs and tokens pre-populated
|
||||||
|
3. **SHA-256 Matching**: Reliable book identification
|
||||||
|
4. **Collection Sync**: Shelf mappings included
|
||||||
|
5. **Backup/Restore**: Save and transfer device configs
|
||||||
|
6. **Offline Configuration**: Configure devices without network access initially
|
||||||
|
|
||||||
|
## Testing Scenarios
|
||||||
|
|
||||||
|
### Scenario 1: New Kobo Device
|
||||||
|
1. Register Kobo device in Bookmann
|
||||||
|
2. Download sidecar configuration
|
||||||
|
3. Add OPDS catalog URL from sidecar to Kobo
|
||||||
|
4. Browse and download books wirelessly
|
||||||
|
5. Progress syncs automatically
|
||||||
|
|
||||||
|
### Scenario 2: Device Re-configuration
|
||||||
|
1. Download current sidecar file
|
||||||
|
2. Update system configuration if needed
|
||||||
|
3. Re-download sidecar with new settings
|
||||||
|
4. Re-configure device with updated file
|
||||||
|
|
||||||
|
### Scenario 3: Collection Management
|
||||||
|
1. Create collections in Bookmann
|
||||||
|
2. Set up device-specific shelf mappings
|
||||||
|
3. Sidecar automatically includes collection info
|
||||||
|
4. Device shelves reflect collection structure
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
meta {
|
||||||
|
name: "Update System Configuration"
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
url: {{base_url}}/api/system/config
|
||||||
|
body: json({
|
||||||
|
"base_url": "https://bookmann.example.com",
|
||||||
|
"opds_base_url": "https://bookmann.example.com/opds",
|
||||||
|
"api_base_url": "https://bookmann.example.com/api"
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{admin_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.status == "success"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
meta {
|
||||||
|
name: "Get Unlinked Books - User View"
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/sync/unlinked-books
|
||||||
|
body: none
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{user_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.unlinked exists()
|
||||||
|
response.body.total exists()
|
||||||
|
response.body.total >= 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
meta {
|
||||||
|
name: "Link Unlinked Book - Manual Resolution"
|
||||||
|
type: http
|
||||||
|
seq: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{base_url}}/api/sync/link-book
|
||||||
|
body: json({
|
||||||
|
"unlinked_book_id": "{{unlinked_book_id}}",
|
||||||
|
"media_item_id": "{{media_item_id}}",
|
||||||
|
"confidence_score": 1.0
|
||||||
|
})
|
||||||
|
auth: {
|
||||||
|
type: bearer
|
||||||
|
bearer: {{user_token}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
response.status == 200
|
||||||
|
response.body.status == "linked"
|
||||||
|
response.body.unlinked_book_id exists()
|
||||||
|
response.body.media_item_id exists()
|
||||||
|
response.body.message exists()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user