Skip to content

Commit d5b2c04

Browse files
authored
feat(sync): enhance Google Calendar sync process with forced restart option (#1425)
- Updated `restartGoogleCalendarSync` method to accept an options parameter for forced synchronization. - Modified error handling in `SyncController` and `error.express.handler` to utilize the new forced restart functionality. - Enhanced tests to cover scenarios for both forced and non-forced sync operations, ensuring correct behavior based on user import status. This change improves the flexibility of the Google Calendar synchronization process, allowing for more robust handling of sync operations in various states.
1 parent 30a5a1d commit d5b2c04

5 files changed

Lines changed: 81 additions & 16 deletions

File tree

packages/backend/src/common/errors/handlers/error.express.handler.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,14 @@ const handleGoogleError = async (
119119
}
120120

121121
if (isFullSyncRequired(e)) {
122-
userService.restartGoogleCalendarSync(userId).catch((err) => {
123-
logger.error(
124-
`Something went wrong with resyncing google calendars for user: ${userId}`,
125-
err,
126-
);
127-
});
122+
userService
123+
.restartGoogleCalendarSync(userId, { force: true })
124+
.catch((err) => {
125+
logger.error(
126+
`Something went wrong with resyncing google calendars for user: ${userId}`,
127+
err,
128+
);
129+
});
128130

129131
res.status(Status.BAD_REQUEST).send({ message: "Full sync in progress." });
130132

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,14 @@ export class SyncController {
105105
return;
106106
} else if (isFullSyncRequired(e as Error) && userId) {
107107
// do not await this call
108-
userService.restartGoogleCalendarSync(userId).catch((err) => {
109-
logger.error(
110-
`Something went wrong with resyncing google calendars for user: ${userId}`,
111-
err,
112-
);
113-
});
108+
userService
109+
.restartGoogleCalendarSync(userId, { force: true })
110+
.catch((err) => {
111+
logger.error(
112+
`Something went wrong with resyncing google calendars for user: ${userId}`,
113+
err,
114+
);
115+
});
114116

115117
res.status(Status.OK).send({ message: "Full sync in progress." });
116118

packages/backend/src/sync/services/maintain/sync.maintenance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export const refreshWatch = async (
142142
};
143143
} catch (e) {
144144
if (isFullSyncRequired(e as Error)) {
145-
userService.restartGoogleCalendarSync(r.user);
145+
userService.restartGoogleCalendarSync(r.user, { force: true });
146146
resynced = true;
147147
} else {
148148
logger.error(

packages/backend/src/user/services/user.service.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,58 @@ describe("UserService", () => {
223223
const calendars = await calendarService.getByUser(userId);
224224
expect(calendars.length).toBeGreaterThan(0);
225225
});
226+
227+
it("skips restart when import is completed and not forced", async () => {
228+
const { user } = await UtilDriver.setupTestUser();
229+
const userId = user._id.toString();
230+
231+
await userMetadataService.updateUserMetadata({
232+
userId,
233+
data: { sync: { importGCal: "completed" } },
234+
});
235+
236+
const stopSpy = jest.spyOn(userService, "stopGoogleCalendarSync");
237+
const startSpy = jest.spyOn(userService, "startGoogleCalendarSync");
238+
239+
await userService.restartGoogleCalendarSync(userId);
240+
241+
expect(stopSpy).not.toHaveBeenCalled();
242+
expect(startSpy).not.toHaveBeenCalled();
243+
244+
const metadata = await userMetadataService.fetchUserMetadata(userId);
245+
expect(metadata.sync?.importGCal).toBe("completed");
246+
247+
stopSpy.mockRestore();
248+
startSpy.mockRestore();
249+
});
250+
251+
it("forces restart when import is completed", async () => {
252+
const { user } = await UtilDriver.setupTestUser();
253+
const userId = user._id.toString();
254+
255+
await userMetadataService.updateUserMetadata({
256+
userId,
257+
data: { sync: { importGCal: "completed" } },
258+
});
259+
260+
const stopSpy = jest
261+
.spyOn(userService, "stopGoogleCalendarSync")
262+
.mockResolvedValue();
263+
const startSpy = jest
264+
.spyOn(userService, "startGoogleCalendarSync")
265+
.mockResolvedValue({ eventsCount: 0, calendarsCount: 0 });
266+
267+
await userService.restartGoogleCalendarSync(userId, { force: true });
268+
269+
expect(stopSpy).toHaveBeenCalledWith(userId);
270+
expect(startSpy).toHaveBeenCalledWith(userId);
271+
272+
const metadata = await userMetadataService.fetchUserMetadata(userId);
273+
expect(metadata.sync?.importGCal).toBe("completed");
274+
275+
stopSpy.mockRestore();
276+
startSpy.mockRestore();
277+
});
226278
});
227279

228280
describe("updateUserMetadata", () => {

packages/backend/src/user/services/user.service.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,23 @@ class UserService {
212212
};
213213
};
214214

215-
restartGoogleCalendarSync = async (userId: string) => {
216-
logger.warn(`Restarting Google Calendar sync for user: ${userId}`);
215+
restartGoogleCalendarSync = async (
216+
userId: string,
217+
options: { force?: boolean } = {},
218+
) => {
219+
const isForce = options.force === true;
220+
221+
logger.warn(
222+
`Restarting Google Calendar sync for user: ${userId}${isForce ? " (forced)" : ""}`,
223+
);
217224

218225
try {
219226
webSocketServer.handleImportGCalStart(userId);
220227

221228
const userMeta = await userMetadataService.fetchUserMetadata(userId);
222-
const proceed = shouldImportGCal(userMeta);
229+
const importStatus = userMeta.sync?.importGCal;
230+
const isImporting = importStatus === "importing";
231+
const proceed = isForce ? !isImporting : shouldImportGCal(userMeta);
223232

224233
if (!proceed) {
225234
webSocketServer.handleImportGCalEnd(

0 commit comments

Comments
 (0)