Skip to content

fix(api): name the genre route, and report where the event stream has been cut - #181

Merged
InstaZDLL merged 2 commits into
mainfrom
fix/name-the-genre-route-for-what-it-does
Sep 10, 2026
Merged

InstaZDLL merged 2 commits into
mainfrom
fix/name-the-genre-route-for-what-it-does

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Closes #179 and #178. Two independent issues, one commit each.

#179 — the path was wrong, not the parameter

GET /api/v2/songs required genre and answered 400 without it, while wired to a handler called list_songs_by_genre.

The issue offered two coherent fixes and asked for one to be picked. The code picked it: a test already pinned that 400 with its reason — the genre is what the request is about, so its absence is a malformed request and not an unfiltered catalogue — so the behaviour is deliberate. And the general listing exists already, as /libraries/{id}/tracks, which pages and searches. Relaxing genre to an option would have undone a decision somebody made on purpose and left two ways to list songs.

So the path moved: /api/v2/songs/by-genre, beside /songs/random. /api/v2/songs now belongs to nobody, and a test says it answers 404 — a path that means nothing is better than one that means something it will not do.

The last reference was found by the suite, not by me: the search that swept the rename looked for "/api/v2/songs" closed and missed the one carrying a query string.

#178 — the watermark was not missing, only unreported

The issue says the stream exposes no watermark. It exists: library.events_purged_through, read in the same transaction that builds the page, and already the value that decides the refusal. It is now a field on LibraryEventPage, at no extra query — and describing the same instant as the rows it travels with, which a second read could not promise.

{ "events": [...], "next_cursor": 41, "has_more": false, "purged_through": 0 }

The test corrected the sentence I wrote to document it. I had said the margin was next_cursor - purged_through. It is not: next_cursor is the end of the page just served, so it describes the margin the client will have after applying it. The margin it holds is its own cursor minus the watermark. The assertion failed on exactly that gap, and both the field's doc comment and the API guide now draw the distinction.

Verification

cargo fmt, clippy -D warnings, 162 Rust tests across 17 targets · biome, tsc and the web build clean.

Both assertions were checked by taking the fix away: hard-coding purged_through: 0 fails the feed test.

#177 is untouched — it needs the desktop audit recorded in web-client-gap-analysis.md before its semantics change.

https://claude.ai/code/session_01TyKunaKXS16hyFDBHwc5KK

Summary by CodeRabbit

  • Nouvelles fonctionnalités

    • L’endpoint de récupération des chansons par genre est désormais disponible à l’adresse /api/v2/songs/by-genre.
    • Les événements de bibliothèque indiquent maintenant jusqu’où les anciens événements ont été purgés, afin de mieux détecter les curseurs expirés et anticiper une resynchronisation.
  • Corrections

    • L’application web utilise la nouvelle route pour filtrer les chansons par genre et par bibliothèque.
  • Documentation

    • Le guide API et l’analyse de compatibilité reflètent les nouvelles routes et informations de synchronisation.

Closes #179.

`GET /api/v2/songs` required `genre` and answered 400 without it, while
being wired to a handler called `list_songs_by_genre`. The path said
"songs", the handler said "songs by genre", and the contract said no.

The behaviour is the deliberate half. A test already pinned that 400
with its reason — the genre is what the request is about, so its absence
is a malformed request and not an unfiltered catalogue — and the general
listing exists elsewhere, as `/libraries/{id}/tracks`, which pages and
searches. Relaxing `genre` to an option would have undone a decision
somebody made on purpose and left two ways to list songs.

So the path moves rather than the parameter: `/api/v2/songs/by-genre`,
beside `/songs/random`. `/api/v2/songs` now belongs to nobody, and a
test says it answers 404 — it was its readability that sent people
looking for a bug, not its status code, and a path that means nothing is
better than one that means something it will not do.

The last reference was found by the suite rather than by me: the search
that swept the rename looked for `"/api/v2/songs"` closed, and missed
the one carrying a query string.

Claude-Session: https://claude.ai/code/session_01TyKunaKXS16hyFDBHwc5KK
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Closes #178.

A client following a library's events keeps a cursor, and the server
refuses it once retention has cut past it. Nothing said how close that
was, so the first sign of trouble was the refusal — the worst moment to
pay for a full resync, since it has to happen right then whatever else
is going on.

The watermark was not missing, only unreported. `library.events_purged_through`
already exists, is already read in the same transaction that builds the
page, and is already what decides the refusal. It is now a field on that
page, at no extra query and describing the same instant as the rows it
travels with, which a second read could not promise.

