feat(dashboard): implement Phase 4.5 Collections Preview Endpoint
Add preview endpoint for custom section builder and rule evaluation: Handler Implementation (internal/handlers/collections.go): - PreviewCollection method: Evaluates filter rules and returns matching items without saving * Accepts library_id, rules array, manual_book_ids array, and limit * Evaluates rules against all library items using collectionService.EvaluateRules * Adds manually selected books to results * Deduplicates manual books (avoids adding same book twice) * Applies limit (default: 20, max: 100) * Returns array of BookInfo with matching items - Helper function: mediaItemsToListMediaItemsRow * Converts database.MediaItems to database.ListMediaItemsRow * Required for EvaluateRules which expects ListMediaItemsRow type Route Registration (internal/router/collections.go): - POST /api/collections/preview - Protected by JWT middleware - Part of collections API group Why This Endpoint is Necessary: - Allows users to see what books match their filter rules BEFORE saving - Avoids creating incorrect collections - Enables testing different rule combinations quickly - Reuses existing service logic (collectionService.EvaluateRules) - Client-side preview would require downloading entire library (10,000+ books) - Would duplicate 500+ lines of rule evaluation logic in TypeScript - Would create maintenance nightmare keeping Go and TypeScript in sync Bruno Test (bruno/collections/preview-collection.bru): - Tests POST /api/collections/preview endpoint - Validates status 200 response - Validates items array in response - Example request with genre filter rule This endpoint is required for both the web UI Custom Section Builder and future mobile apps.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
meta:
|
||||
name: Preview Collection
|
||||
type: http
|
||||
seq: 1
|
||||
http:
|
||||
method: POST
|
||||
url: '{{base_url}}/api/collections/preview'
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"rules\": [\n\
|
||||
{\n \"id\": \"rule1\",\n \"field\": \"genre\",\n\
|
||||
\"operator\": \"equals\",\n \"value\": \"Fiction\",\n\
|
||||
\"priority\": 1\n }\n ],\n \"manual_book_ids\": [],\n \"limit\": 20\n}"
|
||||
runtime:
|
||||
scripts:
|
||||
- type: tests
|
||||
code: "test(\"status must be 200 with valid request\", function() {\n expect(res.status).to.eql(200);\n\
|
||||
});\n\ntest(\"response contains items array\", function() {\n expect(res.body.items).to.exist;\n\
|
||||
expect(res.body.items).to.be.an('array');\n });"
|
||||
|
||||
docs: |-
|
||||
Preview collection with filter rules before saving.
|
||||
|
||||
**Endpoint**: POST /api/collections/preview
|
||||
**Auth**: Required (Bearer token)
|
||||
|
||||
## Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| library_id | string | Yes | Library UUID |
|
||||
| rules | array | No | Filter rules to evaluate |
|
||||
| manual_book_ids | array | No | Manually selected book IDs |
|
||||
| limit | int | No | Max items to return (default: 20, max: 100) |
|
||||
|
||||
## Example Request
|
||||
|
||||
```json
|
||||
{
|
||||
"library_id": "cc23c3a7-f8fb-451a-a78d-2a16df1b725a",
|
||||
"rules": [
|
||||
{
|
||||
"id": "rule1",
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Fiction",
|
||||
"priority": 1
|
||||
}
|
||||
],
|
||||
"manual_book_ids": [],
|
||||
"limit": 20
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
Returns array of matching book items.
|
||||
|
||||
This endpoint is used by the Custom Section Builder to show users what books will match their filter rules before they save the collection.
|
||||
Reference in New Issue
Block a user