feat: fetch all artist albums with caching, filter by type in handler

- Provider fetches all release groups via paginated browse, no type filter
- Service caches all albums in DB, ensures referenced artists exist first
- Server handler filters by album_types (default: album|ep|single), excludes secondary types
- Add secondary_types to domain, DB schema, and MB types
- Add ErrBadRequest handling for MusicBrainz 400 errors
- Replace migrations with single schema.sql
- Add GetAllByArtistID and SaveAll to album repository
This commit is contained in:
Alexander
2026-05-07 20:03:04 +02:00
parent 35ac167952
commit 9f1318e79e
17 changed files with 302 additions and 172 deletions
+16 -9
View File
@@ -107,6 +107,12 @@ func (c *client) readResponse(ctx context.Context, resp *http.Response, endpoint
metrics.ProviderRequests.WithLabelValues("musicbrainz", endpoint, "ok").Inc()
return io.ReadAll(resp.Body)
case http.StatusBadRequest:
body, _ := io.ReadAll(resp.Body)
log.Warn().Str("endpoint", endpoint).Str("body", string(body)).Dur("duration", duration).Msg("HTTP 400 bad request")
metrics.ProviderRequests.WithLabelValues("musicbrainz", endpoint, "bad_request").Inc()
return nil, fmt.Errorf("%w: %s", ErrBadRequest, extractErrorMessage(body))
case http.StatusNotFound:
log.Debug().Str("endpoint", endpoint).Dur("duration", duration).Msg("HTTP 404")
metrics.ProviderRequests.WithLabelValues("musicbrainz", endpoint, "not_found").Inc()
@@ -137,17 +143,12 @@ func (c *client) lookup(ctx context.Context, entity, id string, inc []string) ([
}
func (c *client) browse(ctx context.Context, entity, linkedEntity, linkedID string, limit, offset int, inc []string) ([]byte, error) {
return c.browseWithTypes(ctx, entity, linkedEntity, linkedID, limit, offset, inc, nil)
}
func (c *client) browseWithTypes(ctx context.Context, entity, linkedEntity, linkedID string, limit, offset int, inc []string, types []string) ([]byte, error) {
zerolog.Ctx(ctx).Debug().
Str("entity", entity).
Str("linked_entity", linkedEntity).
Str("linked_id", linkedID).
Int("limit", limit).
Int("offset", offset).
Strs("types", types).
Msg("provider browse")
params := url.Values{}
@@ -159,10 +160,6 @@ func (c *client) browseWithTypes(ctx context.Context, entity, linkedEntity, link
params.Set("inc", joined)
}
if typeStr := strings.Join(types, "|"); typeStr != "" {
params.Set("type", typeStr)
}
return c.get(ctx, entity, params)
}
@@ -184,6 +181,16 @@ func joinIncludes(inc []string) string {
return strings.Join(inc, "+")
}
func extractErrorMessage(body []byte) string {
var errResp struct {
Error string `json:"error"`
}
if json.Unmarshal(body, &errResp) == nil && errResp.Error != "" {
return errResp.Error
}
return string(body)
}
func decode[T any](data []byte) (*T, error) {
var result T
if err := json.Unmarshal(data, &result); err != nil {