Skip to content

Commit 3025aa3

Browse files
Merge pull request #341 from godwinameh/godwinameh
feat(webhooks): implement webhook registration and event dispatch system
2 parents 3340b44 + 93ad150 commit 3025aa3

10 files changed

Lines changed: 192 additions & 26 deletions

backend/src/app.module.ts

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
import { Module } from '@nestjs/common';
2-
import { ConfigModule, ConfigService } from '@nestjs/config';
3-
import { TypeOrmModule } from '@nestjs/typeorm';
4-
import { AppController } from './app.controller';
5-
import { AppService } from './app.service';
6-
import { AssetCategoriesModule } from './asset-categories/asset-categories.module';
7-
import { SettingsModule } from './settings/settings.module';
8-
import { AssetCategory } from './asset-categories/asset-category.entity';
9-
import { DepartmentsModule } from './departments/departments.module';
10-
import { Department } from './departments/department.entity';
11-
import { UsersModule } from './users/users.module';
12-
import { User } from './users/entities/user.entity';
13-
import { SearchModule } from './search/search.module';
14-
import { AuthModule } from './auth/auth.module';
15-
import { RiskModule } from './risk/risk.module';
16-
import { ReportingModule } from './reporting/reporting.module';
17-
import { AssetTransfersModule } from './asset-transfers/asset-transfers.module';
18-
import { FileUpload } from './file-uploads/entities/file-upload.entity';
19-
import { Asset } from './assets/entities/assest.entity';
20-
import { Supplier } from './suppliers/entities/supplier.entity';
21-
import { QrBarcodeModule } from './qr-barcode/qr-barcode.module';
22-
import { VendorContractsModule } from './vendor-contracts/vendor-contracts.module';
1+
import { Module } from "@nestjs/common";
2+
import { ConfigModule, ConfigService } from "@nestjs/config";
3+
import { TypeOrmModule } from "@nestjs/typeorm";
4+
import { ScheduleModule } from "@nestjs/schedule";
5+
import { AppController } from "./app.controller";
6+
import { AppService } from "./app.service";
7+
import { UsersModule } from "./users/users.module";
8+
import { AuthModule } from "./auth/auth.module";
9+
import { ApiKeysModule } from "./api-keys/api-keys.module";
10+
import { OrganizationUnitsModule } from "./organization-units/organization-units.module";
11+
import { ChangeLogModule } from "./change-log/change-log.module";
12+
import { BarcodeModule } from "./barcode/barcode.module";
13+
import { ComplianceModule } from "./compliance/compliance.module";
14+
import { MobileDevicesModule } from "./mobile-devices/mobile-devices.module";
15+
import { PolicyDocumentsModule } from "./policy-documents/policy-documents.module";
16+
import { DeviceHealthModule } from "./device-health/device-health.module";
17+
import { QRCodeModule } from "./QR-Code/qrcode.module";
18+
import { NotificationsModule } from "./notifications/notifications.module";
19+
import { StatusHistoryModule } from "./status-history/status-history.module";
20+
import { DisposalRegistryModule } from "./disposal-registry/disposal-registry.module";
21+
import { VendorDirectoryModule } from "./vendor-directory/vendor-directory.module";
22+
import { WebhooksModule } from "./webhooks/webhooks.module";
2323

