bruno: regenerate collection from canonical docs + permanent docs↔Bruno parity gate
WO4. The old collection was the drifted artifact AGENTS.md warned about:
bodies pre-truncated at their first {{var}}, routes renamed server-side
but not here (/api/notes/:media_id, /api/book-matching/search,
/api/collections/:id/rules/test), an orphan universal-progress suite for
routes that no longer exist.
scripts/gen_bruno_from_docs.py emits one OpenCollection request per
frontmatter'd endpoint doc (191 requests): gate-verified example-request
JSON as body, {{env}} params per the environment file, device-token
routes get an explicit Bearer {{device_token}} header, login/register/
refresh keep the token-capture after-response scripts that chain the
collection. Old 1:1 mirrors deleted (128); hand-authored NewDevDBSetup,
environments, and edge-case suites kept. 30 scenario files with
truncated bodies (the documented corruption) deleted — they could not
run.
internal/docs/drift_test.go gains TestBrunoMirrorsDocs: every
frontmatter'd doc must have a Bruno request with matching method+URL
shape — the collection can never silently drift from the docs again.
opencollection.yml README block replaced with the real run instructions.
make test green (9 pkgs, parity test included in internal/docs).
This commit is contained in:
1 parent
6fc501b35a
commit
acd82fff0a
353 files changed
+5165
-5293
No files matched your search
@@ -1,22 +0,0 @@
|
||||
info:
|
||||
name: Delete Conflict
|
||||
type: http
|
||||
seq: 4
|
||||
http:
|
||||
method: DELETE
|
||||
url: '{{base_url}}/api/conflicts/{{conflict_id}}'
|
||||
auth: inherit
|
||||
body:
|
||||
type: none
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: Bearer {{token
|
||||
|
||||
docs: |-
|
||||
## Delete Conflict
|
||||
|
||||
Permanently deletes a specific conflict record from the system.
|
||||
|
||||
**Method:** DELETE
|
||||
|
||||
**Endpoint:** /api/conflicts/{conflict_id
|
||||
@@ -1,37 +0,0 @@
|
||||
info:
|
||||
name: Dismiss All Resolved Conflicts
|
||||
type: http
|
||||
seq: 5
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/dismiss-all'
|
||||
auth: inherit
|
||||
body:
|
||||
type: none
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: Bearer {{token
|
||||
|
||||
docs: |-
|
||||
## Dismiss All Resolved Conflicts
|
||||
|
||||
Deletes all resolved conflicts for the authenticated user, cleaning up the conflict list.
|
||||
|
||||
**Method:** POST
|
||||
|
||||
**Endpoint:** /api/conflicts/dismiss-all
|
||||
|
||||
**Authentication:** Bearer token
|
||||
|
||||
**Response:**
|
||||
- `deleted` (number): Number of conflict records that were deleted
|
||||
|
||||
**Status Codes:**
|
||||
- 200: Success - conflicts deleted
|
||||
- 401: Unauthorized
|
||||
- 500: Internal server error
|
||||
|
||||
**Example Response:**
|
||||
```json
|
||||
{
|
||||
"deleted": 5
|
||||
@@ -1,22 +0,0 @@
|
||||
info:
|
||||
name: Get Conflict Details
|
||||
type: http
|
||||
seq: 2
|
||||
http:
|
||||
method: GET
|
||||
url: '{{base_url}}/api/conflicts/{{conflict_id}}'
|
||||
auth: inherit
|
||||
body:
|
||||
type: none
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: Bearer {{token
|
||||
|
||||
docs: |-
|
||||
## Get Conflict Details
|
||||
|
||||
Retrieves detailed information about a specific conflict, including side-by-side comparison of conflicting data from all sources.
|
||||
|
||||
**Method:** GET
|
||||
|
||||
**Endpoint:** /api/conflicts/{conflict_id
|
||||
@@ -1,74 +0,0 @@
|
||||
info:
|
||||
name: List Conflicts
|
||||
type: http
|
||||
seq: 1
|
||||
http:
|
||||
method: GET
|
||||
url: '{{base_url}}/api/conflicts?status=unresolved'
|
||||
auth: inherit
|
||||
body:
|
||||
type: none
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: Bearer {{token
|
||||
|
||||
docs: |-
|
||||
## List Conflicts
|
||||
|
||||
Lists all sync conflicts for the authenticated user with optional filtering.
|
||||
|
||||
**Method:** GET
|
||||
|
||||
**Endpoint:** /api/conflicts
|
||||
|
||||
**Authentication:** Bearer token
|
||||
|
||||
**Query Parameters:**
|
||||
- `status` (string, optional): Filter by resolution status
|
||||
- `unresolved`: Only unresolved conflicts (default)
|
||||
- `user_resolved`: Conflicts resolved by user
|
||||
- `auto_resolved`: Automatically resolved conflicts
|
||||
- `all`: All conflicts regardless of status
|
||||
- `type` (string, optional): Filter by conflict type
|
||||
- `progress`: Reading progress conflicts
|
||||
- `note`: Bookmark/note conflicts
|
||||
- `highlight`: Highlight conflicts
|
||||
|
||||
**Response:**
|
||||
- `conflicts` (array): Array of conflict objects
|
||||
- `total` (number): Total number of conflicts matching filters
|
||||
- `unresolved` (number): Number of unresolved conflicts
|
||||
|
||||
**Each Conflict Object:**
|
||||
- `id` (string): Conflict UUID
|
||||
- `media_item_id` (string): Associated book UUID
|
||||
- `media_item_title` (string): Book title
|
||||
- `conflict_type` (string): Type of conflict (progress, note, highlight)
|
||||
- `conflict_data` (object): Side-by-side comparison of conflicting data
|
||||
- `koreader`: Data from KOReader device
|
||||
- `kobo`: Data from Kobo device
|
||||
- `web`: Data from web interface
|
||||
- `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved)
|
||||
- `created_at` (string): ISO 8601 timestamp when conflict was detected
|
||||
|
||||
**Status Codes:**
|
||||
- 200: Success
|
||||
- 401: Unauthorized
|
||||
- 500: Internal server error
|
||||
|
||||
**Example Request:**
|
||||
```
|
||||
GET /api/conflicts?status=unresolved&type=progress
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
```json
|
||||
{
|
||||
"conflicts": [
|
||||
{
|
||||
"id": "conflict-uuid",
|
||||
"media_item_id": "book-uuid",
|
||||
"media_item_title": "Foundation",
|
||||
"conflict_type": "progress",
|
||||
"conflict_data": {
|
||||
"koreader": { "percentage": 0.65, "epubcfi": "..."
|
||||
@@ -1,15 +0,0 @@
|
||||
info:
|
||||
name: Resolve Conflict
|
||||
type: http
|
||||
seq: 3
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/{{conflict_id}}/resolve'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: "{\n \"winner\": \"koreader\",\n \"manual_data\": null,\n \"apply_to_all_future_conflicts\": false,\n \"reason\": \"User chose more recent progress\""
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: Bearer {{token
|
||||
docs: "## Resolve Conflict\n\n Resolves a sync conflict by choosing which source to use for the conflicting data.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/{conflict_id"
|
||||
@@ -0,0 +1,30 @@
|
||||
info:
|
||||
name: Bulk Dismiss Conflicts
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/bulk-dismiss'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: |-
|
||||
{
|
||||
"conflict_ids": [
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"660e8400-e29b-41d4-a716-446655440001"
|
||||
]
|
||||
}
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# Bulk Dismiss Conflicts
|
||||
|
||||
`POST /api/conflicts/bulk-dismiss` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/bulk_dismiss_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -0,0 +1,31 @@
|
||||
info:
|
||||
name: Bulk Resolve Conflicts
|
||||
type: http
|
||||
seq: 2
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/bulk-resolve'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: |-
|
||||
{
|
||||
"conflict_ids": [
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"660e8400-e29b-41d4-a716-446655440001"
|
||||
],
|
||||
"strategy": "highest_progress"
|
||||
}
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# Bulk Resolve Conflicts
|
||||
|
||||
`POST /api/conflicts/bulk-resolve` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/bulk_resolve_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -0,0 +1,21 @@
|
||||
info:
|
||||
name: Delete Conflict
|
||||
type: http
|
||||
seq: 3
|
||||
|
||||
http:
|
||||
method: DELETE
|
||||
url: '{{base_url}}/api/conflicts/{{media_item_id}}'
|
||||
auth: inherit
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# Delete Conflict
|
||||
|
||||
`DELETE /api/conflicts/:id` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/delete_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -0,0 +1,21 @@
|
||||
info:
|
||||
name: Dismiss All Resolved
|
||||
type: http
|
||||
seq: 4
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/dismiss-all'
|
||||
auth: inherit
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# Dismiss All Resolved
|
||||
|
||||
`POST /api/conflicts/dismiss-all` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/dismiss_all_resolved.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -0,0 +1,21 @@
|
||||
info:
|
||||
name: Get Conflict Details
|
||||
type: http
|
||||
seq: 5
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: '{{base_url}}/api/conflicts/{{media_item_id}}'
|
||||
auth: inherit
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# Get Conflict Details
|
||||
|
||||
`GET /api/conflicts/:id` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/get_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -0,0 +1,21 @@
|
||||
info:
|
||||
name: List Conflicts
|
||||
type: http
|
||||
seq: 6
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: '{{base_url}}/api/conflicts'
|
||||
auth: inherit
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# List Conflicts
|
||||
|
||||
`GET /api/conflicts` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/list_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -0,0 +1,27 @@
|
||||
info:
|
||||
name: Resolve Conflict
|
||||
type: http
|
||||
seq: 7
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/{{media_item_id}}/resolve'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: |-
|
||||
{
|
||||
"winner": "kobo"
|
||||
}
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
||||
docs: |-
|
||||
# Resolve Conflict
|
||||
|
||||
`POST /api/conflicts/:id/resolve` — auth: jwt.
|
||||
|
||||
Canonical reference: `docs/developer/api/conflicts/resolve_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
|
||||
@@ -1,17 +0,0 @@
|
||||
info:
|
||||
name: Bulk Dismiss Conflicts
|
||||
type: http
|
||||
seq: 1
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/bulk-dismiss'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: "{\n \"conflict_ids\": [\n \"{{conflictId1"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
- key: Authorization
|
||||
value: Bearer {{authToken
|
||||
docs: "## Bulk Dismiss Conflicts\n\n Dismisses multiple sync conflicts without resolving them. This removes them from the conflict list while leaving the data unchanged.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-dismiss\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to dismiss\n\n **Response:**\n - `results` (array): Results for each dismissal\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully dismissed conflicts\n - `failed` (number): Number of failed dismissals\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 404: One or more conflicts not found\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"conflict_ids\": [\"uuid-1\", \"uuid-2\"]"
|
||||
@@ -1,18 +0,0 @@
|
||||
info:
|
||||
name: Bulk Resolve Conflicts
|
||||
type: http
|
||||
seq: 1
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/bulk-resolve'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: "{\n \"conflict_ids\": [\n \"{{conflictId1"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
- key: Authorization
|
||||
value: Bearer {{authToken
|
||||
docs: "## Bulk Resolve Conflicts\n\n Resolves multiple sync conflicts in a single request using a specified resolution strategy.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-resolve\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to resolve\n - `strategy` (string): Resolution strategy\n - `most_recent`: Use the most recently updated progress\n - `highest_progress`: Use the reading progress with the highest percent read\n - `server`: Always prefer server-side data\n - `device`: Always prefer device-side data\n\n **Response:**\n - `results` (array): Results for each conflict resolution\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully resolved conflicts\n - `failed` (number): Number of failed resolutions\n\n **Status Codes:**\n - 200: Success (with partial results if some failed)\n - 400: Invalid request data\n - 401: Unauthorized\n\
|
||||
\ - 403: Forbidden\n - 404: One or more conflicts not found\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"conflict_ids\": [\"uuid-1\", \"uuid-2\", \"uuid-3\"],\n \"strategy\": \"most_recent\""
|
||||
@@ -1,18 +0,0 @@
|
||||
info:
|
||||
name: Bulk Resolve with Highest Progress Strategy
|
||||
type: http
|
||||
seq: 1
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/conflicts/bulk-resolve'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
data: "{\n \"conflict_ids\": [\n \"{{conflictId1"
|
||||
headers:
|
||||
- key: Content-Type
|
||||
value: application/json
|
||||
- key: Authorization
|
||||
value: Bearer {{authToken
|
||||
docs: "## Bulk Resolve with Highest Progress Strategy\n\n Resolves multiple sync conflicts using the \"highest_progress\" strategy, which keeps the reading progress with the highest percentage read.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-resolve\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to resolve\n - `strategy` (string): Must be \"highest_progress\"\n\n **Response:**\n - `results` (array): Results for each conflict resolution\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully resolved conflicts\n - `failed` (number): Number of failed resolutions\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 500: Internal server error\n\n **Note:** The highest progress strategy is ideal when you want to preserve the most reading progress across devices. Use this when you've been\
|
||||
\ reading on multiple devices and want to keep the furthest position."
|
||||
Reference in new issue
Block a user