Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trigger Workflows with Webhooks #5248

Draft
wants to merge 12 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/api/package.json
Expand Up @@ -47,6 +47,7 @@
"@novu/shared": "^0.23.1",
"@novu/stateless": "^0.23.1",
"@novu/testing": "^0.23.1",
"@paralleldrive/cuid2": "^2.2.2",
"@sendgrid/mail": "^8.1.0",
"@sentry/hub": "^7.40.0",
"@sentry/node": "^7.40.0",
Expand Down
82 changes: 82 additions & 0 deletions apps/api/src/app/webhook/dtos/webhook-request.dto.ts
@@ -0,0 +1,82 @@
import { BaseCommand } from '../../shared/commands/base.command';
import { IsDefined, IsOptional } from 'class-validator';
import { IWebhookTemplateVariable } from '@novu/dal/src/repositories/webhook-trigger/types';

export class SpecificWebhookCommand extends BaseCommand {
@IsDefined()
webhookId: string;
}

export class ListWebhookCommand extends BaseCommand {
@IsDefined()
environmentId: string;

@IsOptional()
notificationTemplateId: string | null;
}

export class UpdateWebhookCommand extends BaseCommand {
@IsDefined()
webhookId: string;

name?: string;

description?: string;

active?: boolean;

variables?: IWebhookTemplateVariable[];
}

export class CreateWebhookCommand extends BaseCommand {
@IsDefined()
webhookId: string;

environmentId: string;

organizationId: string;

templateId: string;

name: string;

description: string;

active?: boolean;

subscribers: string[];

variables?: IWebhookTemplateVariable[];
}

export interface IUpdateWebhookBody {
webhookId: string;

name: string;

description: string;

active?: boolean;

variables?: IWebhookTemplateVariable[];
}

export interface ICreateWebhookBody {
webhookId: string;

environmentId: string;

organizationId: string;

templateId: string;

name: string;

description: string;

subscribers: string[];

active?: boolean;

variables?: IWebhookTemplateVariable[];
}
38 changes: 38 additions & 0 deletions apps/api/src/app/webhook/dtos/webhook-responce.dto.ts
@@ -0,0 +1,38 @@
import { IsDefined, IsString, IsOptional, IsBoolean } from 'class-validator';
import { IWebhookTemplateVariable } from '@novu/dal/src/repositories/webhook-trigger/types';

export class WebhookResponseDto {
@IsDefined()
@IsString()
webhookId: string;

@IsDefined()
@IsString()
environmentId: string;

@IsDefined()
@IsString()
organizationId: string;

@IsDefined()
@IsString()
templateId: string;

@IsDefined()
@IsString()
name: string;

@IsDefined()
@IsString()
description: string;

@IsDefined()
@IsBoolean()
active: boolean;

@IsDefined()
subscribers: string[];

@IsOptional()
variables: IWebhookTemplateVariable | undefined;
}
16 changes: 16 additions & 0 deletions apps/api/src/app/webhook/usecases/convert-responce.usecase.ts
@@ -0,0 +1,16 @@
import { WebhookResponseDto } from '../dtos/webhook-responce.dto';

Check warning on line 1 in apps/api/src/app/webhook/usecases/convert-responce.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (responce)
import { WebhookTriggerEntity } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.entity';

export function convertToDTO(entity: WebhookTriggerEntity): WebhookResponseDto {
return {
webhookId: entity.token,
environmentId: entity._environmentId,
organizationId: entity._organizationId,
templateId: entity._templateId,
name: entity.name,
description: entity.description,
active: entity.active,
variables: entity.variables,
subscribers: entity.subscribers,
} as WebhookResponseDto;
}
29 changes: 29 additions & 0 deletions apps/api/src/app/webhook/usecases/create-webhook.usecase.ts
@@ -0,0 +1,29 @@
import { InstrumentUsecase } from '@novu/application-generic';
import { ApiException } from '../../shared/exceptions/api.exception';
import { CreateWebhookCommand, SpecificWebhookCommand, UpdateWebhookCommand } from '../dtos/webhook-request.dto';
import { WebhookTriggerRepository } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.repository';
import { GenID } from './gen-id.usecase';

export class CreateWebhook {
constructor(private webhookRepository: WebhookTriggerRepository, private genId: GenID) {}

@InstrumentUsecase()
async execute(command: CreateWebhookCommand) {
return await this.webhookRepository.create({
_environmentId: command.environmentId,
_organizationId: command.organizationId,
_templateId: command.environmentId,
_creatorId: 'API',
name: command.name,
description: command.description,
active: command.active ?? false,
variables: command.variables ?? [],
token: this.genId.execute(
SpecificWebhookCommand.create({
webhookId: command.webhookId,
})
),
subscribers: command.subscribers,
});
}
}
23 changes: 23 additions & 0 deletions apps/api/src/app/webhook/usecases/gen-id.usecase.ts
@@ -0,0 +1,23 @@
import { InstrumentUsecase } from '@novu/application-generic';
import { SpecificWebhookCommand } from '../dtos/webhook-request.dto';
import { WebhookTriggerRepository } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.repository';
import { createId } from '@paralleldrive/cuid2';

Check warning on line 4 in apps/api/src/app/webhook/usecases/gen-id.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (paralleldrive)

