fix(server,flutter): sync wire format + systemVariant column (#357)
Two fixes in one commit because they're entangled — the systemVariant work would have been theater otherwise. ## The wire-format bug /api/library/sync was emitting PascalCase JSON for artist / album / track / playlist upserts (raw json.Marshal of sqlc-generated structs with no JSON tags — sqlc.yaml: emit_json_tags=false). Flutter's sync_controller _*FromJson reads snake_case keys, so all metadata sync rows landed in drift with empty strings / zero ints. The like_track / like_album / like_artist / playlist_track entities work because they're hand-built `map[string]string` payloads with snake_case keys — they sidestepped the bug. The 4 raw-marshal entities did not. Existing sync test caught zero of this — it asserts on len(upserts) not field shape. Fix: server-side view structs in library_sync_views.go with proper JSON tags + pgtype-flattening (UUID → 8-4-4-4-12 hex string, Date → "2006-01-02"). Mirrors the playlistRowView pattern from /api/playlists. New library_sync_views_test.go pins the wire keys so future field-name drift breaks loud. ## systemVariant column (closes #357 plan C v1 limitation) playlistSyncView now carries `system_variant` server → wire. Flutter drift schema bumped from 1 → 2 with onUpgrade adding the `systemVariant TEXT NULL` column to cached_playlists. Cursor reset to 0 in the migration so existing rows refresh with the new field on the next sync. playlistsListProvider now filters locally by systemVariant: - kind='user' → systemVariant IS NULL (the add-to-playlist sheet's intent) - kind='system' → systemVariant IS NOT NULL - kind='all' → no filter Closes the documented v1 limitation where the add-to-playlist sheet showed system playlists alongside user-created ones. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,7 +141,7 @@ func (h *handlers) hydrateUpserts(
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range artists {
|
||||
b, _ := json.Marshal(a)
|
||||
b, _ := json.Marshal(toArtistSyncView(a))
|
||||
out["artist"] = append(out["artist"], b)
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ func (h *handlers) hydrateUpserts(
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range albums {
|
||||
b, _ := json.Marshal(a)
|
||||
b, _ := json.Marshal(toAlbumSyncView(a))
|
||||
out["album"] = append(out["album"], b)
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func (h *handlers) hydrateUpserts(
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tracks {
|
||||
b, _ := json.Marshal(t)
|
||||
b, _ := json.Marshal(toTrackSyncView(t))
|
||||
out["track"] = append(out["track"], b)
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ func (h *handlers) hydrateUpserts(
|
||||
if syncpkg.FormatUUID(p.UserID) != userIDStr && !p.IsPublic {
|
||||
continue
|
||||
}
|
||||
b, _ := json.Marshal(p)
|
||||
b, _ := json.Marshal(toPlaylistSyncView(p))
|
||||
out["playlist"] = append(out["playlist"], b)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user