From e9571c3aff82a5da6521a7a1671f2fcb4f63172e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tymczuk?= Date: Tue, 7 Jan 2025 10:21:05 +0100 Subject: [PATCH] fix(api-service): on global preferences update reset workflow preferences per env (#7428) Co-authored-by: Sokratis Vidros --- .../e2e/update-global-preference.e2e.ts | 70 ++++++++++++++++++- .../upsert-preferences.usecase.ts | 7 +- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/apps/api/src/app/subscribers/e2e/update-global-preference.e2e.ts b/apps/api/src/app/subscribers/e2e/update-global-preference.e2e.ts index a41789803b0..8dace7ca50b 100644 --- a/apps/api/src/app/subscribers/e2e/update-global-preference.e2e.ts +++ b/apps/api/src/app/subscribers/e2e/update-global-preference.e2e.ts @@ -1,14 +1,17 @@ -import { ChannelTypeEnum } from '@novu/shared'; +import { ChannelTypeEnum, StepTypeEnum } from '@novu/shared'; import { UserSession } from '@novu/testing'; import { expect } from 'chai'; -import { updateGlobalPreferences } from './helpers'; +import { SubscriberRepository } from '@novu/dal'; +import { getPreference, updateGlobalPreferences } from './helpers'; describe('Update Subscribers global preferences - /subscribers/:subscriberId/preferences (PATCH)', function () { let session: UserSession; + let subscriberRepository: SubscriberRepository; beforeEach(async () => { session = new UserSession(); + subscriberRepository = new SubscriberRepository(); await session.initialize(); }); @@ -89,6 +92,69 @@ describe('Update Subscribers global preferences - /subscribers/:subscriberId/pre }); }); + it('should update user global preferences only for the current environment', async function () { + // create a template in dev environment + await session.createTemplate({ + steps: [ + { + type: StepTypeEnum.IN_APP, + content: 'Hello', + }, + ], + noFeedId: true, + }); + + await session.switchToProdEnvironment(); + // create a subscriber in prod environment + await subscriberRepository.create({ + _environmentId: session.environment._id, + _organizationId: session.organization._id, + subscriberId: session.subscriberId, + }); + // create a template in prod environment + await session.createTemplate({ + steps: [ + { + type: StepTypeEnum.IN_APP, + content: 'Hello', + }, + ], + noFeedId: true, + }); + + await session.switchToDevEnvironment(); + // update the subscriber global preferences in dev environment + const response = await updateGlobalPreferences( + { + enabled: true, + preferences: [{ type: ChannelTypeEnum.IN_APP, enabled: false }], + }, + session + ); + + expect(response.data.data.preference.enabled).to.eql(true); + expect(response.data.data.preference.channels).to.eql({ + [ChannelTypeEnum.EMAIL]: true, + [ChannelTypeEnum.PUSH]: true, + [ChannelTypeEnum.CHAT]: true, + [ChannelTypeEnum.SMS]: true, + [ChannelTypeEnum.IN_APP]: false, + }); + + // get the subscriber preferences in dev environment + const getDevPreferencesResponse = await getPreference(session); + const devPreferences = getDevPreferencesResponse.data.data; + expect(devPreferences.every((item) => !!item.preference.channels.in_app)).to.be.false; + + await session.switchToProdEnvironment(); + + // get the subscriber preferences in prod environment + session.apiKey = session.environment.apiKeys[0].key; + const getProdPreferencesResponse = await getPreference(session); + const prodPreferences = getProdPreferencesResponse.data.data; + expect(prodPreferences.every((item) => !!item.preference.channels.in_app)).to.be.true; + }); + // `enabled` flag is not used anymore. The presence of a preference object means that the subscriber has enabled notifications. it.skip('should update user global preference and disable the flag for the future channels update', async function () { const disablePreferenceData = { diff --git a/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts b/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts index 5f6f06fe83d..bc62397efc8 100644 --- a/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts +++ b/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts @@ -10,6 +10,7 @@ import { UpsertSubscriberGlobalPreferencesCommand } from './upsert-subscriber-gl import { UpsertSubscriberWorkflowPreferencesCommand } from './upsert-subscriber-workflow-preferences.command'; import { UpsertUserWorkflowPreferencesCommand } from './upsert-user-workflow-preferences.command'; import { deepMerge } from '../../utils'; +import { Instrument } from '../../instrumentation'; export type WorkflowPreferencesFull = Omit & { preferences: WorkflowPreferences; @@ -34,6 +35,7 @@ type UpsertPreferencesCommand = Omit< export class UpsertPreferences { constructor(private preferencesRepository: PreferencesRepository) {} + @Instrument() public async upsertWorkflowPreferences( command: UpsertWorkflowPreferencesCommand, ): Promise { @@ -46,6 +48,7 @@ export class UpsertPreferences { }) as Promise; } + @Instrument() public async upsertSubscriberGlobalPreferences( command: UpsertSubscriberGlobalPreferencesCommand, ) { @@ -78,7 +81,7 @@ export class UpsertPreferences { await this.preferencesRepository.update( { - _organizationId: command.organizationId, + _environmentId: command.environmentId, _subscriberId: command._subscriberId, type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW, $or: channelTypes.map((channelType) => ({ @@ -91,6 +94,7 @@ export class UpsertPreferences { ); } + @Instrument() public async upsertSubscriberWorkflowPreferences( command: UpsertSubscriberWorkflowPreferencesCommand, ) { @@ -104,6 +108,7 @@ export class UpsertPreferences { }); } + @Instrument() public async upsertUserWorkflowPreferences( command: UpsertUserWorkflowPreferencesCommand, ): Promise {