Commit 875649f
committed
feat(addToRotation): library_identity lookup + LML resolve + allowlist (BS#1380 PR 4/5)
Fourth in the BS#1380 chain. Chains feature/bs-1380-3-rotation-etl-drift
(which carries PR 1's schema). Depends on PR 2 (#1418) for the
`resolveIdentity` wrapper; merged in here so this branch typechecks.
apps/backend/services/library.service.ts:addToRotation:
- Synchronously looks up `library_identity.discogs_release_id` for the
requested album_id (library_identity.library_id is PRIMARY KEY per
schema.ts:1391-1393).
- If non-NULL, awaits `resolveIdentity({kind:'release',
source:'discogs_release', external_id})` before INSERT to mint a
stable lml_identity_id.
- Triple-writes (discogs_release_id, discogs_release_id_source =
'library_identity', lml_identity_id) on the INSERT.
- On resolve failure (timeout / 5xx / network), the first two still
land; lml_identity_id falls back to NULL. The source-of-the-Discogs-id
is library_identity regardless of whether the mint succeeded —
kill_date forensics, BS#1029 backfill scoping, and the future
discogs_release_id retirement migration all rely on this column being
honest.
- The daily backfill cron (PR 5) catches up within ~24h.
apps/backend/services/library.service.ts:classifyLmlResolveError:
- Pure classifier mapping `unknown` → `LmlResolveFallbackReason`
(`timeout` | `5xx` | `4xx` | `network` | `other`). Each bucket
signals a different operational response (timeout → LML latency,
5xx → LML deploy regression, 4xx → BS-side caller bug, network →
infra noise). Sentry counter `lml.resolve.fallback_to_null` carries
the result as a `reason` attribute alongside `caller=add_to_rotation`.
- Disambiguates LmlClientError statusCode 502 between upstream-5xx
(message starts with "LML responded with") and network/fetch-threw
(message starts with "LML request failed").
apps/backend/controllers/library.controller.ts:pickAddRotationFields:
- Signature-typed allowlist mirroring flowsheet.controller.ts
pickUpdateEntryFields (BS#1099). Only {album_id, rotation_bin} pass
through.
- Server-derived columns (legacy_*, discogs_release_id*,
lml_identity_id, tracklist_lookup_attempted_at, kill_date) and
tubafrenzy-ETL-only snapshot columns (add_date, artist_name,
album_title, record_label) must never be client-supplied through
this endpoint — addToRotation derives the LML-handle columns from
library_identity + the synchronous resolveIdentity hop, and
tubafrenzy is the only legitimate source for the snapshot columns.
- Phrased as an allowlist so future column additions to `rotation`
are implicitly rejected by typecheck until explicitly added to the
signature.
Unit tests:
- classifyLmlResolveError: every catch-block branch (AbortError →
timeout, 504 → timeout, 502 + "LML responded" message → 5xx,
502 + "LML request failed" message → network, 422 → 4xx, 400 →
4xx, 500 → 5xx, ECONNRESET / ENOTFOUND / ECONNREFUSED → network,
plain Error → other, non-Error throwables → other).
- addToRotation: library_identity hit + resolve success → triple-write
lands correctly; library_identity hit + resolve failure → first two
fields land, lml_identity_id NULL, Sentry counter fires once with
reason=timeout; library_identity miss → no LML call, no triple-write;
reads library_identity by library_id PRIMARY KEY.
Integration test (tests/integration/library.spec.js):
- POST /library/rotation with non-allowlisted fields in the body
(forbidden server-derived + ETL-only) verifies the persisted row
has those fields server-set / NULL, not client-supplied.
Refs #13801 parent f9e1641 commit 875649f
4 files changed
Lines changed: 495 additions & 2 deletions
File tree
- apps/backend
- controllers
- services
- tests
- integration
- unit/services
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
307 | 307 | | |
308 | 308 | | |
309 | 309 | | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
310 | 340 | | |
311 | 341 | | |
312 | 342 | | |
313 | 343 | | |
314 | 344 | | |
315 | | - | |
| 345 | + | |
| 346 | + | |
316 | 347 | | |
317 | 348 | | |
318 | 349 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
39 | 40 | | |
40 | 41 | | |
41 | 42 | | |
| |||
340 | 341 | | |
341 | 342 | | |
342 | 343 | | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
343 | 424 | | |
344 | | - | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
345 | 474 | | |
346 | 475 | | |
347 | 476 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
346 | 346 | | |
347 | 347 | | |
348 | 348 | | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
349 | 404 | | |
350 | 405 | | |
351 | 406 | | |
| |||
0 commit comments