Skip to content

Commit

Permalink
feat(api): Delete subscriber channel preference when updating global …
Browse files Browse the repository at this point in the history
…channel (#6767)
  • Loading branch information
rifont authored Oct 28, 2024
1 parent a2781bf commit bd2f04d
Show file tree
Hide file tree
Showing 35 changed files with 1,008 additions and 750 deletions.
4 changes: 2 additions & 2 deletions .github/actions/setup-project/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ runs:

- name: 📚 Start MongoDB
if: ${{ inputs.slim == 'false' }}
uses: supercharge/mongodb-github-action@v1.9.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 4.2.8
mongodb-version: 5.0.29

- name: 🛟 Install dependencies
shell: bash
Expand Down
14 changes: 12 additions & 2 deletions apps/api/src/app/bridge/usecases/sync/sync.usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ import {
UpsertPreferences,
UpsertWorkflowPreferencesCommand,
} from '@novu/application-generic';
import { FeatureFlagsKeysEnum, WorkflowCreationSourceEnum, WorkflowOriginEnum, WorkflowTypeEnum } from '@novu/shared';
import {
FeatureFlagsKeysEnum,
WorkflowCreationSourceEnum,
WorkflowOriginEnum,
WorkflowTypeEnum,
WorkflowPreferencesPartial,
} from '@novu/shared';
import { DiscoverOutput, DiscoverStepOutput, DiscoverWorkflowOutput, GetActionEnum } from '@novu/framework/internal';

import { SyncCommand } from './sync.command';
Expand Down Expand Up @@ -186,7 +192,7 @@ export class Sync {
environmentId: savedWorkflow._environmentId,
organizationId: savedWorkflow._organizationId,
templateId: savedWorkflow._id,
preferences: workflow.preferences,
preferences: this.getWorkflowPreferences(workflow),
})
);
}
Expand Down Expand Up @@ -323,6 +329,10 @@ export class Sync {
return notificationGroupId;
}

private getWorkflowPreferences(workflow: DiscoverWorkflowOutput): WorkflowPreferencesPartial {
return workflow.preferences || {};
}

private getWorkflowName(workflow: DiscoverWorkflowOutput): string {
return workflow.name || workflow.workflowId;
}
Expand Down
19 changes: 13 additions & 6 deletions apps/api/src/app/inbox/e2e/get-preferences.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { UserSession } from '@novu/testing';
import { expect } from 'chai';
import { StepTypeEnum } from '@novu/shared';

