Skip to content

feat: Offline Progress Sync API (#51)#57

Merged
Kaylahray merged 2 commits into
learnault:mainfrom
nonsobethel0-dev:feat/offline-progress-sync-api
Apr 27, 2026
Merged

feat: Offline Progress Sync API (#51)#57
Kaylahray merged 2 commits into
learnault:mainfrom
nonsobethel0-dev:feat/offline-progress-sync-api

Conversation

@nonsobethel0-dev
Copy link
Copy Markdown
Contributor

Fixes #51

What changed

  • Added SyncEvent Prisma model tracking idempotencyKey, deviceId, clientTimestamp, serverTimestamp, syncVersion, status, and rejectionReason
  • POST /api/v1/sync/progress — accepts batched offline progress events; resolves conflicts by rejecting stale sync versions
  • POST /api/v1/sync/completions — reconciles offline quiz/completion attempts; updates score when higher, skips when equal or lower, creates new completion when none exists
  • Idempotency keys prevent duplicate processing of retried payloads
  • Per-item results returned: applied, skipped, or rejected with a reason
  • Both endpoints protected by JWT auth and rate-limited via authenticatedLimiter

Why

  • Enables offline-first learning by accepting locally cached progress once the user reconnects
  • Deterministic conflict resolution ensures data integrity without requiring client-server coordination

How to test

  • POST /api/v1/sync/progress with { "events": [{ "idempotencyKey": "...", "deviceId": "...", "moduleId": "...", "progressPercent": 60, "clientTimestamp": "...", "syncVersion": 1 }] } → should return applied
  • Retry the same payload → second call should return skipped
  • Send a payload with syncVersion lower than an already-stored event → should return skipped with stale version reason
  • POST /api/v1/sync/completions with valid events → applied if module exists and score is new or higher
  • Send completion for a module already completed with higher score → should return skipped
  • Send completion for non-existent module → should return rejected with reason

- Add SyncEvent Prisma model with idempotency key, deviceId, clientTimestamp, serverTimestamp, syncVersion, and per-item status
- POST /sync/progress — accepts batched offline progress events with conflict resolution and stale version detection
- POST /sync/completions — reconciles offline quiz attempts; updates score only when higher, creates new completion otherwise
- Idempotency keys prevent duplicate processing of retried requests
- Per-item results returned: applied, skipped, or rejected with reason
- Both endpoints are rate-limited via authenticatedLimiter
- Unit tests covering retry, conflict, partial success, and validation scenarios
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 27, 2026

@nonsobethel0-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Kaylahray Kaylahray merged commit 93dfcd4 into learnault:main Apr 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants