Skip to content

Commit e8c53ad

Browse files
committed
🐛 fix(sign-up-import-gcal): prevent sync data from being initialized multiple times
1 parent 20e4044 commit e8c53ad

File tree

4 files changed

+43
-19
lines changed

4 files changed

+43
-19
lines changed

packages/backend/src/sync/controllers/sync.controller.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Request, Response } from "express";
22
import { Status } from "@core/errors/status.codes";
33
import { Logger } from "@core/logger/winston.logger";
44
import { Payload_Sync_Notif } from "@core/types/sync.types";
5+
import { shouldImportGCal } from "@core/util/event/event.util";
56
import { getGcalClient } from "@backend/auth/services/google.auth.service";
67
import { SyncError } from "@backend/common/errors/sync/sync.errors";
78
import { UserError } from "@backend/common/errors/user/user.errors";
@@ -96,17 +97,24 @@ class SyncController {
9697
importGCal = async (req: Request, res: Response): Promise<Response> => {
9798
const userId = req.session!.getUserId()!;
9899
const gcalClient = await getGcalClient(userId);
99-
const gCalendarIds = await initSync(gcalClient, userId);
100-
const lastGCalSync = new Date().toUTCString();
100+
const sync = await getSync({ userId });
101+
102+
let gCalendarIds: string[] =
103+
sync?.google.calendarlist.map((item) => item.gCalendarId) ?? [];
104+
105+
if (!sync) gCalendarIds = await initSync(gcalClient, userId);
106+
107+
logger.debug(
108+
`starting gCal(${gCalendarIds.toString()}) imports for user(${userId})`,
109+
);
101110

102111
webSocketServer.handleImportGCalStart(userId);
103112

104113
userService
105114
.fetchUserMetadata(userId)
106-
.then(async (metadata) => {
107-
const sync = metadata.sync ?? {};
108-
109-
if (sync?.importGCal?.importing) {
115+
.then(shouldImportGCal)
116+
.then(async (proceed) => {
117+
if (!proceed) {
110118
webSocketServer.handleImportGCalEnd(
111119
userId,
112120
`User ${userId} is already importing GCal, ignoring this request`,
@@ -117,22 +125,27 @@ class SyncController {
117125

118126
await userService.updateUserMetadata({
119127
userId,
120-
data: { sync: { importGCal: { importing: true, lastGCalSync } } },
128+
data: { sync: { importGCal: "importing" } },
121129
});
122130

123131
await syncService.importFull(gcalClient, gCalendarIds, userId);
124132

125133
await userService.updateUserMetadata({
126134
userId,
127-
data: { sync: { importGCal: { importing: false } } },
135+
data: { sync: { importGCal: "completed" } },
128136
});
129137

130138
webSocketServer.handleImportGCalEnd(userId);
131139
webSocketServer.handleBackgroundCalendarChange(userId);
132140
})
133-
.catch((err) => {
141+
.catch(async (err) => {
134142
const message = `Import gCal failed for user: ${userId}`;
135143

144+
await userService.updateUserMetadata({
145+
userId,
146+
data: { sync: { importGCal: "errored" } },
147+
});
148+
136149
webSocketServer.handleImportGCalEnd(userId, message);
137150

138151
logger.error(message, err);

packages/core/src/types/user.types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,5 @@ export interface Schema_User {
1616
}
1717

1818
export interface UserMetadata extends SupertokensUserMetadata.JSONObject {
19-
sync?: {
20-
importGCal?: {
21-
importing?: boolean; // gCal import in progress
22-
lastGCalSync?: string; // utc Date string
23-
};
24-
};
19+
sync?: { importGCal?: "importing" | "errored" | "completed" | "restart" };
2520
}

packages/core/src/util/event/event.util.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Schema_Event_Recur_Base,
55
Schema_Event_Recur_Instance,
66
} from "@core/types/event.types";
7+
import { UserMetadata } from "@core/types/user.types";
78

89
/** Event utilities for Compass events */
910

@@ -83,3 +84,17 @@ export const filterBaseEvents = (e: Schema_Event[]) => {
8384
*/
8485
export const filterExistingInstances = (e: Schema_Event[]) =>
8586
e.filter(isExistingInstance);
87+
88+
export const shouldImportGCal = (metadata: UserMetadata): boolean => {
89+
const sync = metadata.sync;
90+
91+
switch (sync?.importGCal) {
92+
case "importing":
93+
case "completed":
94+
return false;
95+
case "restart":
96+
case "errored":
97+
default:
98+
return true;
99+
}
100+
};

packages/web/src/socket/SocketProvider.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
USER_SIGN_OUT,
1212
} from "@core/constants/websocket.constants";
1313
import { UserMetadata } from "@core/types/user.types";
14+
import { shouldImportGCal } from "@core/util/event/event.util";
1415
import { ENV_WEB } from "@web/common/constants/env.constants";
1516
import { Sync_AsyncStateContextReason } from "@web/ducks/events/context/sync.context";
1617
import {
@@ -98,12 +99,12 @@ const SocketProvider = ({ children }: { children: ReactNode }) => {
9899
}, [dispatch]);
99100

100101
const onMetadataFetch = useCallback(
101-
(data: UserMetadata) => {
102-
const { importing, lastGCalSync } = data.sync?.importGCal ?? {};
102+
(metadata: UserMetadata) => {
103+
const importGcal = shouldImportGCal(metadata);
103104

104-
onImportStart(importing);
105+
onImportStart(metadata.sync?.importGCal === "importing");
105106

106-
if (!importing && !lastGCalSync) {
107+
if (importGcal) {
107108
dispatch(importGCalSlice.actions.request(undefined));
108109
}
109110
},

0 commit comments

Comments
 (0)