diff --git a/__tests__/alerts.ts b/__tests__/alerts.ts index 27cc48aa3d..4f6870b613 100644 --- a/__tests__/alerts.ts +++ b/__tests__/alerts.ts @@ -429,3 +429,103 @@ describe('mutation clearOpportunityAlert', () => { expect(alerts.opportunityId).toBeNull(); }); }); + +describe('mutation updateHasSeenOpportunity', () => { + const MUTATION = (hasSeenOpportunity?: boolean) => /* GraphQL */ ` + mutation UpdateHasSeenOpportunity { + updateHasSeenOpportunity${hasSeenOpportunity !== undefined ? `(hasSeenOpportunity: ${hasSeenOpportunity})` : ''} { + _ + } + } + `; + + it('should not authorize when not logged in', () => + testMutationErrorCode(client, { mutation: MUTATION() }, 'UNAUTHENTICATED')); + + it('should set hasSeenOpportunity flag to true by default', async () => { + loggedUser = '1'; + + await con.getRepository(Alerts).save( + con.getRepository(Alerts).create({ + userId: '1', + flags: { hasSeenOpportunity: false }, + }), + ); + + const res = await client.mutate(MUTATION()); + expect(res.errors).toBeFalsy(); + const alerts = await con.getRepository(Alerts).findOneBy({ userId: '1' }); + expect(alerts.flags.hasSeenOpportunity).toBe(true); + }); + + it('should set hasSeenOpportunity flag to true when explicitly passed', async () => { + loggedUser = '1'; + + await con.getRepository(Alerts).save( + con.getRepository(Alerts).create({ + userId: '1', + flags: { hasSeenOpportunity: false }, + }), + ); + + const res = await client.mutate(MUTATION(true)); + expect(res.errors).toBeFalsy(); + const alerts = await con.getRepository(Alerts).findOneBy({ userId: '1' }); + expect(alerts.flags.hasSeenOpportunity).toBe(true); + }); + + it('should set hasSeenOpportunity flag to false when passed', async () => { + loggedUser = '1'; + + await con.getRepository(Alerts).save( + con.getRepository(Alerts).create({ + userId: '1', + flags: { hasSeenOpportunity: true }, + }), + ); + + const res = await client.mutate(MUTATION(false)); + expect(res.errors).toBeFalsy(); + const alerts = await con.getRepository(Alerts).findOneBy({ userId: '1' }); + expect(alerts.flags.hasSeenOpportunity).toBe(false); + }); + + it('should preserve existing flags when updating hasSeenOpportunity', async () => { + loggedUser = '1'; + + const lastReferralDate = new Date('2023-02-05 12:00:00'); + await con.getRepository(Alerts).save( + con.getRepository(Alerts).create({ + userId: '1', + flags: { + hasSeenOpportunity: false, + lastReferralReminder: lastReferralDate, + }, + }), + ); + + const res = await client.mutate(MUTATION(true)); + expect(res.errors).toBeFalsy(); + const alerts = await con.getRepository(Alerts).findOneBy({ userId: '1' }); + expect(alerts.flags.hasSeenOpportunity).toBe(true); + // JSONB stores dates as ISO strings + expect(alerts.flags.lastReferralReminder).toEqual( + lastReferralDate.toISOString(), + ); + }); + + it('should work when alerts record does not have flags set', async () => { + loggedUser = '1'; + + await con.getRepository(Alerts).save( + con.getRepository(Alerts).create({ + userId: '1', + }), + ); + + const res = await client.mutate(MUTATION()); + expect(res.errors).toBeFalsy(); + const alerts = await con.getRepository(Alerts).findOneBy({ userId: '1' }); + expect(alerts.flags.hasSeenOpportunity).toBe(true); + }); +}); diff --git a/src/schema/alerts.ts b/src/schema/alerts.ts index cb2f53688c..429834fbe7 100644 --- a/src/schema/alerts.ts +++ b/src/schema/alerts.ts @@ -5,6 +5,7 @@ import { AuthContext, BaseContext, Context } from '../Context'; import { DataSource, QueryRunner } from 'typeorm'; import { insertOrIgnoreAction } from './actions'; import { GQLEmptyResponse } from './common'; +import { updateFlagsStatement } from '../common'; interface GQLAlerts { filter: boolean; @@ -189,6 +190,13 @@ export const typeDefs = /* GraphQL */ ` Reset opportunity alert """ clearOpportunityAlert: EmptyResponse! @auth + + """ + Update the hasSeenOpportunity flag + """ + updateHasSeenOpportunity( + hasSeenOpportunity: Boolean = true + ): EmptyResponse! @auth } extend type Query { @@ -314,6 +322,19 @@ export const resolvers: IResolvers = traceResolvers< await updateAlerts(ctx.con, ctx.userId, { opportunityId: null }); return { _: true }; }, + updateHasSeenOpportunity: async ( + _, + { hasSeenOpportunity = true }: { hasSeenOpportunity?: boolean }, + ctx: AuthContext, + ): Promise => { + await ctx.con.getRepository(Alerts).update( + { userId: ctx.userId }, + { + flags: updateFlagsStatement({ hasSeenOpportunity }), + }, + ); + return { _: true }; + }, }, Query: { userAlerts: (_, __, ctx: Context): Promise | GQLAlerts =>