Skip to content

Commit

Permalink
feat(shared, web, application-generic): Create util for building pref…
Browse files Browse the repository at this point in the history
…erences (#6503)

Co-authored-by: Richard Fontein <[email protected]>
  • Loading branch information
Joel Anton and rifont authored Sep 20, 2024
1 parent e967567 commit 6f9ffec
Show file tree
Hide file tree
Showing 61 changed files with 1,757 additions and 1,738 deletions.
2 changes: 1 addition & 1 deletion apps/api/src/app/bridge/usecases/sync/sync.usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export class Sync {
})
);

if (isWorkflowPreferencesEnabled && workflow.preferences) {
if (isWorkflowPreferencesEnabled) {
await this.upsertPreferences.upsertWorkflowPreferences(
UpsertWorkflowPreferencesCommand.create({
environmentId: savedWorkflow._environmentId,
Expand Down
16 changes: 8 additions & 8 deletions apps/api/src/app/events/e2e/bridge-sync.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,12 @@ describe('Bridge Sync - /bridge/sync (POST)', async () => {
{
preferences: {
workflow: {
defaultValue: false,
enabled: false,
readOnly: true,
},
channels: {
inApp: {
defaultValue: true,
enabled: true,
readOnly: true,
},
},
Expand All @@ -344,13 +344,13 @@ describe('Bridge Sync - /bridge/sync (POST)', async () => {
});

const dashboardPreferences = {
workflow: { defaultValue: false, readOnly: true },
workflow: { enabled: false, readOnly: true },
channels: {
email: { defaultValue: true, readOnly: false },
sms: { defaultValue: true, readOnly: false },
inApp: { defaultValue: false, readOnly: true },
chat: { defaultValue: true, readOnly: false },
push: { defaultValue: true, readOnly: false },
email: { enabled: true, readOnly: false },
sms: { enabled: true, readOnly: false },
inApp: { enabled: false, readOnly: true },
chat: { enabled: true, readOnly: false },
push: { enabled: true, readOnly: false },
},
};

Expand Down
81 changes: 79 additions & 2 deletions apps/api/src/app/events/e2e/bridge-trigger.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ contexts.forEach((context: Context) => {
preferences: {
channels: {
inApp: {
defaultValue: true,
enabled: true,
},
},
},
Expand Down Expand Up @@ -816,7 +816,7 @@ contexts.forEach((context: Context) => {
preferences: {
channels: {
inApp: {
defaultValue: false,
enabled: false,
},
},
},
Expand All @@ -841,6 +841,83 @@ contexts.forEach((context: Context) => {

expect(sentMessages.length).to.be.eq(0);
});

it(`should deliver inApp message if workflow is disabled via workflow preferences and inApp is enabled [${context.name}]`, async () => {
process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true';
const workflowId = `disabled-workflow-${`${context.name}-${uuidv4()}`}`;
const newWorkflow = workflow(
workflowId,
async ({ step }) => {
await step.inApp('send-in-app', () => ({ body: 'Hello there 1' }));
},
{
preferences: {
workflow: {
enabled: false,
},
channels: {
inApp: {
enabled: true,
},
},
},
}
);

await bridgeServer.start({ workflows: [newWorkflow] });

if (context.isStateful) {
await discoverAndSyncBridge(session, workflowsRepository, workflowId, bridgeServer);
}

await triggerEvent(session, workflowId, subscriber, {}, bridge);
await session.awaitRunningJobs();

const sentMessages = await messageRepository.find({
_environmentId: session.environment._id,
_subscriberId: subscriber._id,
templateIdentifier: workflowId,
channel: StepTypeEnum.IN_APP,
});

expect(sentMessages.length).to.be.eq(1);
});

it(`should NOT deliver inApp message if workflow is disabled via workflow preferences [${context.name}]`, async () => {
process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true';
const workflowId = `disabled-workflow-${`${context.name}-${uuidv4()}`}`;
const newWorkflow = workflow(
workflowId,
async ({ step }) => {
await step.inApp('send-in-app', () => ({ body: 'Hello there 1' }));
},
{
preferences: {
workflow: {
enabled: false,
},
},
}
);

await bridgeServer.start({ workflows: [newWorkflow] });

if (context.isStateful) {
await discoverAndSyncBridge(session, workflowsRepository, workflowId, bridgeServer);
}

await triggerEvent(session, workflowId, subscriber, {}, bridge);
await session.awaitRunningJobs();

const sentMessages = await messageRepository.find({
_environmentId: session.environment._id,
_subscriberId: subscriber._id,
templateIdentifier: workflowId,
channel: StepTypeEnum.IN_APP,
});

expect(sentMessages.length).to.be.eq(0);
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,16 @@ export class GetPreferences {
id: workflow._id,
identifier: workflow.triggers[0].identifier,
name: workflow.name,
critical: workflow.critical ?? workflowPreference.template.critical,
/*
* V1 Preferences define `critial` flag on the workflow level.
* V2 Preferences define `critical` flag on the template returned via Preferences.
* This pattern safely returns false when:
* 1. Workflow V1 with no critical flag set
* 2. Workflow V2 with no critical flag set
* 3. Workflow V1 with critical flag set to false
* 4. Workflow V2 with critical flag set to false
*/
critical: workflow.critical || workflowPreference.template.critical || false,
tags: workflow.tags,
},
} satisfies InboxPreference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
SubscriberPreferenceRepository,
SubscriberRepository,
} from '@novu/dal';
import { IPreferenceChannels } from '@novu/shared';
import { IPreferenceChannels, WorkflowPreferences, WorkflowPreferencesPartial } from '@novu/shared';
import { ApiException } from '../../../shared/exceptions/api.exception';
import { AnalyticsEventsEnum } from '../../utils';
import { InboxPreference } from '../../utils/types';
Expand Down Expand Up @@ -237,33 +237,18 @@ export class UpdatePreferences {
environmentId: string;
templateId?: string;
}) {
const preferences = {
const preferences: WorkflowPreferencesPartial = {
workflow: {
defaultValue: PREFERENCE_DEFAULT_VALUE,
enabled: PREFERENCE_DEFAULT_VALUE,
readOnly: false,
},
channels: {
in_app: {
defaultValue: item.channels.in_app !== undefined ? item.channels.in_app : PREFERENCE_DEFAULT_VALUE,
readOnly: false,
},
sms: {
defaultValue: item.channels.sms !== undefined ? item.channels.sms : PREFERENCE_DEFAULT_VALUE,
readOnly: false,
},
email: {
defaultValue: item.channels.email !== undefined ? item.channels.email : PREFERENCE_DEFAULT_VALUE,
readOnly: false,
},
push: {
defaultValue: item.channels.push !== undefined ? item.channels.push : PREFERENCE_DEFAULT_VALUE,
readOnly: false,
},
chat: {
defaultValue: item.channels.chat !== undefined ? item.channels.chat : PREFERENCE_DEFAULT_VALUE,
readOnly: false,
},
},
channels: Object.entries(item.channels).reduce(
(outputChannels, [channel, enabled]) => ({
...outputChannels,
[channel]: { enabled },
}),
{} as WorkflowPreferences['channels']
),
};

if (item.templateId) {
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/app/preferences/dtos/preferences.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IsBoolean, ValidateNested } from 'class-validator';

export class Preference {
@IsBoolean()
defaultValue: boolean;
enabled: boolean;

@IsBoolean()
readOnly: boolean;
Expand Down
Loading

0 comments on commit 6f9ffec

Please sign in to comment.