refactor(progress): ProgressService required at construction; delete the 9 unreachable erase-fallbacks

Batch H1 step 3a. The ceiling census (exhaustive UpdateUniversalProgress
grep) found 9 nil-guarded `if progressSvc != nil { funnel } else { raw
erase-write }` fallbacks — the survey's "four" under-counted kobo.go,
which has FOUR sites (Markup ReadingSync, Markup last-read-place,
AnalyticsGettests, SyncFromServer), plus one each in media.go, queue.go
and koreader.go. All unreachable: the service is created unconditionally
at main.go:127 and wired into every handler and the queue processor; the
test harness wires them too.

The erasure pattern is now unrepresentable:
- NewMediaHandler / NewKOReaderHandler / NewKoboHandler / both
  NewSyncQueueProcessor flavors take the ProgressService as a REQUIRED
  constructor arg; the SetProgressService setters are gone.
- Every fallback body (raw UpdateUniversalProgress with explicit-NULL
  pgtypes for viewport/scroll/panel/reading_mode/zoom and no
  audio_offset_seconds/context_text) is deleted; each write funnels
  through SaveProgress with the LWW seed, so fields the writer doesn't
  know survive.
- Removed the now-dead koreader pg-type helpers (textPtrToPgText,
  intPtrToPgInt4, int64PtrToPgInt8) and their self-tests.

Behavior deltas (stored-row content, no wire shape changes —
KOReader/Kobo response pins stayed green):
- KOReader fallback used to write explicit NULL viewports and drop the
  context_text the koplugin sends; the funneled mainline already maps
  book.ContextText.
- Queue flushes no longer overwrite chapter_progress with the book
  percentage (bogus data; preserved via the seed).
- A web PUT without percentage no longer writes 0% via the deleted
  fallback; percentage seeds from the stored row.

Contract suite: KOReader×4 + Kobo×3 pins green.
This commit is contained in:
John O'Keefe committed 2026-10-16 17:28:06 -04:00
1 parent 28bd323505
commit e83c00f869
9 files changed
+163 -419

No files matched your search

+50 -96
View File
@@ -26,12 +26,8 @@ type KoboHandler struct {
bookResolver *services.BookResolver
}
func NewKoboHandler(db *database.Queries, connManager *wsync.ConnectionManager) *KoboHandler {
return &KoboHandler{db: db, connManager: connManager, bookResolver: services.NewBookResolver(db)}
}
func (h *KoboHandler) SetProgressService(svc *wsync.ProgressService) {
h.progressSvc = svc
func NewKoboHandler(db *database.Queries, connManager *wsync.ConnectionManager, progressSvc *wsync.ProgressService) *KoboHandler {
return &KoboHandler{db: db, connManager: connManager, progressSvc: progressSvc, bookResolver: services.NewBookResolver(db)}
}
func (h *KoboHandler) SetAnnotationService(svc *wsync.AnnotationService) {
@@ -446,29 +442,19 @@ func (h *KoboHandler) Markup(c *echo.Context) error {
}
}
if h.progressSvc != nil {
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "kobo",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Percentage: &percentage,
CurrentPage: currentPage,
TotalPages: totalPages,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: true,
ClearLocator: true,
})
} else {
_, err = h.db.UpdateUniversalProgress(c.Request().Context(), database.UpdateUniversalProgressParams{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Percentage: pgtype.Float8{Float64: percentage, Valid: true},
LastSyncDevice: pgtype.Text{String: "kobo", Valid: true},
LastSyncSource: pgtype.Text{String: "kobo", Valid: true},
})
}
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "kobo",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Percentage: &percentage,
CurrentPage: currentPage,
TotalPages: totalPages,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: true,
ClearLocator: true,
})
if err == nil {
markupsSynced++
@@ -580,31 +566,19 @@ func (h *KoboHandler) Markup(c *echo.Context) error {
epubcfi = *convertedCFI
}
if h.progressSvc != nil {
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "kobo",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Epubcfi: &epubcfi,
ContextText: contextText,
Chapter: &chapter,
ChapterProgress: &chapterProgress,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: false,
})
} else {
_, err = h.db.UpdateUniversalProgress(c.Request().Context(), database.UpdateUniversalProgressParams{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Epubcfi: pgtype.Text{String: epubcfi, Valid: epubcfi != ""},
Chapter: pgtype.Int4{Int32: int32(bookmarkSync.Chapter), Valid: true},
ChapterProgress: pgtype.Float8{Float64: 0.5, Valid: true},
LastSyncDevice: pgtype.Text{String: "kobo", Valid: true},
LastSyncSource: pgtype.Text{String: "kobo", Valid: true},
})
}
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "kobo",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Epubcfi: &epubcfi,
ContextText: contextText,
Chapter: &chapter,
ChapterProgress: &chapterProgress,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: false,
})
if err != nil {
fmt.Printf("Failed to store last-read-place: %v", err)
}
@@ -797,27 +771,17 @@ func (h *KoboHandler) AnalyticsGettests(c *echo.Context) error {
pgMediaUUID := pgtype.UUID{Bytes: bookhoardUUID, Valid: true}
percentage := test.PercentRead / 100.0
if h.progressSvc != nil {
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "kobo",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Percentage: &percentage,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: true,
ClearLocator: true,
})
} else {
_, err = h.db.UpdateUniversalProgress(c.Request().Context(), database.UpdateUniversalProgressParams{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Percentage: pgtype.Float8{Float64: percentage, Valid: true},
LastSyncDevice: pgtype.Text{String: "kobo", Valid: true},
LastSyncSource: pgtype.Text{String: "kobo", Valid: true},
})
}
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "kobo",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Percentage: &percentage,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: true,
ClearLocator: true,
})
}
_, err := h.db.UpdateDeviceLastSync(c.Request().Context(), device.ID)
@@ -860,27 +824,17 @@ func (h *KoboHandler) SyncFromServer(c *echo.Context) error {
pgMediaUUID := pgtype.UUID{Bytes: bookhoardUUID, Valid: true}
percentage := syncData.PercentRead / 100.0
if h.progressSvc != nil {
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "bookhoard",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Percentage: &percentage,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: false,
ClearLocator: true,
})
} else {
_, err = h.db.UpdateUniversalProgress(c.Request().Context(), database.UpdateUniversalProgressParams{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Percentage: pgtype.Float8{Float64: percentage, Valid: true},
LastSyncDevice: pgtype.Text{String: "kobo", Valid: true},
LastSyncSource: pgtype.Text{String: "bookhoard", Valid: true},
})
}
_, err = h.progressSvc.SaveProgress(c.Request().Context(), wsync.SaveProgressRequest{
MediaItemID: pgMediaUUID,
UserID: pgUserID,
Source: "bookhoard",
DeviceID: pgtype.UUID{Bytes: deviceID, Valid: true},
Percentage: &percentage,
DeviceType: "kobo",
DeviceName: device.DeviceName,
Broadcast: false,
ClearLocator: true,
})
if err == nil {
booksSynced++