feat(release): front int PK with app-minted GUID EntryKey on the public addressing surface (P11 W5, 11.H)
This commit is contained in:
@@ -220,19 +220,19 @@ Paged release list, optionally filtered to one medium. Public browse data, same
|
||||
- `sortDescending` (bool, optional, default false): sort direction.
|
||||
- Returns 200 with `PagedResult<ReleaseDto>` on success. Returns 400 if `medium` is unrecognized. Returns 500 on query error.
|
||||
|
||||
### GET api/release/{id:long} (unauthenticated)
|
||||
### GET api/release/{entryKey} (unauthenticated)
|
||||
|
||||
Single release with both metadata navs (nulls for non-matching media). Public, same auth posture as `GET api/release`.
|
||||
Single release with both metadata navs (nulls for non-matching media). Public, same auth posture as `GET api/release`. Addresses releases by their opaque public `EntryKey` (GUID string), never the int PK (Phase 11 §3e).
|
||||
|
||||
- **Route parameter `id`** (long): the SQL release ID.
|
||||
- **Response**: `ReleaseDto` with `Id`, `Title`, `Artist`, `Genre`, `ReleaseDate`, `Medium`, `ImagePath`, and media-specific metadata satellites (`MixMetadata` for Cut/Mix, `SessionMetadata` for Session; others null).
|
||||
- **Route parameter `entryKey`** (string): the release's `EntryKey` (the public handle).
|
||||
- **Response**: `ReleaseDto` with `Id`, `EntryKey`, `Title`, `Artist`, `Genre`, `ReleaseDate`, `Medium`, `ImagePath`, and media-specific metadata satellites (`MixMetadata` for Cut/Mix, `SessionMetadata` for Session; others null).
|
||||
- Returns 200 on success. Returns 404 if not found. Returns 500 on query error.
|
||||
|
||||
### GET api/release/{id:long}/mix/waveform (unauthenticated)
|
||||
### GET api/release/{entryKey}/mix/waveform (unauthenticated)
|
||||
|
||||
Serves the high-res waveform datum for a Mix release as base64-encoded bytes. Mirrors `GET api/track/{id}/waveform` but reads from the `mix-waveforms` vault.
|
||||
Serves the high-res waveform datum for a Mix release as base64-encoded bytes. Mirrors `GET api/track/{id}/waveform` but reads from the `mix-waveforms` vault. Public read — addresses by the release `EntryKey` (§3e).
|
||||
|
||||
- **Route parameter `id`** (long): the SQL release ID.
|
||||
- **Route parameter `entryKey`** (string): the release's `EntryKey`.
|
||||
- **Response**: `WaveformProfileDto` with `BucketCount` and `Data` (base64).
|
||||
- Returns 200 on success. Returns 404 if the release is not a Mix, carries no waveform key, or no datum is stored. Returns 500 on query/vault error.
|
||||
|
||||
|
||||
@@ -66,18 +66,20 @@ public class ReleaseController : ControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
// GET api/release/{id}/mix/waveform (unauthenticated)
|
||||
// GET api/release/{entryKey}/mix/waveform (unauthenticated)
|
||||
// Serves the high-res waveform datum for a Mix release as base64. Mirrors GET api/track/{id}/waveform
|
||||
// but reads from the mix-waveforms vault. 404 when the release is not a Mix, carries no waveform key,
|
||||
// or no datum is stored. Declared before the shorter "{id:long}" route for clarity.
|
||||
[HttpGet("{id:long}/mix/waveform")]
|
||||
public async Task<ActionResult> GetMixWaveform(long id, CancellationToken ct = default)
|
||||
// or no datum is stored. Public read — addresses by the opaque EntryKey, not the int PK (§3e). The
|
||||
// {entryKey} string segment cannot collide with the ApiKey-gated POST {id:long}/mix/waveform (different
|
||||
// verb + constraint). Declared before the shorter "{entryKey}" route for clarity.
|
||||
[HttpGet("{entryKey}/mix/waveform")]
|
||||
public async Task<ActionResult> GetMixWaveform(string entryKey, CancellationToken ct = default)
|
||||
{
|
||||
var lookup = await _releaseService.GetByIdAsync(id, ct);
|
||||
var lookup = await _releaseService.GetByEntryKeyAsync(entryKey, ct);
|
||||
if (!lookup.Success)
|
||||
{
|
||||
var error = lookup.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
_logger.LogError("GetMixWaveform lookup failed for {ReleaseId}: {Error}", id, error);
|
||||
_logger.LogError("GetMixWaveform lookup failed for {EntryKey}: {Error}", entryKey, error);
|
||||
return StatusCode(500, "Failed to load release");
|
||||
}
|
||||
|
||||
@@ -85,14 +87,14 @@ public class ReleaseController : ControllerBase
|
||||
var waveformEntryKey = release?.MixMetadata?.WaveformEntryKey;
|
||||
if (release is null || release.Medium != ReleaseMedium.Mix || string.IsNullOrEmpty(waveformEntryKey))
|
||||
{
|
||||
_logger.LogInformation("No mix waveform datum for release: {ReleaseId}", id);
|
||||
_logger.LogInformation("No mix waveform datum for release: {EntryKey}", entryKey);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var bytes = await _waveformProfileService.GetProfileAsync(waveformEntryKey, VaultConstants.MixWaveforms);
|
||||
if (bytes is null)
|
||||
{
|
||||
_logger.LogInformation("Mix waveform key set but no datum stored for release: {ReleaseId}", id);
|
||||
_logger.LogInformation("Mix waveform key set but no datum stored for release: {EntryKey}", entryKey);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
@@ -159,17 +161,18 @@ public class ReleaseController : ControllerBase
|
||||
return StatusCode(500, error);
|
||||
}
|
||||
|
||||
// GET api/release/{id} (unauthenticated)
|
||||
// Single release with both metadata navs (nulls for non-matching media). Declared after the longer
|
||||
// "{id:long}/mix/waveform" routes so the segmented routes resolve first.
|
||||
[HttpGet("{id:long}")]
|
||||
public async Task<ActionResult> GetReleaseById(long id, CancellationToken ct = default)
|
||||
// GET api/release/{entryKey} (unauthenticated)
|
||||
// Single release with both metadata navs (nulls for non-matching media). Public read — addresses by
|
||||
// the opaque EntryKey, not the int PK (§3e). Declared after the longer "{entryKey}/mix/waveform"
|
||||
// route so the segmented route resolves first.
|
||||
[HttpGet("{entryKey}")]
|
||||
public async Task<ActionResult> GetReleaseByEntryKey(string entryKey, CancellationToken ct = default)
|
||||
{
|
||||
var result = await _releaseService.GetByIdAsync(id, ct);
|
||||
var result = await _releaseService.GetByEntryKeyAsync(entryKey, ct);
|
||||
if (!result.Success)
|
||||
{
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
_logger.LogError("GetReleaseById failed for {ReleaseId}: {Error}", id, error);
|
||||
_logger.LogError("GetReleaseByEntryKey failed for {EntryKey}: {Error}", entryKey, error);
|
||||
return StatusCode(500, "Failed to load release");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user