Skip to content

Commit c89a454

Browse files
committed
fix: base test
1 parent d8359d5 commit c89a454

File tree

7 files changed

+132
-12
lines changed

7 files changed

+132
-12
lines changed

apps/api/src/app/webhooks/dtos/webhook-payload.dto.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { ApiProperty } from '@nestjs/swagger';
22
import { WorkflowResponseDto } from '../../workflows-v2/dtos/workflow-response.dto';
33

4-
enum WebhookEventEnum {
4+
export enum WebhookEventEnum {
55
WORKFLOW_CREATED = 'workflow.created',
66
WORKFLOW_UPDATED = 'workflow.updated',
77
WORKFLOW_DELETED = 'workflow.deleted',
88
}
99

10-
enum WebhookObjectTypeEnum {
10+
export enum WebhookObjectTypeEnum {
1111
WORKFLOW = 'workflow',
1212
}
1313

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { IsDefined, IsString, IsEnum, ValidateNested } from 'class-validator';
2+
import { Type } from 'class-transformer';
3+
import { BaseCommand } from '@novu/application-generic';
4+
5+
import { WorkflowResponseDto } from '../../../workflows-v2/dtos/workflow-response.dto'; // Corrected path
6+
import { WebhookEventEnum, WebhookObjectTypeEnum } from '../../dtos/webhook-payload.dto';
7+
import { EnvironmentCommand } from '../../../shared/commands/project.command';
8+
9+
export class SendWebhookMessageCommand extends EnvironmentCommand {
10+
@IsEnum(WebhookEventEnum)
11+
eventType: WebhookEventEnum;
12+
13+
@IsDefined()
14+
@IsEnum(WebhookObjectTypeEnum)
15+
objectType: WebhookObjectTypeEnum;
16+
17+
/*
18+
* Assuming the payload data structure matches WorkflowResponseDto for now
19+
* This might need to be made more generic if other object types are introduced
20+
*/
21+
@IsDefined()
22+
@ValidateNested()
23+
@Type(() => WorkflowResponseDto)
24+
payload: WorkflowResponseDto;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Inject, Injectable, Logger, Scope } from '@nestjs/common';
2+
import { Svix } from 'svix';
3+
import { v4 as uuidv4 } from 'uuid';
4+
5+
import { SendWebhookMessageCommand } from './send-webhook-message.command';
6+
import { WrapperDto } from '../../dtos/webhook-payload.dto';
7+
8+
const LOG_CONTEXT = 'SendWebhookMessageUseCase';
9+
10+
@Injectable({
11+
scope: Scope.REQUEST, // Assuming command context might be needed later
12+
})
13+
export class SendWebhookMessage {
14+
constructor(@Inject('SVIX_CLIENT') private svix: Svix) {}
15+
16+
async execute(command: SendWebhookMessageCommand): Promise<{ eventId: string } | undefined> {
17+
const eventId = `evt_${uuidv4().replace(/-/g, '')}`;
18+
19+
const webhookPayload: WrapperDto<any> = {
20+
type: command.eventType,
21+
object: command.objectType,
22+
data: command.payload,
23+
timestamp: new Date().toISOString(),
24+
environmentId: command.environmentId,
25+
};
26+
27+
console.log('webhookPayload', webhookPayload);
28+
29+
try {
30+
Logger.log(
31+
`Attempting to send webhook ${command.eventType} for application ${command.organizationId}-${command.environmentId}, Event ID: ${eventId}`,
32+
LOG_CONTEXT
33+
);
34+
35+
const message = await this.svix.message.create(`${command.organizationId}-${command.environmentId}`, {
36+
eventType: command.eventType,
37+
eventId,
38+
payload: webhookPayload,
39+
});
40+
41+
Logger.log(
42+
`Successfully sent webhook ${command.eventType}. Svix Message ID: ${message.id}, Event ID: ${eventId}`,
43+
LOG_CONTEXT
44+
);
45+
46+
return { eventId };
47+
} catch (error: any) {
48+
Logger.error(
49+
`Failed to send webhook ${command.eventType} for application ${
50+
command.applicationId
51+
}. Error: ${error.message}, Event ID: ${eventId}`,
52+
error.stack,
53+
LOG_CONTEXT
54+
);
55+
/*
56+
* Depending on requirements, you might want to throw the error,
57+
* return a specific error object, or handle it differently.
58+
*/
59+
throw error;
60+
}
61+
}
62+
}

apps/api/src/app/webhooks/webhooks.module.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Svix } from 'svix'; // Import Svix SDK
33
import { SharedModule } from '../shared/shared.module';
44
import { WebhooksController } from './webhooks.controller';
55
import { GetWebhookPortalTokenUsecase } from './usecases/get-webhook-portal-token/get-webhook-portal-token.usecase';
6+
import { SendWebhookMessage } from './usecases/send-webhook-message/send-webhook-message.usecase';
67

78
// Define the custom provider for the Svix client
89
const svixProvider: Provider = {
@@ -20,10 +21,7 @@ const svixProvider: Provider = {
2021
@Module({
2122
imports: [SharedModule],
2223
controllers: [WebhooksController],
23-
providers: [
24-
GetWebhookPortalTokenUsecase,
25-
svixProvider, // Add the Svix client provider
26-
],
27-
exports: [],
24+
providers: [GetWebhookPortalTokenUsecase, svixProvider, SendWebhookMessage],
25+
exports: [SendWebhookMessage],
2826
})
2927
export class WebhooksModule {}

apps/api/src/app/workflows-v2/usecases/patch-workflow/patch-workflow.usecase.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,40 @@ import { Injectable } from '@nestjs/common';
22
import { UserSessionData, WorkflowStatusEnum } from '@novu/shared';
33
import { NotificationTemplateEntity, NotificationTemplateRepository } from '@novu/dal';
44
import { GetWorkflowWithPreferencesUseCase, WorkflowWithPreferencesResponseDto } from '@novu/application-generic';
5+
import { SendWebhookMessage } from '../../../webhooks/usecases/send-webhook-message/send-webhook-message.usecase';
56
import { PatchWorkflowCommand } from './patch-workflow.command';
67
import { GetWorkflowUseCase } from '../get-workflow';
78
import { WorkflowResponseDto } from '../../dtos';
9+
import { WebhookEventEnum, WebhookObjectTypeEnum } from '../../../webhooks/dtos/webhook-payload.dto';
810

911
@Injectable()
1012
export class PatchWorkflowUsecase {
1113
constructor(
1214
private getWorkflowWithPreferencesUseCase: GetWorkflowWithPreferencesUseCase,
1315
private notificationTemplateRepository: NotificationTemplateRepository,
14-
private getWorkflowUseCase: GetWorkflowUseCase
16+
private getWorkflowUseCase: GetWorkflowUseCase,
17+
private sendWebhookMessage: SendWebhookMessage
1518
) {}
1619

1720
async execute(command: PatchWorkflowCommand): Promise<WorkflowResponseDto> {
1821
const persistedWorkflow = await this.fetchWorkflow(command);
1922
const transientWorkflow = this.patchWorkflowFields(persistedWorkflow, command);
2023
await this.persistWorkflow(transientWorkflow, command.user);
2124

22-
return await this.getWorkflowUseCase.execute({
25+
const updatedWorkflow = await this.getWorkflowUseCase.execute({
2326
workflowIdOrInternalId: command.workflowIdOrInternalId,
2427
user: command.user,
2528
});
29+
30+
await this.sendWebhookMessage.execute({
31+
eventType: WebhookEventEnum.WORKFLOW_UPDATED,
32+
objectType: WebhookObjectTypeEnum.WORKFLOW,
33+
payload: updatedWorkflow,
34+
organizationId: command.user.organizationId,
35+
environmentId: command.user.environmentId,
36+
});
37+
38+
return updatedWorkflow;
2639
}
2740

2841
private patchWorkflowFields(

apps/api/src/app/workflows-v2/usecases/upsert-workflow/upsert-workflow.usecase.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import { BuildStepIssuesUsecase } from '../build-step-issues/build-step-issues.u
3737
import { GetWorkflowCommand, GetWorkflowUseCase } from '../get-workflow';
3838
import { UpsertStepDataCommand, UpsertWorkflowCommand } from './upsert-workflow.command';
3939
import { StepIssuesDto, WorkflowResponseDto } from '../../dtos';
40+
import { SendWebhookMessage } from '../../../webhooks/usecases/send-webhook-message/send-webhook-message.usecase';
41+
import { WebhookEventEnum, WebhookObjectTypeEnum } from '../../../webhooks/dtos/webhook-payload.dto';
4042

4143
@Injectable()
4244
export class UpsertWorkflowUseCase {
@@ -49,7 +51,8 @@ export class UpsertWorkflowUseCase {
4951
private buildStepIssuesUsecase: BuildStepIssuesUsecase,
5052
private controlValuesRepository: ControlValuesRepository,
5153
private upsertControlValuesUseCase: UpsertControlValuesUseCase,
52-
private analyticsService: AnalyticsService
54+
private analyticsService: AnalyticsService,
55+
private sendWebhookMessage: SendWebhookMessage
5356
) {}
5457

5558
@InstrumentUsecase()
@@ -84,12 +87,22 @@ export class UpsertWorkflowUseCase {
8487

8588
await this.upsertControlValues(upsertedWorkflow, command);
8689

87-
return await this.getWorkflowUseCase.execute(
90+
const updatedWorkflow = await this.getWorkflowUseCase.execute(
8891
GetWorkflowCommand.create({
8992
workflowIdOrInternalId: upsertedWorkflow._id,
9093
user: command.user,
9194
})
9295
);
96+
97+
await this.sendWebhookMessage.execute({
98+
eventType: WebhookEventEnum.WORKFLOW_UPDATED,
99+
objectType: WebhookObjectTypeEnum.WORKFLOW,
100+
payload: updatedWorkflow,
101+
organizationId: command.user.organizationId,
102+
environmentId: command.user.environmentId,
103+
});
104+
105+
return updatedWorkflow;
93106
}
94107

95108
private async buildCreateWorkflowCommand(command: UpsertWorkflowCommand): Promise<CreateWorkflowCommand> {

apps/api/src/app/workflows-v2/workflow.module.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,20 @@ import { CreateVariablesObject } from './usecases/create-variables-object/create
3535
import { BuildStepIssuesUsecase } from './usecases/build-step-issues/build-step-issues.usecase';
3636
import { WorkflowController } from './workflow.controller';
3737
import { DuplicateWorkflowUseCase } from './usecases/duplicate-workflow/duplicate-workflow.usecase';
38+
import { WebhooksModule } from '../webhooks/webhooks.module';
3839

3940
const DAL_REPOSITORIES = [CommunityOrganizationRepository];
4041

4142
@Module({
42-
imports: [SharedModule, MessageTemplateModule, ChangeModule, AuthModule, BridgeModule, IntegrationModule],
43+
imports: [
44+
SharedModule,
45+
MessageTemplateModule,
46+
ChangeModule,
47+
AuthModule,
48+
BridgeModule,
49+
IntegrationModule,
50+
WebhooksModule,
51+
],
4352
controllers: [WorkflowController],
4453
providers: [
4554
...DAL_REPOSITORIES,

0 commit comments

Comments
 (0)