diff --git a/docs/development/troubleshoot.md b/docs/development/troubleshoot.md index f7b397ad3..801a1d3d7 100644 --- a/docs/development/troubleshoot.md +++ b/docs/development/troubleshoot.md @@ -51,3 +51,37 @@ To fix this: 2. Copy the exact connection URI and API key from your Supertokens dashboard 3. Verify the connection URI format matches what Supertokens provides (should include the protocol, domain, and port if applicable) 4. Ensure there are no extra spaces or characters in the environment variable values + +## Google Calendar Repair Fails Or Stops Unexpectedly + +When a user triggers repair from the UI (`Repair Google Calendar`) and the flow +does not complete as expected, first classify the failure mode from the message +and websocket behavior. + +### Quota / rate-limit during repair + +If the user sees: + +- `Google Calendar repair hit a Google API limit. Please wait a few minutes and try again.` + +then backend detected a Google quota/rate-limit response (`403`/`429`), and this +is usually transient. + +Recommended action: + +1. wait a few minutes +2. retry repair +3. if this repeats across many users, investigate Google API quota usage for the project + +### Revoked token during repair + +If access was revoked (for example Google returns `invalid_grant`), backend +prunes Google data and emits `GOOGLE_REVOKED`. + +Operational notes: + +- this path does **not** end with an `IMPORT_GCAL_END` payload with + `status: "ERRORED"` +- UI should transition to reconnect-required behavior rather than repeatedly + retrying repair +- user action is to reconnect Google, then allow sync/import restart diff --git a/docs/features/google-sync-and-websocket-flow.md b/docs/features/google-sync-and-websocket-flow.md index cd489aa5c..0fc1d7598 100644 --- a/docs/features/google-sync-and-websocket-flow.md +++ b/docs/features/google-sync-and-websocket-flow.md @@ -20,6 +20,41 @@ Important server-to-client events: - `IMPORT_GCAL_END` - `GOOGLE_REVOKED` +### `IMPORT_GCAL_END` Payload Contract + +Source files: + +- `packages/core/src/types/websocket.types.ts` +- `packages/backend/src/user/services/user.service.ts` +- `packages/backend/src/sync/services/sync.service.ts` + +`IMPORT_GCAL_END` now carries an explicit `operation` so the client can distinguish +repair completion from incremental completion. + +```ts +type ImportGCalOperation = "INCREMENTAL" | "REPAIR"; + +type ImportGCalEndPayload = + | { + operation: ImportGCalOperation; + status: "COMPLETED"; + eventsCount?: number; + calendarsCount?: number; + } + | { + operation: ImportGCalOperation; + status: "ERRORED" | "IGNORED"; + message: string; + }; +``` + +Operational constraints: + +- repair path (`restartGoogleCalendarSync`) emits `operation: "REPAIR"` +- incremental path (`importIncremental`) emits `operation: "INCREMENTAL"` +- web listeners should keep a defensive `payload?` handler for compatibility with + older emitters/tests + ## Outbound Flow: User Changes An Event In Compass High-level path: @@ -83,6 +118,10 @@ Important error handling behavior: - response is `204` either way - invalid/revoked Google token (`invalid_grant`): - backend prunes Google data, emits `GOOGLE_REVOKED`, returns revoked payload +- invalid/revoked token during repair flow: + - backend repair catches `invalid_grant`, prunes Google data, emits + `GOOGLE_REVOKED`, and exits repair without sending `IMPORT_GCAL_END` with + `status: "ERRORED"` ## Watch And Sync Records @@ -205,6 +244,10 @@ The client: - handles Google revocation - requests `USER_METADATA` to drive connection-state UI and import restart decisions - auto-starts import only when metadata says `sync.importGCal === "RESTART"` and the Google connection is usable +- tracks a dedicated repair-in-progress state (`isRepairing`) separate from + background incremental sync (`isProcessing`) +- only shows a toast for repair failures, and de-duplicates it with a stable toast id + (`GOOGLE_REPAIR_FAILED_TOAST_ID`) ## Revoked Token And Reconnect Lifecycle @@ -342,6 +385,57 @@ Google import progress is also realtime: 4. backend completes import and emits `IMPORT_GCAL_END` 5. client stores import results and triggers a refetch +### Operation-Aware Import Completion Semantics + +Source files: + +- `packages/web/src/socket/hooks/useGcalSync.ts` +- `packages/web/src/auth/hooks/oauth/useConnectGoogle.ts` +- `packages/web/src/ducks/events/slices/sync.slice.ts` + +`IMPORT_GCAL_END` handling is operation-aware: + +- `operation: "REPAIR"` + - always stops repair spinner/state + - `ERRORED`: + - stores `importError` + - shows one repair-failure toast (de-duped by id) + - `COMPLETED`: + - stores import counts in `importResults` + - triggers an events refetch + - `IGNORED`: no refetch +- `operation: "INCREMENTAL"` + - does **not** alter repair spinner/state + - `ERRORED`: + - stores `importError` + - does not show repair-failure toast + - `COMPLETED`: stores counts (if provided) and refetches + +Examples: + +```json +{ "operation": "REPAIR", "status": "COMPLETED", "eventsCount": 42, "calendarsCount": 3 } +``` + +```json +{ "operation": "INCREMENTAL", "status": "ERRORED", "message": "Incremental Google Calendar sync failed for user: 123" } +``` + +### Repair Failure Messaging + +Source files: + +- `packages/backend/src/common/services/gcal/gcal.utils.ts` +- `packages/backend/src/common/errors/integration/gcal/gcal.errors.ts` +- `packages/backend/src/user/services/user.service.ts` + +Repair failures now normalize to two user-facing message classes: + +- quota/rate-limit errors from Google (`403`/`429` with quota-style reasons or messages): + - `"Google Calendar repair hit a Google API limit. Please wait a few minutes and try again."` +- all other repair failures: + - `"Google Calendar repair failed. Please try again."` + ### Manual Import Trigger Contract Source files: