Skip to content

Commit 906cefa

Browse files
authored
feat(api-service): provider trigger overrides alignment (#7448)
1 parent 6de6e34 commit 906cefa

File tree

20 files changed

+161
-62
lines changed

20 files changed

+161
-62
lines changed

apps/api/src/app/events/dtos/trigger-event-request.dto.ts

+62-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { IsDefined, IsObject, IsOptional, IsString, ValidateIf, ValidateNested }
22
import { Type } from 'class-transformer';
33
import { ApiExtraModels, ApiHideProperty, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger';
44
import {
5+
EmailProviderIdEnum,
6+
ProvidersIdEnum,
7+
ProvidersIdEnumConst,
58
TriggerRecipientsPayload,
69
TriggerRecipientsTypeEnum,
710
TriggerRecipientSubscriber,
@@ -24,9 +27,9 @@ export class WorkflowToStepControlValuesDto {
2427
type: 'object',
2528
additionalProperties: {
2629
type: 'object',
27-
additionalProperties: true, // Allows any additional properties
30+
additionalProperties: true,
2831
},
29-
required: false, // Indicates that this property is optional
32+
required: false,
3033
})
3134
steps?: Record<string, Record<string, unknown>>;
3235
}
@@ -45,7 +48,59 @@ export class TopicPayloadDto {
4548
type: TriggerRecipientsTypeEnum;
4649
}
4750

48-
@ApiExtraModels(SubscriberPayloadDto, TenantPayloadDto, TopicPayloadDto)
51+
export class StepsOverrides {
52+
@ApiProperty({
53+
description: 'Passing the provider id and the provider specific configurations',
54+
example: {
55+
sendgrid: {
56+
templateId: '1234567890',
57+
},
58+
},
59+
type: 'object',
60+
additionalProperties: {
61+
type: 'object',
62+
additionalProperties: true,
63+
},
64+
})
65+
providers: Record<ProvidersIdEnum, Record<string, unknown>>;
66+
}
67+
68+
export class TriggerOverrides {
69+
@ApiProperty({
70+
description: 'This could be used to override provider specific configurations',
71+
example: {
72+
'email-step': {
73+
providers: {
74+
sendgrid: {
75+
templateId: '1234567890',
76+
},
77+
},
78+
},
79+
},
80+
type: 'object',
81+
additionalProperties: {
82+
$ref: getSchemaPath(StepsOverrides),
83+
},
84+
})
85+
steps?: Record<string, StepsOverrides>;
86+
87+
@ApiProperty({
88+
description: 'Overrides the provider configuration for the entire workflow and all steps',
89+
example: {
90+
sendgrid: {
91+
templateId: '1234567890',
92+
},
93+
},
94+
type: 'object',
95+
additionalProperties: {
96+
type: 'object',
97+
additionalProperties: true,
98+
},
99+
})
100+
providers?: Record<ProvidersIdEnum, Record<string, unknown>>;
101+
}
102+
103+
@ApiExtraModels(SubscriberPayloadDto, TenantPayloadDto, TopicPayloadDto, StepsOverrides)
49104
export class TriggerEventRequestDto {
50105
@SdkApiProperty(
51106
{
@@ -91,16 +146,16 @@ export class TriggerEventRequestDto {
91146
},
92147
},
93148
},
94-
type: 'object',
149+
type: TriggerOverrides,
95150
additionalProperties: {
96151
type: 'object',
97-
additionalProperties: true, // Allows any additional properties
152+
additionalProperties: true,
98153
},
99-
required: false, // Indicates that this property is optional
154+
required: false,
100155
})
101156
@IsObject()
102157
@IsOptional()
103-
overrides?: Record<string, Record<string, unknown>>;
158+
overrides?: TriggerOverrides;
104159

