@valentine-efagene/qshelter-common 2.0.74 → 2.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/client/browser.d.ts +5 -5
- package/dist/generated/client/client.d.ts +5 -5
- package/dist/generated/client/internal/class.d.ts +11 -11
- package/dist/generated/client/internal/class.js +2 -2
- package/dist/generated/client/internal/prismaNamespace.d.ts +95 -95
- package/dist/generated/client/internal/prismaNamespace.js +25 -25
- package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +27 -27
- package/dist/generated/client/internal/prismaNamespaceBrowser.js +25 -25
- package/dist/generated/client/models/Contract.d.ts +155 -155
- package/dist/generated/client/models/index.d.ts +3 -0
- package/dist/generated/client/models/index.js +3 -0
- package/dist/generated/client/models.d.ts +1 -1
- package/dist/src/events/bus/event-bus.service.d.ts +84 -0
- package/dist/src/events/bus/event-bus.service.js +372 -0
- package/dist/src/events/bus/event-bus.types.d.ts +73 -0
- package/dist/src/events/bus/event-bus.types.js +22 -0
- package/dist/src/events/index.d.ts +5 -6
- package/dist/src/events/index.js +7 -8
- package/dist/src/events/notifications/event-publisher.d.ts +41 -0
- package/dist/src/events/notifications/event-publisher.js +111 -0
- package/dist/src/events/notifications/notification-enums.d.ts +46 -0
- package/dist/src/events/notifications/notification-enums.js +59 -0
- package/dist/src/events/notifications/notification-event.d.ts +76 -0
- package/dist/src/events/notifications/notification-event.js +1 -0
- package/dist/src/events/unified/unified-event.service.d.ts +157 -0
- package/dist/src/events/unified/unified-event.service.js +177 -0
- package/dist/src/events/workflow/event-config.service.d.ts +123 -0
- package/dist/src/events/workflow/event-config.service.js +416 -0
- package/dist/src/events/workflow/event-seeder.d.ts +80 -0
- package/dist/src/events/workflow/event-seeder.js +343 -0
- package/dist/src/events/workflow/workflow-event.service.d.ts +230 -0
- package/dist/src/events/workflow/workflow-event.service.js +682 -0
- package/dist/src/events/workflow/workflow-types.d.ts +364 -0
- package/dist/src/events/workflow/workflow-types.js +22 -0
- package/package.json +4 -1
- package/prisma/schema.prisma +87 -79
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './Amenity';
|
|
2
2
|
export * from './ApiKey';
|
|
3
|
+
export * from './ApprovalRequest';
|
|
3
4
|
export * from './Contract';
|
|
4
5
|
export * from './ContractDocument';
|
|
5
6
|
export * from './ContractEvent';
|
|
@@ -34,6 +35,7 @@ export * from './PropertyMedia';
|
|
|
34
35
|
export * from './PropertyPaymentMethod';
|
|
35
36
|
export * from './PropertyPaymentMethodLink';
|
|
36
37
|
export * from './PropertyPaymentMethodPhase';
|
|
38
|
+
export * from './PropertyTransferRequest';
|
|
37
39
|
export * from './PropertyUnit';
|
|
38
40
|
export * from './PropertyVariant';
|
|
39
41
|
export * from './PropertyVariantAmenity';
|
|
@@ -43,6 +45,7 @@ export * from './Role';
|
|
|
43
45
|
export * from './RolePermission';
|
|
44
46
|
export * from './Settings';
|
|
45
47
|
export * from './Social';
|
|
48
|
+
export * from './StepEventAttachment';
|
|
46
49
|
export * from './Tenant';
|
|
47
50
|
export * from './Transaction';
|
|
48
51
|
export * from './User';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './Amenity';
|
|
2
2
|
export * from './ApiKey';
|
|
3
|
+
export * from './ApprovalRequest';
|
|
3
4
|
export * from './Contract';
|
|
4
5
|
export * from './ContractDocument';
|
|
5
6
|
export * from './ContractEvent';
|
|
@@ -34,6 +35,7 @@ export * from './PropertyMedia';
|
|
|
34
35
|
export * from './PropertyPaymentMethod';
|
|
35
36
|
export * from './PropertyPaymentMethodLink';
|
|
36
37
|
export * from './PropertyPaymentMethodPhase';
|
|
38
|
+
export * from './PropertyTransferRequest';
|
|
37
39
|
export * from './PropertyUnit';
|
|
38
40
|
export * from './PropertyVariant';
|
|
39
41
|
export * from './PropertyVariantAmenity';
|
|
@@ -43,6 +45,7 @@ export * from './Role';
|
|
|
43
45
|
export * from './RolePermission';
|
|
44
46
|
export * from './Settings';
|
|
45
47
|
export * from './Social';
|
|
48
|
+
export * from './StepEventAttachment';
|
|
46
49
|
export * from './Tenant';
|
|
47
50
|
export * from './Transaction';
|
|
48
51
|
export * from './User';
|
|
@@ -33,13 +33,13 @@ export type * from './models/StepEventAttachment.js';
|
|
|
33
33
|
export type * from './models/PaymentMethodPhaseDocument.js';
|
|
34
34
|
export type * from './models/Contract.js';
|
|
35
35
|
export type * from './models/ContractPhase.js';
|
|
36
|
+
export type * from './models/ContractEvent.js';
|
|
36
37
|
export type * from './models/DocumentationStep.js';
|
|
37
38
|
export type * from './models/DocumentationStepDocument.js';
|
|
38
39
|
export type * from './models/DocumentationStepApproval.js';
|
|
39
40
|
export type * from './models/ContractInstallment.js';
|
|
40
41
|
export type * from './models/ContractPayment.js';
|
|
41
42
|
export type * from './models/ContractDocument.js';
|
|
42
|
-
export type * from './models/ContractEvent.js';
|
|
43
43
|
export type * from './models/DocumentTemplate.js';
|
|
44
44
|
export type * from './models/OfferLetter.js';
|
|
45
45
|
export type * from './models/ContractTermination.js';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Bus Service
|
|
3
|
+
*
|
|
4
|
+
* Multi-transport event delivery system for serverless microservices.
|
|
5
|
+
* Supports HTTP webhooks, AWS SNS, SQS, and EventBridge.
|
|
6
|
+
*
|
|
7
|
+
* This is a plain TypeScript implementation (no NestJS) designed for Lambda/serverless environments.
|
|
8
|
+
*/
|
|
9
|
+
import { EventHandler, EventResult, EventBusConfig } from './event-bus.types';
|
|
10
|
+
/**
|
|
11
|
+
* Event Bus Service - Multi-transport event delivery
|
|
12
|
+
*/
|
|
13
|
+
export declare class EventBusService {
|
|
14
|
+
private eventHandlers;
|
|
15
|
+
private readonly config;
|
|
16
|
+
private readonly httpClient;
|
|
17
|
+
constructor(config?: Partial<EventBusConfig>);
|
|
18
|
+
/**
|
|
19
|
+
* Register an event handler/endpoint
|
|
20
|
+
*/
|
|
21
|
+
registerHandler(handler: EventHandler): void;
|
|
22
|
+
/**
|
|
23
|
+
* Register multiple handlers at once
|
|
24
|
+
*/
|
|
25
|
+
registerHandlers(handlers: EventHandler[]): void;
|
|
26
|
+
/**
|
|
27
|
+
* Publish an event to all registered handlers
|
|
28
|
+
*/
|
|
29
|
+
publish<T = any>(eventType: string, data: T, options?: {
|
|
30
|
+
tenantId?: number;
|
|
31
|
+
source?: string;
|
|
32
|
+
metadata?: Record<string, any>;
|
|
33
|
+
correlationId?: string;
|
|
34
|
+
}): Promise<EventResult[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Execute a single handler based on its transport type
|
|
37
|
+
*/
|
|
38
|
+
private executeHandler;
|
|
39
|
+
/**
|
|
40
|
+
* Execute HTTP webhook call
|
|
41
|
+
*/
|
|
42
|
+
private executeHttpHandler;
|
|
43
|
+
/**
|
|
44
|
+
* Publish to AWS SNS
|
|
45
|
+
*/
|
|
46
|
+
private executeSnsHandler;
|
|
47
|
+
/**
|
|
48
|
+
* Publish to AWS EventBridge
|
|
49
|
+
*/
|
|
50
|
+
private executeEventBridgeHandler;
|
|
51
|
+
/**
|
|
52
|
+
* Send to AWS SQS
|
|
53
|
+
*/
|
|
54
|
+
private executeSqsHandler;
|
|
55
|
+
/**
|
|
56
|
+
* Execute internal handler function
|
|
57
|
+
*/
|
|
58
|
+
private executeInternalHandler;
|
|
59
|
+
/**
|
|
60
|
+
* Send failed events to dead letter queue
|
|
61
|
+
*/
|
|
62
|
+
private sendToDeadLetterQueue;
|
|
63
|
+
/**
|
|
64
|
+
* Generate unique event ID
|
|
65
|
+
*/
|
|
66
|
+
private generateEventId;
|
|
67
|
+
/**
|
|
68
|
+
* Sleep helper for retries
|
|
69
|
+
*/
|
|
70
|
+
private sleep;
|
|
71
|
+
/**
|
|
72
|
+
* Get all registered handlers
|
|
73
|
+
*/
|
|
74
|
+
getHandlers(): Map<string, EventHandler[]>;
|
|
75
|
+
/**
|
|
76
|
+
* Clear all handlers (useful for testing)
|
|
77
|
+
*/
|
|
78
|
+
clearHandlers(): void;
|
|
79
|
+
}
|
|
80
|
+
export declare function createEventBus(config?: Partial<EventBusConfig>): EventBusService;
|
|
81
|
+
/**
|
|
82
|
+
* Get the singleton EventBus instance (must be created first)
|
|
83
|
+
*/
|
|
84
|
+
export declare function getEventBus(): EventBusService;
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Bus Service
|
|
3
|
+
*
|
|
4
|
+
* Multi-transport event delivery system for serverless microservices.
|
|
5
|
+
* Supports HTTP webhooks, AWS SNS, SQS, and EventBridge.
|
|
6
|
+
*
|
|
7
|
+
* This is a plain TypeScript implementation (no NestJS) designed for Lambda/serverless environments.
|
|
8
|
+
*/
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import * as crypto from 'crypto';
|
|
11
|
+
import { EventTransportType, } from './event-bus.types';
|
|
12
|
+
/**
|
|
13
|
+
* Event Bus Service - Multi-transport event delivery
|
|
14
|
+
*/
|
|
15
|
+
export class EventBusService {
|
|
16
|
+
eventHandlers = new Map();
|
|
17
|
+
config;
|
|
18
|
+
httpClient;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = {
|
|
21
|
+
defaultTransport: config?.defaultTransport || EventTransportType.HTTP,
|
|
22
|
+
defaultTimeout: config?.defaultTimeout || 30000,
|
|
23
|
+
defaultRetries: config?.defaultRetries || 3,
|
|
24
|
+
enableDeadLetterQueue: config?.enableDeadLetterQueue ?? true,
|
|
25
|
+
awsRegion: config?.awsRegion || process.env.AWS_REGION || 'us-east-1',
|
|
26
|
+
};
|
|
27
|
+
this.httpClient = axios.create({
|
|
28
|
+
timeout: this.config.defaultTimeout,
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Register an event handler/endpoint
|
|
36
|
+
*/
|
|
37
|
+
registerHandler(handler) {
|
|
38
|
+
const handlers = this.eventHandlers.get(handler.eventType) || [];
|
|
39
|
+
handlers.push(handler);
|
|
40
|
+
this.eventHandlers.set(handler.eventType, handlers);
|
|
41
|
+
console.log(`[EventBus] Registered ${handler.transport} handler for event: ${handler.eventType}`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Register multiple handlers at once
|
|
45
|
+
*/
|
|
46
|
+
registerHandlers(handlers) {
|
|
47
|
+
handlers.forEach(handler => this.registerHandler(handler));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Publish an event to all registered handlers
|
|
51
|
+
*/
|
|
52
|
+
async publish(eventType, data, options) {
|
|
53
|
+
const eventId = this.generateEventId();
|
|
54
|
+
const payload = {
|
|
55
|
+
eventType,
|
|
56
|
+
eventId,
|
|
57
|
+
timestamp: new Date(),
|
|
58
|
+
tenantId: options?.tenantId,
|
|
59
|
+
source: options?.source || 'event-bus',
|
|
60
|
+
data,
|
|
61
|
+
metadata: options?.metadata,
|
|
62
|
+
correlationId: options?.correlationId || eventId,
|
|
63
|
+
};
|
|
64
|
+
console.log(`[EventBus] Publishing event: ${eventType} [${eventId}]`);
|
|
65
|
+
const handlers = this.eventHandlers.get(eventType) || [];
|
|
66
|
+
if (handlers.length === 0) {
|
|
67
|
+
console.warn(`[EventBus] No handlers registered for event: ${eventType}`);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
// Execute all handlers in parallel (fan-out pattern)
|
|
71
|
+
const results = await Promise.allSettled(handlers.map(handler => this.executeHandler(handler, payload)));
|
|
72
|
+
return results.map((result, index) => {
|
|
73
|
+
if (result.status === 'fulfilled') {
|
|
74
|
+
return result.value;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const handler = handlers[index];
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
eventId,
|
|
81
|
+
error: result.reason?.message || 'Unknown error',
|
|
82
|
+
retryCount: 0,
|
|
83
|
+
executionTime: 0,
|
|
84
|
+
transport: handler.transport,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Execute a single handler based on its transport type
|
|
91
|
+
*/
|
|
92
|
+
async executeHandler(handler, payload) {
|
|
93
|
+
const startTime = Date.now();
|
|
94
|
+
const retryConfig = handler.retryConfig || {
|
|
95
|
+
maxRetries: this.config.defaultRetries,
|
|
96
|
+
retryDelay: 1000,
|
|
97
|
+
backoffMultiplier: 2,
|
|
98
|
+
};
|
|
99
|
+
let lastError;
|
|
100
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
101
|
+
try {
|
|
102
|
+
let response;
|
|
103
|
+
switch (handler.transport) {
|
|
104
|
+
case EventTransportType.HTTP:
|
|
105
|
+
response = await this.executeHttpHandler(handler, payload);
|
|
106
|
+
break;
|
|
107
|
+
case EventTransportType.SNS:
|
|
108
|
+
response = await this.executeSnsHandler(handler, payload);
|
|
109
|
+
break;
|
|
110
|
+
case EventTransportType.EVENTBRIDGE:
|
|
111
|
+
response = await this.executeEventBridgeHandler(handler, payload);
|
|
112
|
+
break;
|
|
113
|
+
case EventTransportType.SQS:
|
|
114
|
+
response = await this.executeSqsHandler(handler, payload);
|
|
115
|
+
break;
|
|
116
|
+
case EventTransportType.INTERNAL:
|
|
117
|
+
response = await this.executeInternalHandler(handler, payload);
|
|
118
|
+
break;
|
|
119
|
+
default:
|
|
120
|
+
throw new Error(`Unsupported transport: ${handler.transport}`);
|
|
121
|
+
}
|
|
122
|
+
const executionTime = Date.now() - startTime;
|
|
123
|
+
console.log(`[EventBus] Event ${payload.eventId} executed successfully via ${handler.transport} (attempt ${attempt + 1}/${retryConfig.maxRetries + 1})`);
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
eventId: payload.eventId,
|
|
127
|
+
statusCode: response?.statusCode || 200,
|
|
128
|
+
response: response?.data,
|
|
129
|
+
retryCount: attempt,
|
|
130
|
+
executionTime,
|
|
131
|
+
transport: handler.transport,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
lastError = error;
|
|
136
|
+
console.error(`[EventBus] Event ${payload.eventId} failed via ${handler.transport} (attempt ${attempt + 1}/${retryConfig.maxRetries + 1}): ${error.message}`);
|
|
137
|
+
if (attempt < retryConfig.maxRetries) {
|
|
138
|
+
const delay = retryConfig.retryDelay * Math.pow(retryConfig.backoffMultiplier, attempt);
|
|
139
|
+
await this.sleep(delay);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// All retries exhausted
|
|
144
|
+
const executionTime = Date.now() - startTime;
|
|
145
|
+
// Send to dead letter queue if enabled
|
|
146
|
+
if (this.config.enableDeadLetterQueue) {
|
|
147
|
+
await this.sendToDeadLetterQueue(handler, payload, lastError);
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
eventId: payload.eventId,
|
|
152
|
+
error: lastError?.message || 'Unknown error',
|
|
153
|
+
retryCount: retryConfig.maxRetries,
|
|
154
|
+
executionTime,
|
|
155
|
+
transport: handler.transport,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Execute HTTP webhook call
|
|
160
|
+
*/
|
|
161
|
+
async executeHttpHandler(handler, payload) {
|
|
162
|
+
if (!handler.endpoint) {
|
|
163
|
+
throw new Error('HTTP handler requires endpoint');
|
|
164
|
+
}
|
|
165
|
+
const headers = {
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
'X-Event-Id': payload.eventId,
|
|
168
|
+
'X-Event-Type': payload.eventType,
|
|
169
|
+
'X-Correlation-Id': payload.correlationId || '',
|
|
170
|
+
...(handler.headers || {}),
|
|
171
|
+
};
|
|
172
|
+
// Add authentication
|
|
173
|
+
if (handler.authentication) {
|
|
174
|
+
switch (handler.authentication.type) {
|
|
175
|
+
case 'bearer':
|
|
176
|
+
headers['Authorization'] = `Bearer ${handler.authentication.credentials}`;
|
|
177
|
+
break;
|
|
178
|
+
case 'api-key':
|
|
179
|
+
headers['X-API-Key'] = handler.authentication.credentials;
|
|
180
|
+
break;
|
|
181
|
+
case 'basic':
|
|
182
|
+
headers['Authorization'] = `Basic ${handler.authentication.credentials}`;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const timeout = handler.timeout || this.config.defaultTimeout;
|
|
187
|
+
const response = await this.httpClient.post(handler.endpoint, payload, {
|
|
188
|
+
headers,
|
|
189
|
+
timeout,
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
statusCode: response.status,
|
|
193
|
+
data: response.data,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Publish to AWS SNS
|
|
198
|
+
*/
|
|
199
|
+
async executeSnsHandler(handler, payload) {
|
|
200
|
+
if (!handler.snsTopicArn) {
|
|
201
|
+
throw new Error('SNS handler requires topic ARN');
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const { SNSClient, PublishCommand } = await import('@aws-sdk/client-sns');
|
|
205
|
+
const client = new SNSClient({ region: this.config.awsRegion });
|
|
206
|
+
const command = new PublishCommand({
|
|
207
|
+
TopicArn: handler.snsTopicArn,
|
|
208
|
+
Message: JSON.stringify(payload),
|
|
209
|
+
MessageAttributes: {
|
|
210
|
+
eventType: { DataType: 'String', StringValue: payload.eventType },
|
|
211
|
+
eventId: { DataType: 'String', StringValue: payload.eventId },
|
|
212
|
+
...(payload.tenantId && { tenantId: { DataType: 'Number', StringValue: String(payload.tenantId) } }),
|
|
213
|
+
...(payload.correlationId && { correlationId: { DataType: 'String', StringValue: payload.correlationId } }),
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
const result = await client.send(command);
|
|
217
|
+
console.log(`[EventBus] Published event ${payload.eventId} to SNS topic: ${handler.snsTopicArn}`);
|
|
218
|
+
return { MessageId: result.MessageId };
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error(`[EventBus] Failed to publish to SNS: ${error.message}`);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Publish to AWS EventBridge
|
|
227
|
+
*/
|
|
228
|
+
async executeEventBridgeHandler(handler, payload) {
|
|
229
|
+
if (!handler.eventBridgeDetail) {
|
|
230
|
+
throw new Error('EventBridge handler requires event bus configuration');
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const { EventBridgeClient, PutEventsCommand } = await import('@aws-sdk/client-eventbridge');
|
|
234
|
+
const client = new EventBridgeClient({ region: this.config.awsRegion });
|
|
235
|
+
const command = new PutEventsCommand({
|
|
236
|
+
Entries: [{
|
|
237
|
+
EventBusName: handler.eventBridgeDetail.eventBusName,
|
|
238
|
+
Source: handler.eventBridgeDetail.source,
|
|
239
|
+
DetailType: handler.eventBridgeDetail.detailType,
|
|
240
|
+
Detail: JSON.stringify(payload),
|
|
241
|
+
}],
|
|
242
|
+
});
|
|
243
|
+
const result = await client.send(command);
|
|
244
|
+
console.log(`[EventBus] Published event ${payload.eventId} to EventBridge: ${handler.eventBridgeDetail.eventBusName}`);
|
|
245
|
+
return { Entries: result.Entries };
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
console.error(`[EventBus] Failed to publish to EventBridge: ${error.message}`);
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Send to AWS SQS
|
|
254
|
+
*/
|
|
255
|
+
async executeSqsHandler(handler, payload) {
|
|
256
|
+
if (!handler.sqsQueueUrl) {
|
|
257
|
+
throw new Error('SQS handler requires queue URL');
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const { SQSClient, SendMessageCommand } = await import('@aws-sdk/client-sqs');
|
|
261
|
+
const client = new SQSClient({ region: this.config.awsRegion });
|
|
262
|
+
const command = new SendMessageCommand({
|
|
263
|
+
QueueUrl: handler.sqsQueueUrl,
|
|
264
|
+
MessageBody: JSON.stringify(payload),
|
|
265
|
+
MessageAttributes: {
|
|
266
|
+
eventType: { DataType: 'String', StringValue: payload.eventType },
|
|
267
|
+
eventId: { DataType: 'String', StringValue: payload.eventId },
|
|
268
|
+
...(payload.tenantId && { tenantId: { DataType: 'Number', StringValue: String(payload.tenantId) } }),
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
const result = await client.send(command);
|
|
272
|
+
console.log(`[EventBus] Sent event ${payload.eventId} to SQS queue: ${handler.sqsQueueUrl}`);
|
|
273
|
+
return { MessageId: result.MessageId };
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.error(`[EventBus] Failed to send to SQS: ${error.message}`);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Execute internal handler function
|
|
282
|
+
*/
|
|
283
|
+
async executeInternalHandler(handler, payload) {
|
|
284
|
+
if (!handler.handler) {
|
|
285
|
+
throw new Error('Internal handler requires handler function');
|
|
286
|
+
}
|
|
287
|
+
return await handler.handler(payload);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Send failed events to dead letter queue
|
|
291
|
+
*/
|
|
292
|
+
async sendToDeadLetterQueue(handler, payload, error) {
|
|
293
|
+
console.error(`[EventBus] Sending event ${payload.eventId} to dead letter queue after exhausting retries`);
|
|
294
|
+
// Log to CloudWatch/console (in Lambda, this goes to CloudWatch Logs)
|
|
295
|
+
console.error({
|
|
296
|
+
message: 'Dead letter event',
|
|
297
|
+
eventId: payload.eventId,
|
|
298
|
+
eventType: payload.eventType,
|
|
299
|
+
handler: {
|
|
300
|
+
transport: handler.transport,
|
|
301
|
+
endpoint: handler.endpoint,
|
|
302
|
+
},
|
|
303
|
+
error: error?.message,
|
|
304
|
+
payload,
|
|
305
|
+
});
|
|
306
|
+
// If DLQ URL is configured, send to SQS
|
|
307
|
+
if (this.config.deadLetterQueueUrl) {
|
|
308
|
+
try {
|
|
309
|
+
const { SQSClient, SendMessageCommand } = await import('@aws-sdk/client-sqs');
|
|
310
|
+
const client = new SQSClient({ region: this.config.awsRegion });
|
|
311
|
+
await client.send(new SendMessageCommand({
|
|
312
|
+
QueueUrl: this.config.deadLetterQueueUrl,
|
|
313
|
+
MessageBody: JSON.stringify({
|
|
314
|
+
payload,
|
|
315
|
+
handler: {
|
|
316
|
+
transport: handler.transport,
|
|
317
|
+
endpoint: handler.endpoint,
|
|
318
|
+
},
|
|
319
|
+
error: error?.message,
|
|
320
|
+
failedAt: new Date().toISOString(),
|
|
321
|
+
}),
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
324
|
+
catch (dlqError) {
|
|
325
|
+
console.error(`[EventBus] Failed to send to DLQ: ${dlqError.message}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Generate unique event ID
|
|
331
|
+
*/
|
|
332
|
+
generateEventId() {
|
|
333
|
+
return `evt_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Sleep helper for retries
|
|
337
|
+
*/
|
|
338
|
+
sleep(ms) {
|
|
339
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get all registered handlers
|
|
343
|
+
*/
|
|
344
|
+
getHandlers() {
|
|
345
|
+
return this.eventHandlers;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Clear all handlers (useful for testing)
|
|
349
|
+
*/
|
|
350
|
+
clearHandlers() {
|
|
351
|
+
this.eventHandlers.clear();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Create a singleton EventBus instance
|
|
356
|
+
*/
|
|
357
|
+
let eventBusInstance = null;
|
|
358
|
+
export function createEventBus(config) {
|
|
359
|
+
if (!eventBusInstance) {
|
|
360
|
+
eventBusInstance = new EventBusService(config);
|
|
361
|
+
}
|
|
362
|
+
return eventBusInstance;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Get the singleton EventBus instance (must be created first)
|
|
366
|
+
*/
|
|
367
|
+
export function getEventBus() {
|
|
368
|
+
if (!eventBusInstance) {
|
|
369
|
+
throw new Error('EventBus not initialized. Call createEventBus() first.');
|
|
370
|
+
}
|
|
371
|
+
return eventBusInstance;
|
|
372
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Bus Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the EventBus system.
|
|
5
|
+
*/
|
|
6
|
+
export declare enum EventTransportType {
|
|
7
|
+
HTTP = "http",// Direct HTTP webhook call
|
|
8
|
+
SNS = "sns",// AWS SNS topic
|
|
9
|
+
EVENTBRIDGE = "eventbridge",// AWS EventBridge
|
|
10
|
+
SQS = "sqs",// AWS SQS queue
|
|
11
|
+
INTERNAL = "internal"
|
|
12
|
+
}
|
|
13
|
+
export declare enum EventStatus {
|
|
14
|
+
PENDING = "pending",
|
|
15
|
+
PROCESSING = "processing",
|
|
16
|
+
SUCCESS = "success",
|
|
17
|
+
FAILED = "failed",
|
|
18
|
+
RETRY = "retry",
|
|
19
|
+
DEAD_LETTER = "dead_letter"
|
|
20
|
+
}
|
|
21
|
+
export interface EventPayload<T = any> {
|
|
22
|
+
eventType: string;
|
|
23
|
+
eventId: string;
|
|
24
|
+
timestamp: Date;
|
|
25
|
+
tenantId?: number;
|
|
26
|
+
source: string;
|
|
27
|
+
data: T;
|
|
28
|
+
metadata?: Record<string, any>;
|
|
29
|
+
correlationId?: string;
|
|
30
|
+
causationId?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface EventHandler {
|
|
33
|
+
eventType: string;
|
|
34
|
+
transport: EventTransportType;
|
|
35
|
+
endpoint?: string;
|
|
36
|
+
snsTopicArn?: string;
|
|
37
|
+
eventBridgeDetail?: {
|
|
38
|
+
eventBusName: string;
|
|
39
|
+
source: string;
|
|
40
|
+
detailType: string;
|
|
41
|
+
};
|
|
42
|
+
sqsQueueUrl?: string;
|
|
43
|
+
handler?: (payload: EventPayload) => Promise<any>;
|
|
44
|
+
retryConfig?: {
|
|
45
|
+
maxRetries: number;
|
|
46
|
+
retryDelay: number;
|
|
47
|
+
backoffMultiplier: number;
|
|
48
|
+
};
|
|
49
|
+
timeout?: number;
|
|
50
|
+
headers?: Record<string, string>;
|
|
51
|
+
authentication?: {
|
|
52
|
+
type: 'bearer' | 'api-key' | 'basic';
|
|
53
|
+
credentials: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export interface EventBusConfig {
|
|
57
|
+
defaultTransport: EventTransportType;
|
|
58
|
+
defaultTimeout: number;
|
|
59
|
+
defaultRetries: number;
|
|
60
|
+
enableDeadLetterQueue: boolean;
|
|
61
|
+
deadLetterQueueUrl?: string;
|
|
62
|
+
awsRegion?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface EventResult {
|
|
65
|
+
success: boolean;
|
|
66
|
+
eventId: string;
|
|
67
|
+
statusCode?: number;
|
|
68
|
+
response?: any;
|
|
69
|
+
error?: string;
|
|
70
|
+
retryCount: number;
|
|
71
|
+
executionTime: number;
|
|
72
|
+
transport: EventTransportType;
|
|
73
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Bus Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the EventBus system.
|
|
5
|
+
*/
|
|
6
|
+
export var EventTransportType;
|
|
7
|
+
(function (EventTransportType) {
|
|
8
|
+
EventTransportType["HTTP"] = "http";
|
|
9
|
+
EventTransportType["SNS"] = "sns";
|
|
10
|
+
EventTransportType["EVENTBRIDGE"] = "eventbridge";
|
|
11
|
+
EventTransportType["SQS"] = "sqs";
|
|
12
|
+
EventTransportType["INTERNAL"] = "internal";
|
|
13
|
+
})(EventTransportType || (EventTransportType = {}));
|
|
14
|
+
export var EventStatus;
|
|
15
|
+
(function (EventStatus) {
|
|
16
|
+
EventStatus["PENDING"] = "pending";
|
|
17
|
+
EventStatus["PROCESSING"] = "processing";
|
|
18
|
+
EventStatus["SUCCESS"] = "success";
|
|
19
|
+
EventStatus["FAILED"] = "failed";
|
|
20
|
+
EventStatus["RETRY"] = "retry";
|
|
21
|
+
EventStatus["DEAD_LETTER"] = "dead_letter";
|
|
22
|
+
})(EventStatus || (EventStatus = {}));
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export * from './notification-enums';
|
|
2
|
-
export * from './notification-event';
|
|
3
|
-
export * from './event-publisher';
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './event-
|
|
6
|
-
export * from './workflow-event.service';
|
|
1
|
+
export * from './notifications/notification-enums';
|
|
2
|
+
export * from './notifications/notification-event';
|
|
3
|
+
export * from './notifications/event-publisher';
|
|
4
|
+
export * from './bus/event-bus.types';
|
|
5
|
+
export * from './bus/event-bus.service';
|
package/dist/src/events/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// Notification
|
|
2
|
-
export * from './notification-enums';
|
|
3
|
-
export * from './notification-event';
|
|
4
|
-
export * from './event-publisher';
|
|
5
|
-
// Event-
|
|
6
|
-
export * from './
|
|
7
|
-
export * from './event-
|
|
8
|
-
export * from './workflow-event.service';
|
|
1
|
+
// Notification types and publisher (SNS-based)
|
|
2
|
+
export * from './notifications/notification-enums';
|
|
3
|
+
export * from './notifications/notification-event';
|
|
4
|
+
export * from './notifications/event-publisher';
|
|
5
|
+
// Event bus (multi-transport delivery)
|
|
6
|
+
export * from './bus/event-bus.types';
|
|
7
|
+
export * from './bus/event-bus.service';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NotificationMeta } from './notification-event';
|
|
2
|
+
import { NotificationType, NotificationChannel } from './notification-enums';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for the event publisher
|
|
5
|
+
*/
|
|
6
|
+
interface EventPublisherConfig {
|
|
7
|
+
region?: string;
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
topicArn?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Event Publisher for sending notification events to SNS
|
|
13
|
+
* Used by all services to publish events to the notifications topic
|
|
14
|
+
*/
|
|
15
|
+
export declare class EventPublisher {
|
|
16
|
+
private readonly snsClient;
|
|
17
|
+
private readonly topicArn;
|
|
18
|
+
private readonly serviceName;
|
|
19
|
+
constructor(serviceName: string, config?: EventPublisherConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Publish a notification event to SNS
|
|
22
|
+
*/
|
|
23
|
+
publish<T>(type: NotificationType, channel: NotificationChannel, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Convenience method for publishing email notifications
|
|
26
|
+
*/
|
|
27
|
+
publishEmail<T>(type: NotificationType, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Convenience method for publishing SMS notifications
|
|
30
|
+
*/
|
|
31
|
+
publishSMS<T>(type: NotificationType, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Convenience method for publishing push notifications
|
|
34
|
+
*/
|
|
35
|
+
publishPush<T>(type: NotificationType, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get or create an EventPublisher for a service
|
|
39
|
+
*/
|
|
40
|
+
export declare function getEventPublisher(serviceName: string, config?: EventPublisherConfig): EventPublisher;
|
|
41
|
+
export {};
|