feat(activity): integrate fetch with cache layer #298

Merged
fuzzy merged 1 commit from phase02-refresh into main 2026-07-07 09:26:02 +00:00
Owner

What

Add internal/activity/refresh.go with the orchestration layer that ties the fetcher and cache together, completing Phase 2.

RefreshContext()

The main orchestration function that:

  1. Loads the cached 7-day window from SQLite (GetActivityWindow)
  2. Checks staleness via GetRefreshWindow
  3. If stale/missing days exist, fetches events from the forge API with the computed since parameter
  4. Upserts each aggregated day to cache (UpsertDailyActivity)
  5. Returns the fresh window from cache

Error handling (#198)

  • If a fetch fails but cached data exists: returns cached data (stale but usable)
  • If a fetch fails and cache is empty: propagates the error
  • If all days are fresh: returns cached data immediately without API call

Test mocks

  • mockForgeClient — returns predefined feeds, supports since filtering
  • errClient — always returns network errors

References: #199, #196, #197, #198

Phase 2 is now fully complete. Next: Phase 3 (Sparkline Rendering).

Testing

  • 4 new tests: empty cache fetch, partial cache with fetch error, full cache with fetch error, no fallback data error
  • golangci-lint run ./internal/activity/ — 0 issues
  • go test ./... — all pass
## What Add `internal/activity/refresh.go` with the orchestration layer that ties the fetcher and cache together, completing Phase 2. ### RefreshContext() The main orchestration function that: 1. Loads the cached 7-day window from SQLite (`GetActivityWindow`) 2. Checks staleness via `GetRefreshWindow` 3. If stale/missing days exist, fetches events from the forge API with the computed `since` parameter 4. Upserts each aggregated day to cache (`UpsertDailyActivity`) 5. Returns the fresh window from cache ### Error handling (#198) - If a fetch fails but cached data exists: returns cached data (stale but usable) - If a fetch fails and cache is empty: propagates the error - If all days are fresh: returns cached data immediately without API call ### Test mocks - `mockForgeClient` — returns predefined feeds, supports `since` filtering - `errClient` — always returns network errors References: #199, #196, #197, #198 **Phase 2 is now fully complete.** Next: Phase 3 (Sparkline Rendering). ## Testing - [x] 4 new tests: empty cache fetch, partial cache with fetch error, full cache with fetch error, no fallback data error - [x] `golangci-lint run ./internal/activity/` — 0 issues - [x] `go test ./...` — all pass
feat(activity): integrate fetch with cache layer
All checks were successful
Test and Release / lint (pull_request) Successful in 3m49s
Test and Release / test (pull_request) Successful in 11m4s
21465c9e0f
- RefreshContext(): orchestrate full refresh cycle:
  1. Load cached 7-day window from SQLite
  2. Check staleness via GetRefreshWindow
  3. If stale/missing, fetch events from API with since param
  4. Upsert aggregated results to cache
  5. Return fresh window from cache
- Handle fetch errors gracefully: return cached data if available,
  propagate error only if cache is empty
- Add mock forge client with time-aware filtering
- 4 new tests: empty cache, partial fetch error, full fetch error,
  no fallback data

Ref: #199, #196, #197, #198

This completes Phase 2.
the.auditor left a comment

Review

Result: Approved — no blocking issues.

Summary: Adds internal/activity/refresh.go with RefreshContext(), the orchestration layer tying fetcher + cache together. Flow: get cached window → check staleness → if fresh return early → fetch events with computed since → upsert each day → re-query fresh window. Graceful degradation: fetch errors return stale cache if any data exists. ROADMAP2.md updated — Phase 2 fully complete.

Non-blocking:

  • #299 — Add godoc for exported RefreshContext

Checklist:

  • Correctness: Orchestration flow correct, error fallback logic sound
  • Completeness: Closes #199, #196, #197, #198 — Phase 2 integration complete
  • Test coverage: 4 tests (empty cache fetch, cached + fetch error, partial cache + fetch error, no fallback error)
  • Error handling: Fetch errors degrade gracefully when cache exists, propagate when empty; upsert errors propagate
  • Maintainability: Clean 38-line function, clear orchestration, well-structured mocks
## Review **Result: Approved** — no blocking issues. **Summary:** Adds `internal/activity/refresh.go` with `RefreshContext()`, the orchestration layer tying fetcher + cache together. Flow: get cached window → check staleness → if fresh return early → fetch events with computed since → upsert each day → re-query fresh window. Graceful degradation: fetch errors return stale cache if any data exists. ROADMAP2.md updated — Phase 2 fully complete. **Non-blocking:** - #299 — Add godoc for exported `RefreshContext` **Checklist:** - Correctness: ✅ Orchestration flow correct, error fallback logic sound - Completeness: ✅ Closes #199, #196, #197, #198 — Phase 2 integration complete - Test coverage: ✅ 4 tests (empty cache fetch, cached + fetch error, partial cache + fetch error, no fallback error) - Error handling: ✅ Fetch errors degrade gracefully when cache exists, propagate when empty; upsert errors propagate - Maintainability: ✅ Clean 38-line function, clear orchestration, well-structured mocks
fuzzy added this to the Steeper project 2026-07-07 09:16:57 +00:00
fuzzy self-assigned this 2026-07-07 09:17:00 +00:00
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-07-07 09:17:09 +00:00
fuzzy merged commit 21465c9e0f into main 2026-07-07 09:26:02 +00:00
fuzzy deleted branch phase02-refresh 2026-07-07 09:26:02 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/steeper!298
No description provided.