105160
@ApiProperty({
106161
description: 'The recipients list of people who will receive the notification.',

apps/api/src/app/events/dtos/trigger-event-to-all-request.dto.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Type } from 'class-transformer';
33
import { ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger';
44
import { TriggerRecipientSubscriber, TriggerTenantContext } from '@novu/shared';
55

6-
import { SubscriberPayloadDto, TenantPayloadDto } from './trigger-event-request.dto';
6+
import { SubscriberPayloadDto, TenantPayloadDto, TriggerOverrides } from './trigger-event-request.dto';
77

88
export class TriggerEventToAllRequestDto {
99
@ApiProperty({
@@ -40,10 +40,16 @@ export class TriggerEventToAllRequestDto {
4040
},
4141
},
4242
},
43+
type: TriggerOverrides,
44+
additionalProperties: {
45+
type: 'object',
46+
additionalProperties: true,
47+
},
48+
required: false,
4349
})
4450
@IsObject()
4551
@IsOptional()
46-
overrides?: Record<string, Record<string, unknown>>;
52+
overrides?: TriggerOverrides;
4753

4854
@ApiProperty({
4955
description: 'A unique identifier for this transaction, we will generated a UUID if not provided.',

apps/api/src/app/events/events.controller.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class EventsController {
138138
})
139139
@ApiCreatedResponse({
140140
description: 'Broadcast request has been registered successfully ',
141-
type: TriggerEventResponseDto, // Specify the response type
141+
type: TriggerEventResponseDto,
142142
})
143143
async broadcastEventToAll(
144144
@UserSession() user: UserSessionData,

apps/api/src/app/events/usecases/parse-event-request/parse-event-request.command.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
TriggerRecipientSubscriber,
77
TriggerRequestCategoryEnum,
88
TriggerTenantContext,
9+
TriggerOverrides,
910
} from '@novu/shared';
1011

1112
import { EnvironmentWithUserCommand } from '../../../shared/commands/project.command';
@@ -19,7 +20,7 @@ export class ParseEventRequestBaseCommand extends EnvironmentWithUserCommand {
1920
payload: any; // eslint-disable-line @typescript-eslint/no-explicit-any
2021

2122
@IsDefined()
22-
overrides: Record<string, Record<string, unknown>>;
23+
overrides: TriggerOverrides;
2324

2425
@IsString()
2526
@IsOptional()

apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.command.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { IsDefined, IsObject, IsOptional, IsString } from 'class-validator';
2-
import { TriggerRecipientSubscriber, TriggerTenantContext } from '@novu/shared';
2+
import { TriggerOverrides, TriggerRecipientSubscriber, TriggerTenantContext } from '@novu/shared';
33

44
import { EnvironmentWithUserCommand } from '../../../shared/commands/project.command';
55

@@ -17,7 +17,7 @@ export class TriggerEventToAllCommand extends EnvironmentWithUserCommand {
1717

1818
@IsObject()
1919
@IsOptional()
20-
overrides: Record<string, Record<string, unknown>>;
20+
overrides?: TriggerOverrides;
2121

2222
@IsOptional()
2323
actor?: TriggerRecipientSubscriber | null;

apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.usecase.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import { Injectable } from '@nestjs/common';
2-
import { SubscriberRepository } from '@novu/dal';
32
import { AddressingTypeEnum, TriggerEventStatusEnum, TriggerRequestCategoryEnum } from '@novu/shared';
43

54
import { TriggerEventToAllCommand } from './trigger-event-to-all.command';
65
import { ParseEventRequest, ParseEventRequestBroadcastCommand } from '../parse-event-request';
76

87
@Injectable()
98
export class TriggerEventToAll {
10-
constructor(
11-
private subscriberRepository: SubscriberRepository,
12-
private parseEventRequest: ParseEventRequest
13-
) {}
9+
constructor(private parseEventRequest: ParseEventRequest) {}
1410

1511
public async execute(command: TriggerEventToAllCommand) {
1612
await this.parseEventRequest.execute(

apps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,14 @@ export class SendMessageChat extends SendMessageBase {
450450
...(command.overrides[integration?.channel] || {}),
451451
...(command.overrides[integration?.providerId] || {}),
452452
};
453-
const bridgeProviderData = command.bridgeData?.providers?.[integration.providerId] || {};
454453

455454
const result = await chatHandler.send({
456-
bridgeProviderData,
455+
bridgeProviderData: this.combineOverrides(
456+
command.bridgeData,
457+
command.overrides,
458+
command.step.stepId,
459+
integration.providerId
460+
),
457461
phoneNumber,
458462
customData: overrides,
459463
webhookUrl: chatWebhookUrl,

apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -457,10 +457,17 @@ export class SendMessageEmail extends SendMessageBase {
457457
): Promise<SendMessageResult> {
458458
const mailFactory = new MailFactory();
459459
const mailHandler = mailFactory.getHandler(this.buildFactoryIntegration(integration), mailData.from);
460-
const bridgeProviderData = command.bridgeData?.providers?.[integration.providerId] || {};
461460

462461
try {
463-
const result = await mailHandler.send({ ...mailData, bridgeProviderData });
462+
const result = await mailHandler.send({
463+
...mailData,
464+
bridgeProviderData: this.combineOverrides(
465+
command.bridgeData,
466+
command.overrides,
467+
command.step.stepId,
468+
integration.providerId
469+
),
470+
});
464471

465472
Logger.verbose({ command }, 'Email message has been sent', LOG_CONTEXT);
466473

apps/worker/src/app/workflow/usecases/send-message/send-message-push.usecase.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,6 @@ export class SendMessagePush extends SendMessageBase {
292292
try {
293293
const pushHandler = this.getIntegrationHandler(integration);
294294
const bridgeOutputs = command.bridgeData?.outputs;
295-
const bridgeProviderData = command.bridgeData?.providers?.[integration.providerId] || {};
296295

297296
const result = await pushHandler.send({
298297
target: [deviceToken],
@@ -302,7 +301,12 @@ export class SendMessagePush extends SendMessageBase {
302301
overrides,
303302
subscriber,
304303
step,
305-
bridgeProviderData,
304+
bridgeProviderData: this.combineOverrides(
305+
command.bridgeData,
306+
command.overrides,
307+
command.step.stepId,
308+
integration.providerId
309+
),
306310
});
307311

308312
await this.createExecutionDetails.execute(

apps/worker/src/app/workflow/usecases/send-message/send-message-sms.usecase.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@ import { Injectable } from '@nestjs/common';
22
import { addBreadcrumb } from '@sentry/node';
33
import { ModuleRef } from '@nestjs/core';
44

5-
import {
6-
MessageRepository,
7-
NotificationStepEntity,
8-
SubscriberRepository,
9-
MessageEntity,
10-
IntegrationEntity,
11-
} from '@novu/dal';
5+
import { MessageRepository, SubscriberRepository, MessageEntity, IntegrationEntity } from '@novu/dal';
126
import { ChannelTypeEnum, LogCodeEnum, ExecutionDetailsSourceEnum, ExecutionDetailsStatusEnum } from '@novu/shared';
137
import {
148
InstrumentUsecase,
@@ -299,15 +293,19 @@ export class SendMessageSms extends SendMessageBase {
299293
if (!smsHandler) {
300294
throw new PlatformException(`Sms handler for provider ${integration.providerId} is not found`);
301295
}
302-
const bridgeProviderData = command.bridgeData?.providers?.[integration.providerId] || {};
303296

304297
const result = await smsHandler.send({
305298
to: overrides.to || phone,
306299
from: overrides.from || integration.credentials.from,
307300
content: bridgeBody || overrides.content || content,
308301
id: message._id,
309302
customData: overrides.customData || {},
310-
bridgeProviderData,
303+
bridgeProviderData: this.combineOverrides(
304+
command.bridgeData,
305+
command.overrides,
306+
command.step.stepId,
307+
integration.providerId
308+
),
311309
});
312310

313311
await this.createExecutionDetails.execute(

apps/worker/src/app/workflow/usecases/send-message/send-message.base.ts

+14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@novu/shared';
2020
import { format } from 'date-fns';
2121
import i18next from 'i18next';
22+
import { merge } from 'lodash';
2223

2324
import {
2425
CreateExecutionDetails,
@@ -48,6 +49,19 @@ export abstract class SendMessageBase extends SendMessageType {
4849
super(messageRepository, createExecutionDetails);
4950
}
5051

52+
protected combineOverrides(
53+
bridgeData: Record<string, any> | null | undefined,
54+
overrides: Record<string, any> | undefined,
55+
stepId: string | undefined,
56+
integrationId: string
57+
): Record<string, unknown> {
58+
const bridgeProviderData = bridgeData?.providers?.[integrationId] || {};
59+
const workflowGlobalProviderOverrides = overrides?.providers?.[integrationId] || {};
60+
const triggerOverrides = stepId ? overrides?.steps?.[stepId]?.providers[integrationId] || {} : {};
61+
62+
return merge({}, bridgeProviderData, workflowGlobalProviderOverrides, triggerOverrides);
63+
}
64+
5165
protected async getIntegration(params: {
5266
id?: string;
5367
providerId?: ProvidersIdEnum;

apps/worker/src/app/workflow/usecases/send-message/send-message.command.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IsDefined, IsOptional, IsString } from 'class-validator';
22
import { NotificationStepEntity, JobEntity } from '@novu/dal';
33
import { EnvironmentWithUserCommand } from '@novu/application-generic';
44
import { ExecuteOutput } from '@novu/framework/internal';
5-
import { WorkflowPreferences } from '@novu/shared';
5+
import { WorkflowPreferences, TriggerOverrides } from '@novu/shared';
66

77
export class SendMessageCommand extends EnvironmentWithUserCommand {
88
@IsDefined()
@@ -16,7 +16,7 @@ export class SendMessageCommand extends EnvironmentWithUserCommand {
1616
compileContext?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
1717

1818
@IsDefined()
19-
overrides: Record<string, Record<string, unknown>>;
19+
overrides: TriggerOverrides;
2020

2121
@IsDefined()
2222
step: NotificationStepEntity;

apps/worker/src/app/workflow/usecases/subscriber-job-bound/subscriber-job-bound.command.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ITenantDefine,
66
StatelessControls,
77
SubscriberSourceEnum,
8+
TriggerOverrides,
89
TriggerRequestCategoryEnum,
910
} from '@novu/shared';
1011
import { SubscriberEntity } from '@novu/dal';
@@ -24,7 +25,7 @@ export class SubscriberJobBoundCommand extends EnvironmentWithUserCommand {
2425
identifier: string;
2526

2627
@IsDefined()
27-
overrides: Record<string, Record<string, unknown>>;
28+
overrides: TriggerOverrides;
2829

2930
@IsOptional()
3031
@ValidateNested()

libs/application-generic/src/dtos/process-subscriber-job.dto.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import {
33
ITenantDefine,
44
StatelessControls,
55
SubscriberSourceEnum,
6+
TriggerOverrides,
67
TriggerRequestCategoryEnum,
78
} from '@novu/shared';
89
import { SubscriberEntity } from '@novu/dal';
910
import { DiscoverWorkflowOutput } from '@novu/framework/internal';
1011

11-
import {
12-
IBulkJobParams,
13-
IJobParams,
14-
} from '../services/queues/queue-base.service';
12+
import { IBulkJobParams, IJobParams } from '../services/queues/queue-base.service';
1513

1614
export interface IProcessSubscriberDataDto {
1715
environmentId: string;
@@ -21,7 +19,7 @@ export interface IProcessSubscriberDataDto {
2119
transactionId: string;
2220
identifier: string;
2321
payload: any;
24-
overrides: Record<string, Record<string, unknown>>;
22+
overrides: TriggerOverrides;
2523
tenant?: ITenantDefine;
2624
actor?: SubscriberEntity;
2725
subscriber: ISubscribersDefine;

0 commit comments

Comments
 (0)