describe('Get all preferences - /inbox/preferences (GET)', function () {
let session: UserSession;
Expand All @@ -9,22 +10,28 @@ describe('Get all preferences - /inbox/preferences (GET)', function () {
await session.initialize();
});

it('should always get the global preferences even if workflow preferences are not present', async function () {
it('should return no global preferences if workflow preferences are not present', async function () {
const response = await session.testAgent
.get('/v1/inbox/preferences')
.set('Authorization', `Bearer ${session.subscriberToken}`);

const globalPreference = response.body.data[0];

expect(globalPreference.channels.email).to.equal(true);
expect(globalPreference.channels.in_app).to.equal(true);
expect(globalPreference.channels.email).to.equal(undefined);
expect(globalPreference.channels.in_app).to.equal(undefined);
expect(globalPreference.level).to.equal('global');
expect(response.body.data.length).to.equal(1);
});

it('should get both global and workflow preferences if workflow is present', async function () {
it('should get both global preferences for active channels and workflow preferences if workflow is present', async function () {
await session.createTemplate({
noFeedId: true,
steps: [
{
type: StepTypeEnum.EMAIL,
content: 'Test notification content',
},
],
});

const response = await session.testAgent
Expand All @@ -34,13 +41,13 @@ describe('Get all preferences - /inbox/preferences (GET)', function () {
const globalPreference = response.body.data[0];

expect(globalPreference.channels.email).to.equal(true);
expect(globalPreference.channels.in_app).to.equal(true);
expect(globalPreference.channels.in_app).to.equal(undefined);
expect(globalPreference.level).to.equal('global');

const workflowPreference = response.body.data[1];

expect(workflowPreference.channels.email).to.equal(true);
expect(workflowPreference.channels.in_app).to.equal(true);
expect(workflowPreference.channels.in_app).to.equal(undefined);
expect(workflowPreference.level).to.equal('template');
});

Expand Down
107 changes: 88 additions & 19 deletions apps/api/src/app/inbox/e2e/update-preferences.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EmailBlockTypeEnum, PreferenceLevelEnum, StepTypeEnum } from '@novu/shared';
import { UserSession } from '@novu/testing';
import { expect } from 'chai';
import e from 'express';

describe('Update global preferences - /inbox/preferences (PATCH)', function () {
let session: UserSession;
Expand Down Expand Up @@ -38,48 +39,53 @@ describe('Update global preferences - /inbox/preferences (PATCH)', function () {
.set('Authorization', `Bearer ${session.subscriberToken}`);

expect(response.status).to.equal(200);
expect(response.body.data.channels.email).to.equal(true);
expect(response.body.data.channels.in_app).to.equal(true);
expect(response.body.data.channels.sms).to.equal(false);
expect(response.body.data.channels.push).to.equal(false);
expect(response.body.data.channels.chat).to.equal(true);
expect(response.body.data.channels.email).to.equal(undefined);
expect(response.body.data.channels.in_app).to.equal(undefined);
expect(response.body.data.channels.sms).to.equal(undefined);
expect(response.body.data.channels.push).to.equal(undefined);
expect(response.body.data.channels.chat).to.equal(undefined);
expect(response.body.data.level).to.equal(PreferenceLevelEnum.GLOBAL);
});

it('should update the particular channel sent in the body and return all channels', async function () {
it('should update the particular channel sent in the body and return only active channels', async function () {
await session.createTemplate({
noFeedId: true,
steps: [
{
type: StepTypeEnum.IN_APP,
content: 'Test notification content',
},
],
});

const response = await session.testAgent
.patch('/v1/inbox/preferences')
.send({
email: true,
in_app: true,
sms: false,
push: false,
chat: true,
})
.set('Authorization', `Bearer ${session.subscriberToken}`);

expect(response.status).to.equal(200);
expect(response.body.data.channels.email).to.equal(true);
expect(response.body.data.channels.email).to.equal(undefined);
expect(response.body.data.channels.in_app).to.equal(true);
expect(response.body.data.channels.sms).to.equal(false);
expect(response.body.data.channels.push).to.equal(false);
expect(response.body.data.channels.chat).to.equal(true);
expect(response.body.data.channels.sms).to.equal(undefined);
expect(response.body.data.channels.push).to.equal(undefined);
expect(response.body.data.channels.chat).to.equal(undefined);
expect(response.body.data.level).to.equal(PreferenceLevelEnum.GLOBAL);

const responseSecond = await session.testAgent
.patch('/v1/inbox/preferences')
.send({
email: false,
in_app: true,
})
.set('Authorization', `Bearer ${session.subscriberToken}`);

expect(responseSecond.status).to.equal(200);
expect(responseSecond.body.data.channels.email).to.equal(false);
expect(responseSecond.body.data.channels.email).to.equal(undefined);
expect(responseSecond.body.data.channels.in_app).to.equal(true);
expect(responseSecond.body.data.channels.sms).to.equal(false);
expect(responseSecond.body.data.channels.push).to.equal(false);
expect(responseSecond.body.data.channels.chat).to.equal(true);
expect(responseSecond.body.data.channels.sms).to.equal(undefined);
expect(responseSecond.body.data.channels.push).to.equal(undefined);
expect(responseSecond.body.data.channels.chat).to.equal(undefined);
expect(responseSecond.body.data.level).to.equal(PreferenceLevelEnum.GLOBAL);
});
});
Expand Down Expand Up @@ -256,4 +262,67 @@ describe('Update workflow preferences - /inbox/preferences/:workflowId (PATCH)',
expect(responseSecond.body.data.channels.chat).to.equal(true);
expect(responseSecond.body.data.level).to.equal(PreferenceLevelEnum.TEMPLATE);
});

it('should unset the suscribers workflow preference for the specified channels when the global preference is updated', async function () {
const workflow = await session.createTemplate({
noFeedId: true,
steps: [
{
type: StepTypeEnum.IN_APP,
content: 'Test notification content',
},
{
type: StepTypeEnum.EMAIL,
content: 'Test notification content',
},
],
});

const updateWorkflowPrefResponse = await session.testAgent
.patch(`/v1/inbox/preferences/${workflow._id}`)
.send({
email: false,
in_app: false,
})
.set('Authorization', `Bearer ${session.subscriberToken}`);

expect(updateWorkflowPrefResponse.status).to.equal(200);
expect(updateWorkflowPrefResponse.body.data.channels.email).to.equal(false);
expect(updateWorkflowPrefResponse.body.data.channels.in_app).to.equal(false);
expect(updateWorkflowPrefResponse.body.data.channels.sms).to.equal(undefined);
expect(updateWorkflowPrefResponse.body.data.channels.push).to.equal(undefined);
expect(updateWorkflowPrefResponse.body.data.channels.chat).to.equal(undefined);
expect(updateWorkflowPrefResponse.body.data.level).to.equal(PreferenceLevelEnum.TEMPLATE);

const updateGlobalPrefResponse = await session.testAgent
.patch(`/v1/inbox/preferences`)
.send({
email: true,
})
.set('Authorization', `Bearer ${session.subscriberToken}`);

expect(updateGlobalPrefResponse.status).to.equal(200);
expect(updateGlobalPrefResponse.body.data.channels.email).to.equal(true);
expect(updateGlobalPrefResponse.body.data.channels.in_app).to.equal(true);
expect(updateGlobalPrefResponse.body.data.channels.sms).to.equal(undefined);
expect(updateGlobalPrefResponse.body.data.channels.push).to.equal(undefined);
expect(updateGlobalPrefResponse.body.data.channels.chat).to.equal(undefined);
expect(updateGlobalPrefResponse.body.data.level).to.equal(PreferenceLevelEnum.GLOBAL);

const getInboxPrefResponse = await session.testAgent
.get(`/v1/inbox/preferences`)
.set('Authorization', `Bearer ${session.subscriberToken}`);

const workflowPref = getInboxPrefResponse.body.data.find(
(pref) => pref.level === PreferenceLevelEnum.TEMPLATE && pref.workflow.id === workflow._id
);

expect(getInboxPrefResponse.status).to.equal(200);
expect(workflowPref.channels.email).to.equal(true);
expect(workflowPref.channels.in_app).to.equal(false);
expect(workflowPref.channels.sms).to.equal(undefined);
expect(workflowPref.channels.push).to.equal(undefined);
expect(workflowPref.channels.chat).to.equal(undefined);
expect(workflowPref.level).to.equal(PreferenceLevelEnum.TEMPLATE);
});
});
10 changes: 5 additions & 5 deletions apps/api/src/app/inbox/inbox.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import { UpdateNotificationActionCommand } from './usecases/update-notification-
import { UpdateAllNotificationsRequestDto } from './dtos/update-all-notifications-request.dto';
import { UpdateAllNotificationsCommand } from './usecases/update-all-notifications/update-all-notifications.command';
import { UpdateAllNotifications } from './usecases/update-all-notifications/update-all-notifications.usecase';
import { GetPreferences } from './usecases/get-preferences/get-preferences.usecase';
import { GetPreferencesCommand } from './usecases/get-preferences/get-preferences.command';
import { GetInboxPreferences } from './usecases/get-inbox-preferences/get-inbox-preferences.usecase';
import { GetInboxPreferencesCommand } from './usecases/get-inbox-preferences/get-inbox-preferences.command';
import { GetPreferencesResponseDto } from './dtos/get-preferences-response.dto';
import { UpdatePreferencesRequestDto } from './dtos/update-preferences-request.dto';
import { UpdatePreferences } from './usecases/update-preferences/update-preferences.usecase';
Expand All @@ -46,7 +46,7 @@ export class InboxController {
private markNotificationAsUsecase: MarkNotificationAs,
private updateNotificationActionUsecase: UpdateNotificationAction,
private updateAllNotifications: UpdateAllNotifications,
private getPreferencesUsecase: GetPreferences,
private getInboxPreferencesUsecase: GetInboxPreferences,
private updatePreferencesUsecase: UpdatePreferences
) {}

Expand Down Expand Up @@ -107,8 +107,8 @@ export class InboxController {
@SubscriberSession() subscriberSession: SubscriberEntity,
@Query() query: GetPreferencesRequestDto
): Promise<GetPreferencesResponseDto[]> {
return await this.getPreferencesUsecase.execute(
GetPreferencesCommand.create({
return await this.getInboxPreferencesUsecase.execute(
GetInboxPreferencesCommand.create({
organizationId: subscriberSession._organizationId,
subscriberId: subscriberSession.subscriberId,
environmentId: subscriberSession._environmentId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IsArray, IsOptional, IsString } from 'class-validator';
import { EnvironmentWithSubscriber } from '../../../shared/commands/project.command';

export class GetPreferencesCommand extends EnvironmentWithSubscriber {
export class GetInboxPreferencesCommand extends EnvironmentWithSubscriber {
@IsOptional()
@IsArray()
@IsString({ each: true })
Expand Down
Loading

0 comments on commit bd2f04d

Please sign in to comment.