refactor(conflicts): one ConflictDetail builder for the three row shapes
ListConflicts, GetConflict, and the SSR media-detail active-conflict lookup each hand-mapped their sqlc row (list-by-user, get-by-id, list-by-media-item) into ConflictDetailResponse, triplicating the optional resolution tail — including the silently-swallowed ResolutionData unmarshal error, three times. NewConflictDetail is the single mapper; a malformed resolution blob now logs instead of vanishing. Also extracts koreaderSyncEndpoints (the KOReader URL map duplicated verbatim between token rotation and device approval; kobo's two URL sets stay local because they deliberately differ). Verified in the test-runner container (host conflict tests fail environmentally on the container-only uploads path — baseline behavior).
This commit is contained in:
1 parent
58fbe372e2
commit
b47a2b86a7
3 files changed
+62
-55
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -103,26 +104,10 @@ func (h *ConflictHandler) GetConflictsData(c *echo.Context) ([]ConflictDetailRes
|
||||
continue
|
||||
}
|
||||
|
||||
detail := ConflictDetailResponse{
|
||||
ID: uuid.UUID(conflict.ID.Bytes).String(),
|
||||
MediaItemID: uuid.UUID(conflict.MediaItemID.Bytes).String(),
|
||||
MediaItemTitle: conflict.Title,
|
||||
ConflictType: conflict.ConflictType,
|
||||
ConflictData: conflictData,
|
||||
ResolutionStatus: conflict.ResolutionStatus.String,
|
||||
CreatedAt: conflict.CreatedAt.Time,
|
||||
}
|
||||
|
||||
if conflict.ResolvedBy.Valid {
|
||||
detail.ResolvedBy = uuid.UUID(conflict.ResolvedBy.Bytes).String()
|
||||
}
|
||||
if conflict.ResolvedAt.Valid {
|
||||
detail.ResolvedAt = &conflict.ResolvedAt.Time
|
||||
}
|
||||
if conflict.ResolutionData != nil {
|
||||
if err := json.Unmarshal(conflict.ResolutionData, &detail.ResolutionData); err == nil {
|
||||
}
|
||||
}
|
||||
detail := NewConflictDetail(conflict.ID, conflict.MediaItemID,
|
||||
conflict.Title, conflict.ConflictType, conflict.ResolutionStatus.String,
|
||||
conflictData, conflict.CreatedAt, conflict.ResolvedAt, conflict.ResolvedBy,
|
||||
conflict.ResolutionData)
|
||||
|
||||
response = append(response, detail)
|
||||
if conflict.ResolutionStatus.String == "unresolved" {
|
||||
@@ -133,6 +118,41 @@ func (h *ConflictHandler) GetConflictsData(c *echo.Context) ([]ConflictDetailRes
|
||||
return response, len(response), unresolvedCount, nil
|
||||
}
|
||||
|
||||
// NewConflictDetail maps any sync-conflict row shape (list-by-user,
|
||||
// get-by-id, list-by-media-item) to the wire detail. The resolution
|
||||
// tail is conditional exactly as before; a malformed resolution blob
|
||||
// now logs instead of vanishing silently.
|
||||
func NewConflictDetail(
|
||||
id, mediaItemID pgtype.UUID,
|
||||
title, conflictType, resolutionStatus string,
|
||||
conflictData map[string]ConflictSourceData,
|
||||
createdAt, resolvedAt pgtype.Timestamptz,
|
||||
resolvedBy pgtype.UUID,
|
||||
resolutionData []byte,
|
||||
) ConflictDetailResponse {
|
||||
detail := ConflictDetailResponse{
|
||||
ID: uuid.UUID(id.Bytes).String(),
|
||||
MediaItemID: uuid.UUID(mediaItemID.Bytes).String(),
|
||||
MediaItemTitle: title,
|
||||
ConflictType: conflictType,
|
||||
ConflictData: conflictData,
|
||||
ResolutionStatus: resolutionStatus,
|
||||
CreatedAt: createdAt.Time,
|
||||
}
|
||||
if resolvedBy.Valid {
|
||||
detail.ResolvedBy = uuid.UUID(resolvedBy.Bytes).String()
|
||||
}
|
||||
if resolvedAt.Valid {
|
||||
detail.ResolvedAt = &resolvedAt.Time
|
||||
}
|
||||
if resolutionData != nil {
|
||||
if err := json.Unmarshal(resolutionData, &detail.ResolutionData); err != nil {
|
||||
log.Printf("conflict %s: unparsable resolution data: %v", detail.ID, err)
|
||||
}
|
||||
}
|
||||
return detail
|
||||
}
|
||||
|
||||
func (h *ConflictHandler) ListConflicts(c *echo.Context) error {
|
||||
conflicts, total, unresolved, err := h.GetConflictsData(c)
|
||||
if err != nil {
|
||||
@@ -177,26 +197,10 @@ func (h *ConflictHandler) GetConflict(c *echo.Context) error {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "failed to parse conflict data")
|
||||
}
|
||||
|
||||
detail := ConflictDetailResponse{
|
||||
ID: uuid.UUID(conflict.ID.Bytes).String(),
|
||||
MediaItemID: uuid.UUID(conflict.MediaItemID.Bytes).String(),
|
||||
MediaItemTitle: mediaItem.Title,
|
||||
ConflictType: conflict.ConflictType,
|
||||
ConflictData: conflictData,
|
||||
ResolutionStatus: conflict.ResolutionStatus.String,
|
||||
CreatedAt: conflict.CreatedAt.Time,
|
||||
}
|
||||
|
||||
if conflict.ResolvedBy.Valid {
|
||||
detail.ResolvedBy = uuid.UUID(conflict.ResolvedBy.Bytes).String()
|
||||
}
|
||||
if conflict.ResolvedAt.Valid {
|
||||
detail.ResolvedAt = &conflict.ResolvedAt.Time
|
||||
}
|
||||
if conflict.ResolutionData != nil {
|
||||
if err := json.Unmarshal(conflict.ResolutionData, &detail.ResolutionData); err == nil {
|
||||
}
|
||||
}
|
||||
detail := NewConflictDetail(conflict.ID, conflict.MediaItemID,
|
||||
mediaItem.Title, conflict.ConflictType, conflict.ResolutionStatus.String,
|
||||
conflictData, conflict.CreatedAt, conflict.ResolvedAt, conflict.ResolvedBy,
|
||||
conflict.ResolutionData)
|
||||
|
||||
return c.JSON(http.StatusOK, detail)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user