diff --git a/apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts b/apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts index dce91af2b6c..79373009e10 100644 --- a/apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts +++ b/apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts @@ -6,6 +6,7 @@ import { EnvironmentRepository, IntegrationEntity, MessageEntity, + LayoutRepository, } from '@novu/dal'; import { ChannelTypeEnum, @@ -42,6 +43,7 @@ export class SendMessageEmail extends SendMessageBase { protected environmentRepository: EnvironmentRepository, protected subscriberRepository: SubscriberRepository, protected messageRepository: MessageRepository, + protected layoutRepository: LayoutRepository, protected createLogUsecase: CreateLog, protected createExecutionDetails: CreateExecutionDetails, private compileEmailTemplateUsecase: CompileEmailTemplate, @@ -133,6 +135,8 @@ export class SendMessageEmail extends SendMessageBase { command.overrides[integration?.providerId] || {} ); + const overrideLayoutId = await this.getOverrideLayoutId(command); + let html; let subject = ''; let content; @@ -141,7 +145,7 @@ export class SendMessageEmail extends SendMessageBase { subject: emailChannel.template.subject || '', preheader: emailChannel.template.preheader, content: emailChannel.template.content, - layoutId: emailChannel.template._layoutId, + layoutId: overrideLayoutId ?? emailChannel.template._layoutId, contentType: emailChannel.template.contentType ? emailChannel.template.contentType : 'editor', payload: { ...command.payload, @@ -454,6 +458,37 @@ export class SendMessageEmail extends SendMessageBase { } } + private async getOverrideLayoutId(command: SendMessageCommand) { + const overrideLayoutIdentifier = command.overrides?.layoutIdentifier; + + if (overrideLayoutIdentifier) { + const layoutOverride = await this.layoutRepository.findOne( + { + _environmentId: command.environmentId, + identifier: overrideLayoutIdentifier, + }, + '_id' + ); + if (!layoutOverride) { + await this.createExecutionDetails.execute( + CreateExecutionDetailsCommand.create({ + ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job), + detail: DetailEnum.LAYOUT_NOT_FOUND, + source: ExecutionDetailsSourceEnum.INTERNAL, + status: ExecutionDetailsStatusEnum.FAILED, + isTest: false, + isRetry: false, + raw: JSON.stringify({ + layoutIdentifier: overrideLayoutIdentifier, + }), + }) + ); + } + + return layoutOverride?._id; + } + } + public buildFactoryIntegration(integration: IntegrationEntity, senderName?: string) { return { ...integration, diff --git a/docs/docs/platform/layouts.md b/docs/docs/platform/layouts.md index 80522947f17..e2a6c8199cc 100644 --- a/docs/docs/platform/layouts.md +++ b/docs/docs/platform/layouts.md @@ -55,6 +55,34 @@ You can preview your layout combined with your email content through the `Previe +## Override layout on trigger + +To override your assigned layout during a trigger event use the `layoutIdentifier` property, the layout specified will be used for all emails in the context of that trigger event. + +```ts +import { Novu } from '@novu/node'; + +const novu = new Novu(''); + +novu.trigger('workflow-identifier', { + to: { + subscriberId: '...', + }, + payload: { + attachments: [ + { + file: fs.readFileSync(__dirname + '/data/test.jpeg'), + name: 'test.jpeg', + mime: 'image/jpg', + }, + ], + }, + overrides: { + layoutIdentifier: 'your-layout-identifier', + }, +}); +``` + ## Using SDK Novu SDK supports all layout functionalities: diff --git a/packages/application-generic/src/usecases/create-execution-details/types/index.ts b/packages/application-generic/src/usecases/create-execution-details/types/index.ts index 3cca3d725f6..2cca3a75666 100644 --- a/packages/application-generic/src/usecases/create-execution-details/types/index.ts +++ b/packages/application-generic/src/usecases/create-execution-details/types/index.ts @@ -12,6 +12,7 @@ export enum DetailEnum { MESSAGE_CONTENT_SYNTAX_ERROR = 'Message content could not be generated due to syntax error in email editor', MESSAGE_CREATED = 'Message created', SUBSCRIBER_NO_ACTIVE_INTEGRATION = 'Subscriber does not have an active integration', + LAYOUT_NOT_FOUND = 'Layout not found ', INTEGRATION_INSTANCE_SELECTED = 'Integration instance selected', LIMIT_PASSED_NOVU_INTEGRATION = "Novu's provider limit has been reached", SUBSCRIBER_NO_CHANNEL_DETAILS = 'Subscriber missing recipient details', diff --git a/packages/node/src/lib/events/events.interface.ts b/packages/node/src/lib/events/events.interface.ts index 2a2628a4260..af8ef575f43 100644 --- a/packages/node/src/lib/events/events.interface.ts +++ b/packages/node/src/lib/events/events.interface.ts @@ -51,6 +51,8 @@ export type ITriggerOverrides = { [key in 'expo']?: ITriggerOverrideExpo; } & { [key in 'delay']?: ITriggerOverrideDelayAction; +} & { + [key in 'layoutIdentifier']?: string; } & { [key in 'email']?: IEmailOverrides; } & { diff --git a/packages/node/src/lib/events/events.spec.ts b/packages/node/src/lib/events/events.spec.ts index 67667b68528..37e3da0ff30 100644 --- a/packages/node/src/lib/events/events.spec.ts +++ b/packages/node/src/lib/events/events.spec.ts @@ -92,6 +92,32 @@ describe('test use of novus node package - Events', () => { }); }); + test('should pass layout identifier overrides to request', async () => { + mockedAxios.post.mockResolvedValue({}); + + await novu.events.trigger('test-template', { + to: ['test-user', 'test-another-user'], + payload: { + organizationName: 'Company', + }, + overrides: { + layoutIdentifier: 'overrides-identifier', + }, + }); + + expect(mockedAxios.post).toHaveBeenCalled(); + expect(mockedAxios.post).toHaveBeenCalledWith('/events/trigger', { + name: 'test-template', + to: ['test-user', 'test-another-user'], + overrides: { + layoutIdentifier: 'overrides-identifier', + }, + payload: { + organizationName: 'Company', + }, + }); + }); + test('should trigger correctly for all subscribers definitions ', async () => { mockedAxios.post.mockResolvedValue({});