export class GenID {
constructor(private webhookRepository: WebhookTriggerRepository) {}

@InstrumentUsecase()
async execute(command: SpecificWebhookCommand) {
let newID = '';
let idCount = 0;

do {
newID = 'wh-' + createId();
idCount = await this.webhookRepository.count({
_id: command.webhookId,
});
} while (idCount != 0);

return newID;
}
}
Empty file.
22 changes: 22 additions & 0 deletions apps/api/src/app/webhook/usecases/get-webhook.usecase.ts
@@ -0,0 +1,22 @@
import { InstrumentUsecase } from '@novu/application-generic';
import { SpecificWebhookCommand } from '../dtos/webhook-request.dto';
import { WebhookTriggerRepository } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.repository';
import { convertToDTO } from './convert-responce.usecase';

Check warning on line 4 in apps/api/src/app/webhook/usecases/get-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (responce)
import { WebhookResponseDto } from '../dtos/webhook-responce.dto';

Check warning on line 5 in apps/api/src/app/webhook/usecases/get-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (responce)

export class GetWebhook {
constructor(private webhookRepository: WebhookTriggerRepository) {}

@InstrumentUsecase()
async execute(command: SpecificWebhookCommand): Promise<WebhookResponseDto | null> {
const webhook = await this.webhookRepository.findOne({
_id: command.webhookId,
});

if (webhook) {
return convertToDTO(webhook);
}

return webhook;
}
}
27 changes: 27 additions & 0 deletions apps/api/src/app/webhook/usecases/list-webhook.usecase.ts
@@ -0,0 +1,27 @@
import { InstrumentUsecase } from '@novu/application-generic';
import { ListWebhookCommand } from '../dtos/webhook-request.dto';
import { WebhookTriggerRepository } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.repository';
import { WebhookResponseDto } from '../dtos/webhook-responce.dto';

Check warning on line 4 in apps/api/src/app/webhook/usecases/list-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (responce)
import { WebhookTriggerEntity } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.entity';
import { convertToDTO } from './convert-responce.usecase';

Check warning on line 6 in apps/api/src/app/webhook/usecases/list-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (responce)

export class ListWebhook {
constructor(private webhookRepository: WebhookTriggerRepository) {}

@InstrumentUsecase()
async execute(command: ListWebhookCommand): Promise<WebhookResponseDto[]> {
// const whReturn: WebhookResponseDto;
let webhooks: WebhookTriggerEntity[];

if (command.notificationTemplateId) {
webhooks = await this.webhookRepository.getWebhooksPerNotificationTemplate(
command.environmentId,
command.notificationTemplateId
);
} else {
webhooks = await this.webhookRepository.getWebhooksPerEnv(command.environmentId);
}

return webhooks.map((wh) => convertToDTO(wh));
}
}
34 changes: 34 additions & 0 deletions apps/api/src/app/webhook/usecases/regen-webhook.usecase.ts
@@ -0,0 +1,34 @@
import { InstrumentUsecase } from '@novu/application-generic';
import { SpecificWebhookCommand } from '../dtos/webhook-request.dto';
import { WebhookTriggerRepository } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.repository';
import { NotFoundException } from '@nestjs/common';
import { createId } from '@paralleldrive/cuid2';

Check warning on line 5 in apps/api/src/app/webhook/usecases/regen-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (paralleldrive)
import { GenID } from './gen-id.usecase';

export class RegenWebhook {

Check warning on line 8 in apps/api/src/app/webhook/usecases/regen-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (Regen)
constructor(private webhookRepository: WebhookTriggerRepository, private genId: GenID) {}

@InstrumentUsecase()
async execute(command: SpecificWebhookCommand) {
const webhook = await this.webhookRepository.findOne({
_id: command.webhookId,
});

if (!webhook) {
throw new NotFoundException('Webhook not found');
}

return await this.webhookRepository.update(
{
_id: command.webhookId,
},
{
token: this.genId.execute(
SpecificWebhookCommand.create({
webhookId: command.webhookId,
})
),
}
);
}
}
32 changes: 32 additions & 0 deletions apps/api/src/app/webhook/usecases/update-webhook.usecase.ts
@@ -0,0 +1,32 @@
import { InstrumentUsecase } from '@novu/application-generic';
import { ApiException } from '../../shared/exceptions/api.exception';
import { UpdateWebhookCommand } from '../dtos/webhook-request.dto';
import { WebhookTriggerRepository } from '@novu/dal/src/repositories/webhook-trigger/webhook-trigger.repository';
import { convertToDTO } from './convert-responce.usecase';

Check warning on line 5 in apps/api/src/app/webhook/usecases/update-webhook.usecase.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (responce)

export class UpdateWebhook {
constructor(private webhookRepository: WebhookTriggerRepository) {}

@InstrumentUsecase()
async execute(command: UpdateWebhookCommand) {
const webhook = await this.webhookRepository.findOne({
token: command.webhookId,
});

if (!webhook) {
throw new ApiException('Webhook not found', 'WEBHOOK_NOT_FOUND');
}

await this.webhookRepository.update(
{
token: command.webhookId,
},
{
name: command.name ?? webhook.name,
description: command.description ?? webhook.description,
active: command.active ?? webhook.active,
variables: command.variables ?? webhook.variables,
}
);
}
}