2424
@Module({
2525
imports: [
@@ -54,10 +54,20 @@ import { VendorContractsModule } from './vendor-contracts/vendor-contracts.modul
5454
UsersModule,
5555
SearchModule,
5656
AuthModule,
57-
RiskModule,
58-
ReportingModule,
59-
QrBarcodeModule,
60-
VendorContractsModule,
57+
ApiKeysModule,
58+
OrganizationUnitsModule,
59+
ChangeLogModule,
60+
BarcodeModule,
61+
ComplianceModule,
62+
MobileDevicesModule,
63+
PolicyDocumentsModule,
64+
DeviceHealthModule,
65+
QRCodeModule,
66+
NotificationsModule,
67+
StatusHistoryModule,
68+
DisposalRegistryModule,
69+
VendorDirectoryModule,
70+
WebhooksModule,
6171
],
6272
controllers: [AppController],
6373
providers: [AppService],
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
import { EventEmitter2 } from '@nestjs/event-emitter';
3+
4+
export class AssetsService {
5+
constructor(private repo: any, private eventEmitter: EventEmitter2) {
6+
// Initialization logic if needed
7+
}
8+
9+
createAsset(data) {
10+
const asset = this.repo.save(data);
11+
this.eventEmitter.emit('asset.created', asset);
12+
}
13+
}
14+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// webhooks/dto/create-webhook.dto.ts
2+
import { IsUrl, IsString } from 'class-validator';
3+
4+
export class CreateWebhookDto {
5+
@IsUrl()
6+
url: string;
7+
8+
@IsString()
9+
eventType: string;
10+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// webhooks/entities/webhook.entity.ts
2+
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
3+
4+
@Entity('webhooks')
5+
export class Webhook {
6+
@PrimaryGeneratedColumn('uuid')
7+
id: string;
8+
9+
@Column()
10+
url: string;
11+
12+
@Column() // e.g. "asset.created", "asset.updated"
13+
eventType: string;
14+
15+
@Column({ default: true })
16+
isActive: boolean;
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { WebhooksController } from './webhooks.controller';
3+
4+
describe('WebhooksController', () => {
5+
let controller: WebhooksController;
6+
7+
beforeEach(async () => {
8+
const module: TestingModule = await Test.createTestingModule({
9+
controllers: [WebhooksController],
10+
}).compile();
11+
12+
controller = module.get<WebhooksController>(WebhooksController);
13+
});
14+
15+
it('should be defined', () => {
16+
expect(controller).toBeDefined();
17+
});
18+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
import { Controller, Post, Body, Get } from '@nestjs/common';
3+
import { WebhooksService } from './webhooks.service';
4+
import { CreateWebhookDto } from './dto/create-webhook.dto';
5+
6+
@Controller('webhooks')
7+
export class WebhooksController {
8+
constructor(private webhookService: WebhooksService) {}
9+
10+
@Post()
11+
registerWebhook(@Body() dto: CreateWebhookDto) {
12+
return this.webhookService.register(dto);
13+
}
14+
15+
@Get()
16+
findAll() {
17+
return this.webhookService.findAll();
18+
}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// webhooks/webhooks.listener.ts
2+
import { OnEvent } from '@nestjs/event-emitter';
3+
import { WebhooksService } from './webhooks.service';
4+
import { Injectable } from '@nestjs/common';
5+
6+
@Injectable()
7+
export class WebhookListener {
8+
constructor(private webhookService: WebhooksService) {}
9+
10+
@OnEvent('asset.created')
11+
handleNewAsset(asset) {
12+
this.webhookService.triggerEvent('asset.created', asset);
13+
}
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Module } from '@nestjs/common';
2+
import { WebhooksController } from './webhooks.controller';
3+
import { WebhooksService } from './webhooks.service';
4+
import { WebhookListener } from './webhooks.listener';
5+
import { TypeOrmModule } from '@nestjs/typeorm';
6+
import { Webhook } from './entities/webhook.entity';
7+
8+
@Module({
9+
imports: [TypeOrmModule.forFeature([Webhook])],
10+
controllers: [WebhooksController],
11+
providers: [WebhooksService, WebhookListener]
12+
})
13+
export class WebhooksModule {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { WebhooksService } from './webhooks.service';
3+
4+
describe('WebhooksService', () => {
5+
let service: WebhooksService;
6+
7+
beforeEach(async () => {
8+
const module: TestingModule = await Test.createTestingModule({
9+
providers: [WebhooksService],
10+
}).compile();
11+
12+
service = module.get<WebhooksService>(WebhooksService);
13+
});
14+
15+
it('should be defined', () => {
16+
expect(service).toBeDefined();
17+
});
18+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { Repository } from 'typeorm';
3+
import { Webhook } from './entities/webhook.entity';
4+
import { InjectRepository } from '@nestjs/typeorm';
5+
import { CreateWebhookDto } from './dto/create-webhook.dto';
6+
import axios from 'axios';
7+
8+
@Injectable()
9+
export class WebhooksService {
10+
constructor(
11+
@InjectRepository(Webhook) private repo: Repository<Webhook>,
12+
) {}
13+
14+
register(dto: CreateWebhookDto) {
15+
return this.repo.save(dto);
16+
}
17+
18+
findAll() {
19+
return this.repo.find();
20+
}
21+
22+
async triggerEvent(eventType: string, payload: any) {
23+
const webhooks = await this.repo.find({ where: { eventType, isActive: true }});
24+
25+
for (const hook of webhooks) {
26+
try {
27+
await axios.post(hook.url, payload);
28+
} catch (e) {
29+
console.error(`Webhook failed: ${hook.url}`, e.message);
30+
}
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)