From f9fcaef8551c8ba362a3de19dba077ae3b7c7558 Mon Sep 17 00:00:00 2001 From: VincentHan12 Date: Thu, 29 Jan 2026 22:30:23 +0000 Subject: [PATCH 1/3] fix: status change no longer affects edited status --- .../modules/journey/journey.resolver.spec.ts | 206 +++++++++++++++--- .../app/modules/journey/journey.resolver.ts | 61 ++++-- 2 files changed, 218 insertions(+), 49 deletions(-) diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts index 511482509c8..eba53e42c57 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts @@ -2368,65 +2368,205 @@ describe('JourneyResolver', () => { describe('journeysArchive', () => { it('archives an array of journeys', async () => { - prismaService.journey.findMany.mockResolvedValueOnce([journey]) - expect( - await resolver.journeysArchive(accessibleJourneys, ['journeyId']) - ).toEqual([journey]) - expect(prismaService.journey.updateMany).toHaveBeenCalledWith({ - where: { AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] }, - data: { status: JourneyStatus.archived, archivedAt: new Date() } - }) + const journey2 = { + ...journey, + id: 'journey2Id', + updatedAt: new Date('2025-11-19T12:34:56.647Z') + } + prismaService.journey.findMany.mockResolvedValueOnce([journey, journey2]) + await resolver.journeysArchive(accessibleJourneys, [ + 'journeyId', + 'journey2Id' + ]) expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] } + where: { + AND: [accessibleJourneys, { id: { in: ['journeyId', 'journey2Id'] } }] + } }) + expect(prismaService.journey.update).toHaveBeenCalledTimes(2) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { + where: { id: 'journeyId' }, + data: { + status: JourneyStatus.archived, + archivedAt: new Date(), + updatedAt: journey.updatedAt + } + }) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { + where: { id: 'journey2Id' }, + data: { + status: JourneyStatus.archived, + archivedAt: new Date(), + updatedAt: journey2.updatedAt + } + }) + }) + + it('keeps updatedAt the same when archiving a journey', async () => { + const originalUpdatedAt = new Date('2021-11-19T12:34:56.647Z') + const newJourney = { ...journey, updatedAt: originalUpdatedAt } + prismaService.journey.findMany.mockResolvedValueOnce([newJourney]) + await resolver.journeysArchive(accessibleJourneys, ['journeyId']) + expect(prismaService.journey.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + updatedAt: originalUpdatedAt + }) + }) + ) }) }) describe('journeysDelete', () => { it('deletes an array of journeys', async () => { - prismaService.journey.findMany.mockResolvedValueOnce([journey]) - expect( - await resolver.journeysDelete(accessibleJourneys, ['journeyId']) - ).toEqual([journey]) - expect(prismaService.journey.updateMany).toHaveBeenCalledWith({ - where: { AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] }, - data: { status: JourneyStatus.deleted, deletedAt: new Date() } - }) + const journey2 = { + ...journey, + id: 'journey2Id', + updatedAt: new Date('2025-11-19T12:34:56.647Z') + } + prismaService.journey.findMany.mockResolvedValueOnce([journey, journey2]) + await resolver.journeysDelete(accessibleJourneys, [ + 'journeyId', + 'journey2Id' + ]) expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] } + where: { + AND: [accessibleJourneys, { id: { in: ['journeyId', 'journey2Id'] } }] + } }) + expect(prismaService.journey.update).toHaveBeenCalledTimes(2) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { + where: { id: 'journeyId' }, + data: { + status: JourneyStatus.deleted, + deletedAt: new Date(), + updatedAt: journey.updatedAt + } + }) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { + where: { id: 'journey2Id' }, + data: { + status: JourneyStatus.deleted, + deletedAt: new Date(), + updatedAt: journey2.updatedAt + } + }) + }) + + it('keeps updatedAt the same when deleting a journey', async () => { + const originalUpdatedAt = new Date('2021-11-19T12:34:56.647Z') + const newJourney = { ...journey, updatedAt: originalUpdatedAt } + prismaService.journey.findMany.mockResolvedValueOnce([newJourney]) + await resolver.journeysDelete(accessibleJourneys, ['journeyId']) + expect(prismaService.journey.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + updatedAt: originalUpdatedAt + }) + }) + ) }) }) describe('journeysTrash', () => { it('trashes an array of journeys', async () => { - prismaService.journey.findMany.mockResolvedValueOnce([journey]) - expect( - await resolver.journeysTrash(accessibleJourneys, ['journeyId']) - ).toEqual([journey]) - expect(prismaService.journey.updateMany).toHaveBeenCalledWith({ - where: { AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] }, - data: { status: JourneyStatus.trashed, trashedAt: new Date() } - }) + const journey2 = { + ...journey, + id: 'journey2Id', + updatedAt: new Date('2025-11-19T12:34:56.647Z') + } + prismaService.journey.findMany.mockResolvedValueOnce([journey, journey2]) + await resolver.journeysTrash(accessibleJourneys, [ + 'journeyId', + 'journey2Id' + ]) expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] } + where: { + AND: [accessibleJourneys, { id: { in: ['journeyId', 'journey2Id'] } }] + } }) + expect(prismaService.journey.update).toHaveBeenCalledTimes(2) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { + where: { id: 'journeyId' }, + data: { + status: JourneyStatus.trashed, + trashedAt: new Date(), + updatedAt: journey.updatedAt + } + }) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { + where: { id: 'journey2Id' }, + data: { + status: JourneyStatus.trashed, + trashedAt: new Date(), + updatedAt: journey2.updatedAt + } + }) + }) + + it('keeps updatedAt the same when trashing a journey', async () => { + const originalUpdatedAt = new Date('2021-11-19T12:34:56.647Z') + const newJourney = { ...journey, updatedAt: originalUpdatedAt } + prismaService.journey.findMany.mockResolvedValueOnce([newJourney]) + await resolver.journeysTrash(accessibleJourneys, ['journeyId']) + expect(prismaService.journey.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + updatedAt: originalUpdatedAt + }) + }) + ) }) }) describe('journeysRestore', () => { - it('restores a Journey', async () => { - prismaService.journey.findMany.mockResolvedValueOnce([journey]) - await resolver.journeysRestore(accessibleJourneys, ['journeyId']) + it('restores an array of journeys', async () => { + const journey2 = { + ...journey, + id: 'journey2Id', + updatedAt: new Date('2025-11-19T12:34:56.647Z') + } + prismaService.journey.findMany.mockResolvedValueOnce([journey, journey2]) + await resolver.journeysRestore(accessibleJourneys, [ + 'journeyId', + 'journey2Id' + ]) expect(prismaService.journey.findMany).toHaveBeenCalledWith({ where: { - AND: [accessibleJourneys, { id: { in: ['journeyId'] } }] + AND: [accessibleJourneys, { id: { in: ['journeyId', 'journey2Id'] } }] } }) - expect(prismaService.journey.update).toHaveBeenCalledWith({ + expect(prismaService.journey.update).toHaveBeenCalledTimes(2) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { where: { id: 'journeyId' }, - data: { status: JourneyStatus.published, publishedAt: new Date() } + data: { + status: JourneyStatus.published, + publishedAt: new Date(), + updatedAt: journey.updatedAt + } }) + expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { + where: { id: 'journey2Id' }, + data: { + status: JourneyStatus.published, + publishedAt: new Date(), + updatedAt: journey2.updatedAt + } + }) + }) + + it('keeps updatedAt the same when restoring a journey', async () => { + const originalUpdatedAt = new Date('2021-11-19T12:34:56.647Z') + const newJourney = { ...journey, updatedAt: originalUpdatedAt } + prismaService.journey.findMany.mockResolvedValueOnce([newJourney]) + await resolver.journeysRestore(accessibleJourneys, ['journeyId']) + expect(prismaService.journey.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + updatedAt: originalUpdatedAt + }) + }) + ) }) }) diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts index 1a22e3a3eda..2e83be7d822 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts @@ -957,13 +957,22 @@ export class JourneyResolver { accessibleJourneys: Prisma.JourneyWhereInput, @Args('ids') ids: string[] ): Promise { - await this.prismaService.journey.updateMany({ - where: { AND: [accessibleJourneys, { id: { in: ids } }] }, - data: { status: JourneyStatus.archived, archivedAt: new Date() } - }) - return await this.prismaService.journey.findMany({ + const results = await this.prismaService.journey.findMany({ where: { AND: [accessibleJourneys, { id: { in: ids } }] } }) + return await Promise.all( + results.map( + async (journey) => + await this.prismaService.journey.update({ + where: { id: journey.id }, + data: { + status: JourneyStatus.archived, + archivedAt: new Date(), + updatedAt: journey.updatedAt + } + }) + ) + ) } @Mutation() @@ -973,13 +982,22 @@ export class JourneyResolver { accessibleJourneys: Prisma.JourneyWhereInput, @Args('ids') ids: string[] ): Promise { - await this.prismaService.journey.updateMany({ - where: { AND: [accessibleJourneys, { id: { in: ids } }] }, - data: { status: JourneyStatus.deleted, deletedAt: new Date() } - }) - return await this.prismaService.journey.findMany({ + const results = await this.prismaService.journey.findMany({ where: { AND: [accessibleJourneys, { id: { in: ids } }] } }) + return await Promise.all( + results.map( + async (journey) => + await this.prismaService.journey.update({ + where: { id: journey.id }, + data: { + status: JourneyStatus.deleted, + deletedAt: new Date(), + updatedAt: journey.updatedAt + } + }) + ) + ) } @Mutation() @@ -989,13 +1007,23 @@ export class JourneyResolver { accessibleJourneys: Prisma.JourneyWhereInput, @Args('ids') ids: string[] ): Promise { - await this.prismaService.journey.updateMany({ - where: { AND: [accessibleJourneys, { id: { in: ids } }] }, - data: { status: JourneyStatus.trashed, trashedAt: new Date() } - }) - return await this.prismaService.journey.findMany({ + const results = await this.prismaService.journey.findMany({ where: { AND: [accessibleJourneys, { id: { in: ids } }] } }) + console.log(results.length) + return await Promise.all( + results.map( + async (journey) => + await this.prismaService.journey.update({ + where: { id: journey.id }, + data: { + status: JourneyStatus.trashed, + trashedAt: new Date(), + updatedAt: journey.updatedAt + } + }) + ) + ) } @Mutation() @@ -1015,7 +1043,8 @@ export class JourneyResolver { where: { id: journey.id }, data: { status: JourneyStatus.published, - publishedAt: new Date() + publishedAt: new Date(), + updatedAt: journey.updatedAt } }) ) From 056fd361765e00824f12e1e907a8c282cc7b721a Mon Sep 17 00:00:00 2001 From: VincentHan12 Date: Thu, 29 Jan 2026 22:51:11 +0000 Subject: [PATCH 2/3] fix: lint --- apis/api-journeys/src/app/modules/journey/journey.resolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts index 2e83be7d822..8da5f7031e6 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts @@ -1010,7 +1010,6 @@ export class JourneyResolver { const results = await this.prismaService.journey.findMany({ where: { AND: [accessibleJourneys, { id: { in: ids } }] } }) - console.log(results.length) return await Promise.all( results.map( async (journey) => From eecf2b175871930f41c440916e22a8012c973047 Mon Sep 17 00:00:00 2001 From: VincentHan12 Date: Fri, 30 Jan 2026 00:51:13 +0000 Subject: [PATCH 3/3] fix: adding updatedAt to where to prevent race condition --- .../app/modules/journey/journey.resolver.spec.ts | 16 ++++++++-------- .../src/app/modules/journey/journey.resolver.ts | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts index eba53e42c57..5790f70c237 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts @@ -2385,7 +2385,7 @@ describe('JourneyResolver', () => { }) expect(prismaService.journey.update).toHaveBeenCalledTimes(2) expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { - where: { id: 'journeyId' }, + where: { id: 'journeyId', updatedAt: journey.updatedAt }, data: { status: JourneyStatus.archived, archivedAt: new Date(), @@ -2393,7 +2393,7 @@ describe('JourneyResolver', () => { } }) expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { - where: { id: 'journey2Id' }, + where: { id: 'journey2Id', updatedAt: journey2.updatedAt }, data: { status: JourneyStatus.archived, archivedAt: new Date(), @@ -2436,7 +2436,7 @@ describe('JourneyResolver', () => { }) expect(prismaService.journey.update).toHaveBeenCalledTimes(2) expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { - where: { id: 'journeyId' }, + where: { id: 'journeyId', updatedAt: journey.updatedAt }, data: { status: JourneyStatus.deleted, deletedAt: new Date(), @@ -2444,7 +2444,7 @@ describe('JourneyResolver', () => { } }) expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { - where: { id: 'journey2Id' }, + where: { id: 'journey2Id', updatedAt: journey2.updatedAt }, data: { status: JourneyStatus.deleted, deletedAt: new Date(), @@ -2487,7 +2487,7 @@ describe('JourneyResolver', () => { }) expect(prismaService.journey.update).toHaveBeenCalledTimes(2) expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { - where: { id: 'journeyId' }, + where: { id: 'journeyId', updatedAt: journey.updatedAt }, data: { status: JourneyStatus.trashed, trashedAt: new Date(), @@ -2495,7 +2495,7 @@ describe('JourneyResolver', () => { } }) expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { - where: { id: 'journey2Id' }, + where: { id: 'journey2Id', updatedAt: journey2.updatedAt }, data: { status: JourneyStatus.trashed, trashedAt: new Date(), @@ -2538,7 +2538,7 @@ describe('JourneyResolver', () => { }) expect(prismaService.journey.update).toHaveBeenCalledTimes(2) expect(prismaService.journey.update).toHaveBeenNthCalledWith(1, { - where: { id: 'journeyId' }, + where: { id: 'journeyId', updatedAt: journey.updatedAt }, data: { status: JourneyStatus.published, publishedAt: new Date(), @@ -2546,7 +2546,7 @@ describe('JourneyResolver', () => { } }) expect(prismaService.journey.update).toHaveBeenNthCalledWith(2, { - where: { id: 'journey2Id' }, + where: { id: 'journey2Id', updatedAt: journey2.updatedAt }, data: { status: JourneyStatus.published, publishedAt: new Date(), diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts index 8da5f7031e6..a2be7e0a841 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts @@ -964,7 +964,7 @@ export class JourneyResolver { results.map( async (journey) => await this.prismaService.journey.update({ - where: { id: journey.id }, + where: { id: journey.id, updatedAt: journey.updatedAt }, data: { status: JourneyStatus.archived, archivedAt: new Date(), @@ -989,7 +989,7 @@ export class JourneyResolver { results.map( async (journey) => await this.prismaService.journey.update({ - where: { id: journey.id }, + where: { id: journey.id, updatedAt: journey.updatedAt }, data: { status: JourneyStatus.deleted, deletedAt: new Date(), @@ -1014,7 +1014,7 @@ export class JourneyResolver { results.map( async (journey) => await this.prismaService.journey.update({ - where: { id: journey.id }, + where: { id: journey.id, updatedAt: journey.updatedAt }, data: { status: JourneyStatus.trashed, trashedAt: new Date(), @@ -1039,7 +1039,7 @@ export class JourneyResolver { results.map( async (journey) => await this.prismaService.journey.update({ - where: { id: journey.id }, + where: { id: journey.id, updatedAt: journey.updatedAt }, data: { status: JourneyStatus.published, publishedAt: new Date(),