fix(etl): normalize empty playlist_contents on update (apps#14306 parity)#265
Open
raymondjacobson wants to merge 1 commit into
Open
Conversation
f8ec063 to
bbb8053
Compare
77b7256 to
5ba7248
Compare
bbb8053 to
fc0aca1
Compare
5ba7248 to
3b51123
Compare
fc0aca1 to
21f3bcb
Compare
3b51123 to
cf1ebad
Compare
21f3bcb to
f9643da
Compare
cf1ebad to
22d2137
Compare
f9643da to
0a12b34
Compare
22d2137 to
996a8b7
Compare
0a12b34 to
50ba103
Compare
996a8b7 to
fab81fa
Compare
…ity)
apps#14306 fixed a bug where Python's `if not playlist_metadata.get("playlist_contents")`
truthiness check treated the SDK-sent empty list `[]` as field-omitted and
silently skipped the JSONB update — so removing the last track from a
playlist appeared to succeed in the UI but reappeared on reload.
go-openaudio's mergePlaylistFromMetadata uses `_, ok :=` (key-exists)
rather than truthiness, so the underlying skip-on-empty bug does not
reproduce. Verified via TestPlaylistUpdate_EmptyArrayMarksAllTracksRemoved
(the junction table correctly clears, and the JSONB column gets updated).
However, an adjacent parity gap was discovered: go-openaudio was persisting
whatever shape the SDK sent (bare `[]` or legacy `{"track_ids":[]}`) into
playlists.playlist_contents, while apps' process_playlist_contents always
normalizes to the dict form `{"track_ids":[...]}`. Downstream API readers
expect one shape.
This commit:
- Introduces normalizePlaylistContentsJSON that accepts bare array, legacy
dict, explicit null, and missing key, and always emits the canonical
`{"track_ids":[...]}` form.
- Switches both create-path (metadataPlaylistContentsJSON) and update-path
(mergePlaylistFromMetadata) to use it.
- Empty list now lands as `{"track_ids":[]}` in playlists.playlist_contents,
matching apps.
Tests:
- TestNormalizePlaylistContentsJSON covers all input shapes.
- TestPlaylistUpdate_EmptyArrayMarksAllTracksRemoved exercises both bare
`[]` (new SDK) and legacy `{track_ids:[]}` empty paths.
- TestPlaylistUpdate_EmptyArrayWritesJSONBColumn asserts the JSONB column
ends up as a JSON object with a track_ids key.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
50ba103 to
f696056
Compare
fab81fa to
ed53a6b
Compare
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
Picks up the backend behavior change from apps#14306 ("allow removing the last track from a playlist").
apps' bug
In Python,
if not playlist_metadata.get("playlist_contents")is true for an empty list. So when the SDK sent{"playlist_contents": []}(user removing the last track from a playlist), apps'populate_playlist_record_metadatasilently skipped the JSONB update. The UI showed the track removed (optimistic update), but a reload restored it because the persisted state was unchanged.apps' fix swaps
if not ...forif ... is None, so the empty list goes throughprocess_playlist_contentslike any other value.go-openaudio status
go-openaudio's
mergePlaylistFromMetadataalready uses a_, ok :=key-exists check, not a truthiness check — so the underlying bug does not reproduce here. Verified viaTestPlaylistUpdate_EmptyArrayMarksAllTracksRemoved: theplaylist_tracksjunction correctly marks all rowsis_removed=true.Adjacent gap found and fixed
apps'
process_playlist_contentsalways normalizes its output to the dict form{"track_ids": [...]}regardless of input shape. go-openaudio was persisting whatever shape the SDK sent (bare[]or legacy dict), which downstream readers wouldn't expect.This PR adds
normalizePlaylistContentsJSON:Wired into both create (
metadataPlaylistContentsJSON) and update (mergePlaylistFromMetadata).Stack context
Stacked on #252 (5E — oauth_redirect_uris). Follow-up after the main parity stack.
Test plan
TestNormalizePlaylistContentsJSON— 6 sub-cases covering all input shapes.TestPlaylistUpdate_EmptyArrayMarksAllTracksRemoved— both bare[](new SDK) and legacy{track_ids:[]}paths correctly clear the junction.TestPlaylistUpdate_EmptyArrayWritesJSONBColumn— asserts the persisted JSONB is a JSON object with atrack_idskey (apps' canonical form).🤖 Generated with Claude Code