Skip to content

Commit

Permalink
fix(application-generic): potential get preferences performance impro…
Browse files Browse the repository at this point in the history
…vement (#7467)
  • Loading branch information
LetItRock authored Jan 9, 2025
1 parent 55bcf20 commit 2af9e78
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
SubscriberRepository,
PreferencesRepository,
NotificationTemplateEntity,
PreferencesEntity,
} from '@novu/dal';
import {
ChannelTypeEnum,
IPreferenceChannels,
ISubscriberPreferenceResponse,
PreferencesTypeEnum,
StepTypeEnum,
Expand All @@ -15,7 +17,11 @@ import {
import { GetSubscriberPreferenceCommand } from './get-subscriber-preference.command';
import { Instrument, InstrumentUsecase } from '../../instrumentation';
import { MergePreferences } from '../merge-preferences/merge-preferences.usecase';
import { GetPreferences, PreferenceSet } from '../get-preferences';
import {
GetPreferences,
GetPreferencesResponseDto,
PreferenceSet,
} from '../get-preferences';
import {
filteredPreference,
overridePreferences,
Expand Down Expand Up @@ -121,68 +127,103 @@ export class GetSubscriberPreference {
return acc;
}, {});

const mergedPreferences: ISubscriberPreferenceResponse[] = workflowList.map(
(workflow) => {
const preferences = workflowPreferenceSets[workflow._id];
const mergeCommand = MergePreferencesCommand.create({
workflowResourcePreference: preferences.workflowResourcePreference,
workflowUserPreference: preferences.workflowUserPreference,
subscriberWorkflowPreference:
preferences.subscriberWorkflowPreference,
...(subscriberGlobalPreference ? { subscriberGlobalPreference } : {}),
});
const merged = MergePreferences.execute(mergeCommand);
const workflowPreferences: ISubscriberPreferenceResponse[] =
this.calculateWorkflowPreferences(
workflowList,
workflowPreferenceSets,
subscriberGlobalPreference,
command.includeInactiveChannels,
);

const nonCriticalWorkflowPreferences = workflowPreferences.filter(
(preference) => !preference.template.critical,
);

return nonCriticalWorkflowPreferences;
}

const includedChannels = this.getChannels(
workflow,
command.includeInactiveChannels,
);
@Instrument()
private calculateWorkflowPreferences(
workflowList: NotificationTemplateEntity[],
workflowPreferenceSets: Record<string, PreferenceSet>,
subscriberGlobalPreference: PreferencesEntity,
includeInactiveChannels: boolean,
): ISubscriberPreferenceResponse[] {
return workflowList.map((workflow) => {
const preferences = workflowPreferenceSets[workflow._id];
const merged = this.mergePreferences(
preferences,
subscriberGlobalPreference,
);

const initialChannels = filteredPreference(
{
email: true,
sms: true,
in_app: true,
chat: true,
push: true,
},
includedChannels,
);
const includedChannels = this.getChannels(
workflow,
includeInactiveChannels,
);

const { channels, overrides } = overridePreferences(
{
template: GetPreferences.mapWorkflowPreferencesToChannelPreferences(
merged.source.WORKFLOW_RESOURCE,
),
subscriber:
GetPreferences.mapWorkflowPreferencesToChannelPreferences(
merged.preferences,
),
workflowOverride: {},
},
initialChannels,
);
const initialChannels = filteredPreference(
{
email: true,
sms: true,
in_app: true,
chat: true,
push: true,
},
includedChannels,
);

return {
preference: {
channels,
enabled: true,
overrides,
},
template: mapTemplateConfiguration({
...workflow,
critical: merged.preferences.all.readOnly,
}),
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
};
const { channels, overrides } = this.calculateChannelsAndOverrides(
merged,
initialChannels,
);

return {
preference: {
channels,
enabled: true,
overrides,
},
template: mapTemplateConfiguration({
...workflow,
critical: merged.preferences.all.readOnly,
}),
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
};
});
}

@Instrument()
private calculateChannelsAndOverrides(
merged: GetPreferencesResponseDto,
initialChannels: IPreferenceChannels,
) {
return overridePreferences(
{
template: GetPreferences.mapWorkflowPreferencesToChannelPreferences(
merged.source.WORKFLOW_RESOURCE,
),
subscriber: GetPreferences.mapWorkflowPreferencesToChannelPreferences(
merged.preferences,
),
workflowOverride: {},
},
initialChannels,
);
}

const nonCriticalWorkflowPreferences = mergedPreferences.filter(
(preference) => !preference.template.critical,
);
@Instrument()
private mergePreferences(
preferences: PreferenceSet,
subscriberGlobalPreference: PreferencesEntity,
) {
const mergeCommand = MergePreferencesCommand.create({
workflowResourcePreference: preferences.workflowResourcePreference,
workflowUserPreference: preferences.workflowUserPreference,
subscriberWorkflowPreference: preferences.subscriberWorkflowPreference,
...(subscriberGlobalPreference ? { subscriberGlobalPreference } : {}),
});

return nonCriticalWorkflowPreferences;
return MergePreferences.execute(mergeCommand);
}

private getChannels(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import merge from 'lodash/merge';
import { PreferencesTypeEnum } from '@novu/shared';
import { PreferencesEntity } from '@novu/dal';
import { deepMerge } from '../../utils';

import { GetPreferencesResponseDto } from '../get-preferences';
import { MergePreferencesCommand } from './merge-preferences.command';
import { DeepRequired } from '../../http/utils.types';

/**
* Merge preferences for a subscriber.
Expand Down Expand Up @@ -44,9 +43,7 @@ export class MergePreferences {
...(isWorkflowPreferenceReadonly ? [] : subscriberPreferences),
];

const mergedPreferences = deepMerge(
preferencesList as DeepRequired<PreferencesEntity>[],
);
const mergedPreferences = merge({}, ...preferencesList);

// Build the source object
const source = {
Expand Down

0 comments on commit 2af9e78

Please sign in to comment.