feat(api): add slim series external-index endpoint for discovery tools#27
Merged
Conversation
Add GET /api/v1/series/external-index returning a paginated, slim per-series projection: series UUID, linked external IDs (source, externalId, optional externalUrl), local volume/chapter maxima, and volumes owned. It is a lightweight alternative to GET /api/v1/series?full=true for external discovery consumers that key on external IDs and would otherwise over-fetch and discard the full DTO. Gated by SeriesRead and honors sharing-tag content visibility, so it only exports series the caller can access; never anonymous. Reuses the existing batch repositories (get_for_series_ids and get_book_classification_aggregates_for_series_ids) over just the current page's IDs, with no new SQL or schema. Ordering is fixed to name-asc for deterministic single-sweep pagination. Items are wrapped under the standard PaginatedResponse `data` key, consistent with the other v1 list endpoints. Registers the new schemas and path in the OpenAPI document. Adds handler tests covering response shape, a series with no external IDs, classification counts, pagination total, and the anonymous 401 path.
…endpoint Regenerate the OpenAPI specification and frontend TypeScript types to reflect the series external-index endpoint added to the backend. Adds the /api/v1/series/external-index path and the SeriesExternalIndexDto and SeriesExternalIdRefDto schemas to web/openapi.json and the mirrored docs/api/openapi.json, with matching types in the generated TypeScript client. The endpoint's paginated response reuses the shared PaginatedResponse schema, consistent with the other series list endpoints. Generated via `make openapi-all`; the pre-commit OpenAPI sync check passes with no drift.
The openapi-sync pre-commit hook still matched the pre-crate paths (src/api/*.rs, src/db/entities/*.rs), which no longer exist after the workspace was split into crates. As a result the hook never fired on backend changes, so OpenAPI spec/type drift could slip through. Point the files pattern at the crates that actually feed the generated spec via utoipa annotations: codex-api, codex-models, codex-services, codex-tasks, codex-events, and codex-db.
Deploying codex with
|
| Latest commit: |
81c57ff
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b68fbccc.codex-asm.pages.dev |
| Branch Preview URL: | https://tsundoku-support.codex-asm.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a read-only, paginated
GET /api/v1/series/external-indexendpoint that projects each series down to just the fields an external discovery tool needs: the series ID, its linked external IDs, and how far the local library has progressed. It is a lightweight alternative to fetching the full series payload purely to read external IDs.Motivation
There was no way to enumerate series alongside their external IDs in a single slim call. The full series response returns external IDs but carries metadata, genres, tags, covers, ratings, and links that a discovery consumer would fetch and immediately discard, while the lightweight series response omits external IDs entirely. As a result, a tool that just wants "what does this library own, keyed by external ID" had to over-fetch the full payload for every series.
Changes
GET /api/v1/series/external-index?page=&pageSize=endpoint returning a paginated list of series, each withid,externalIds(source,externalId, optionalexternalUrl),localMaxVolume,localMaxChapter, andvolumesOwned. Items are returned under the standard paginateddatakey.externalIdsarray) so consumers can offer manual linking.SeriesReadpermission (Reader role and up); anonymous requests are rejected. Results honor the same sharing-tag content visibility rules as the main series listing, so callers only see series they can access.Notes
volumesOwnedis an approximate file count, not an authoritative measure; the reliable "how far along" signals arelocalMaxVolumeandlocalMaxChapter.updatedSince); consumers re-sweep the full list, which is fine at personal-library scale.pageSizeis capped at the existing maximum.