The test found an error in the sentence that documented it. The margin
is the client's **own** cursor minus the watermark, not `next_cursor`
minus the watermark: `next_cursor` is the end of the page just served,
so it says what the margin becomes once the page is applied, not what it
is. The assertion failed on exactly that gap; the field's documentation
and the API guide now draw the distinction rather than glossing it.

Claude-Session: https://claude.ai/code/session_01TyKunaKXS16hyFDBHwc5KK
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added scope: server Server core (Rust) scope: web Embedded web player (React) scope: docs Docs, README, assets type: fix Bug fix labels Sep 10, 2026
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 8f5d136d-3d89-45ef-a70d-478b7f5e6cc3

📥 Commits

Reviewing files that changed from the base of the PR and between 2cc300f and 04e1f04.

📒 Files selected for processing (9)
  • docs/api-v2-guide.md
  • docs/opensubsonic-gap-analysis.md
  • src/api/catalog.rs
  • src/api/mod.rs
  • src/services/library_events.rs
  • src/services/mod.rs
  • tests/catalog.rs
  • tests/sync.rs
  • webapp/src/api.ts

Included review availability: 8 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 10 reviews per hour.


📝 Walkthrough

Walkthrough

La route de filtrage par genre passe à /api/v2/songs/by-genre. Les pages d’événements exposent maintenant purged_through, avec une documentation et des tests mis à jour.

Changes

Route de filtrage par genre

Layer / File(s) Summary
Contrat et intégration de la route
src/api/catalog.rs, src/api/mod.rs, webapp/src/api.ts, docs/api-v2-guide.md, docs/opensubsonic-gap-analysis.md, tests/catalog.rs
La route devient /api/v2/songs/by-genre. Le client web, la documentation et les tests utilisent ce chemin. Les tests vérifient aussi que l’ancien chemin renvoie 404 et qu’un genre absent renvoie 400.

Limite de rétention des événements

Layer / File(s) Summary
Watermark de rétention et validation
src/services/mod.rs, src/services/library_events.rs, docs/api-v2-guide.md, tests/sync.rs
LibraryEventPage expose purged_through, lu dans le même snapshot que les événements. La documentation et les tests couvrent la valeur initiale, la marge nulle et la progression de next_cursor.

Estimated code review effort: 2 (Simple) | ~10 minutes

Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to 04e1f

Genre-filtered song requests now use the explicit by-genre route, while event pages expose their retention watermark. The API, client, documentation, and tests align with the intended behavior, with no remaining merge-blocking risk identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Les changements relatifs à purged_through et au flux d’événements ne sont pas couverts par l’issue fournie [#179]. Ils correspondent à l’issue #178 mentionnée dans la description, mais cette issue n… Ajoutez l’issue #178 aux issues liées et fournissez ses critères d’acceptation, ou déplacez les changements relatifs à purged_through dans une pull request distincte.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Le titre décrit clairement les deux changements principaux : le nommage de la route par genre et l’exposition de la coupure du flux d’événements.
Description check ✅ Passed La description fournit un résumé détaillé, les changements effectués et les résultats de validation. Elle n’utilise pas les titres du modèle et ne reprend pas explicitement la confirmation DCO, mais s…
Linked Issues check ✅ Passed L’objectif de l’issue [#179] est respecté : la route devient /api/v2/songs/by-genre, le paramètre genre reste obligatoire et /api/v2/songs renvoie 404. Les tests couvrent ces comportements.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 7 files. (2 skipped: 2 u…
Full details: Out of Scope Changes check

Explanation

Les changements relatifs à purged_through et au flux d’événements ne sont pas couverts par l’issue fournie [#179]. Ils correspondent à l’issue #178 mentionnée dans la description, mais cette issue n’est pas présente dans le contexte des issues liées.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/name-the-genre-route-for-what-it-does

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size: s 10-50 lines type: fix Bug fix and removed type: fix Bug fix labels Sep 10, 2026
@InstaZDLL
InstaZDLL merged commit 4fc7410 into main Sep 10, 2026
15 of 16 checks passed
@InstaZDLL
InstaZDLL deleted the fix/name-the-genre-route-for-what-it-does branch September 10, 2026 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: docs Docs, README, assets scope: server Server core (Rust) scope: web Embedded web player (React) size: s 10-50 lines type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: /api/v2/songs requires a genre, so it cannot list songs

1 participant