diff --git a/packages/backend/src/event/classes/gcal.event.parser.ts b/packages/backend/src/event/classes/gcal.event.parser.ts index b7910cbe4..c2c4ba8ca 100644 --- a/packages/backend/src/event/classes/gcal.event.parser.ts +++ b/packages/backend/src/event/classes/gcal.event.parser.ts @@ -281,6 +281,29 @@ export class GcalEventParser { return [...seriesChanges, ...baseChanges]; } + async instanceToStandalone( + session?: ClientSession, + ): Promise { + const event = gEventToCompassEvent(this.#event, this.userId); + + const { + recurrence, // eslint-disable-line @typescript-eslint/no-unused-vars + gRecurringEventId, // eslint-disable-line @typescript-eslint/no-unused-vars + ...eventWithoutProps + } = event; + + return this.upsertCompassEvent( + { + $unset: { recurrence: 1, gRecurringEventId: 1 }, + $set: { + ...eventWithoutProps, + updatedAt: new Date(), + }, + }, + session, + ); + } + async standaloneToSeries(session?: ClientSession) { this.#logger.info( `UPDATING ${this.getTransitionString()}: ${this.#event.id} to series`, diff --git a/packages/backend/src/sync/services/sync/__tests__/gcal.sync.processor.upsert.instance.test.ts b/packages/backend/src/sync/services/sync/__tests__/gcal.sync.processor.upsert.instance.test.ts index 96d2d890e..0a902987b 100644 --- a/packages/backend/src/sync/services/sync/__tests__/gcal.sync.processor.upsert.instance.test.ts +++ b/packages/backend/src/sync/services/sync/__tests__/gcal.sync.processor.upsert.instance.test.ts @@ -1,4 +1,5 @@ import { Categories_Recurrence } from "@core/types/event.types"; +import { gSchema$Event } from "@core/types/gcal"; import { UtilDriver } from "@backend/__tests__/drivers/util.driver"; import { cleanupCollections, @@ -60,6 +61,46 @@ describe("GcalSyncProcessor UPSERT: INSTANCE", () => { expect(updatedInstance?.title).toEqual(instanceTitle); }); + it("should handle DETACHING an INSTANCE into a STANDALONE event", async () => { + const { user } = await UtilDriver.setupTestUser(); + + const { gcalEvents } = await simulateDbAfterGcalImport(user._id.toString()); + + const origInstance = gcalEvents.instances[0]; + + const standalone = { + ...origInstance, + summary: "Detached Instance Event", + } as gSchema$Event; + + delete (standalone as { recurringEventId?: string }).recurringEventId; + delete (standalone as { recurrence?: string[] }).recurrence; + + const processor = new GcalSyncProcessor(user._id.toString()); + const changes = await processor.processEvents([standalone]); + + expect(changes).toHaveLength(1); + expect(changes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: standalone.summary, + category: Categories_Recurrence.RECURRENCE_INSTANCE, + transition: ["RECURRENCE_INSTANCE", "STANDALONE_CONFIRMED"], + operation: "RECURRENCE_INSTANCE_UPDATED", + }), + ]), + ); + + const updatedStandalone = await mongoService.event.findOne({ + gEventId: standalone.id!, + user: user._id.toString(), + }); + + expect(updatedStandalone).toBeDefined(); + expect(updatedStandalone?.gRecurringEventId).toBeUndefined(); + expect(updatedStandalone?.recurrence).toBeUndefined(); + }); + it("should handle UPDATING a REGULAR, BASE and TIMED INSTANCE", async () => { const { user } = await UtilDriver.setupTestUser(); diff --git a/packages/backend/src/sync/services/sync/gcal.sync.processor.ts b/packages/backend/src/sync/services/sync/gcal.sync.processor.ts index ff2c2dead..b2fafc831 100644 --- a/packages/backend/src/sync/services/sync/gcal.sync.processor.ts +++ b/packages/backend/src/sync/services/sync/gcal.sync.processor.ts @@ -80,6 +80,8 @@ export class GcalSyncProcessor { return parser.createSeries(session); case "STANDALONE->>RECURRENCE_BASE_CONFIRMED": return parser.standaloneToSeries(session); + case "RECURRENCE_INSTANCE->>STANDALONE_CONFIRMED": + return parser.instanceToStandalone(session); case "RECURRENCE_BASE->>STANDALONE_CONFIRMED": return parser.seriesToStandalone(session); case "RECURRENCE_INSTANCE->>RECURRENCE_BASE_CONFIRMED":