@valentine-efagene/qshelter-common 2.0.69 → 2.0.72
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 +6 -0
- package/dist/generated/client/client.d.ts +6 -0
- package/dist/generated/client/commonInputTypes.d.ts +30 -0
- package/dist/generated/client/enums.d.ts +14 -6
- package/dist/generated/client/enums.js +13 -6
- package/dist/generated/client/internal/class.d.ts +11 -0
- package/dist/generated/client/internal/class.js +2 -2
- package/dist/generated/client/internal/prismaNamespace.d.ts +90 -1
- package/dist/generated/client/internal/prismaNamespace.js +16 -0
- package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +18 -0
- package/dist/generated/client/internal/prismaNamespaceBrowser.js +16 -0
- package/dist/generated/client/models/EventHandler.d.ts +154 -4
- package/dist/generated/client/models/PaymentMethodPhaseStep.d.ts +133 -0
- package/dist/generated/client/models/StepEventAttachment.d.ts +1331 -0
- package/dist/generated/client/models/StepEventAttachment.js +1 -0
- package/dist/generated/client/models.d.ts +1 -0
- package/dist/src/events/workflow-event.service.d.ts +59 -34
- package/dist/src/events/workflow-event.service.js +190 -168
- package/dist/src/events/workflow-types.d.ts +136 -87
- package/dist/src/events/workflow-types.js +9 -1
- package/package.json +1 -1
- package/prisma/migrations/20260106003757_business_friendly_handler_types/migration.sql +28 -0
- package/prisma/schema.prisma +51 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -29,6 +29,7 @@ export type * from './models/PropertyPaymentMethod.js';
|
|
|
29
29
|
export type * from './models/PropertyPaymentMethodLink.js';
|
|
30
30
|
export type * from './models/PropertyPaymentMethodPhase.js';
|
|
31
31
|
export type * from './models/PaymentMethodPhaseStep.js';
|
|
32
|
+
export type * from './models/StepEventAttachment.js';
|
|
32
33
|
export type * from './models/PaymentMethodPhaseDocument.js';
|
|
33
34
|
export type * from './models/Contract.js';
|
|
34
35
|
export type * from './models/ContractPhase.js';
|
|
@@ -35,7 +35,14 @@ import { PrismaClient } from '../../generated/client/client';
|
|
|
35
35
|
import type { EmitEventInput, WorkflowEventData, ProcessEventResult } from './workflow-types';
|
|
36
36
|
import { EventPublisher } from './event-publisher';
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Automation registry interface for RUN_AUTOMATION handlers
|
|
39
|
+
*/
|
|
40
|
+
export interface AutomationRegistry {
|
|
41
|
+
get(automationName: string): ((inputs: Record<string, unknown>, tenantId: string) => Promise<unknown>) | undefined;
|
|
42
|
+
register(automationName: string, handler: (inputs: Record<string, unknown>, tenantId: string) => Promise<unknown>): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Service registry interface for internal handlers (legacy support)
|
|
39
46
|
*/
|
|
40
47
|
export interface ServiceRegistry {
|
|
41
48
|
get(serviceName: string): any | undefined;
|
|
@@ -43,16 +50,16 @@ export interface ServiceRegistry {
|
|
|
43
50
|
}
|
|
44
51
|
export declare class WorkflowEventService {
|
|
45
52
|
private prisma;
|
|
46
|
-
private
|
|
53
|
+
private automationRegistry;
|
|
47
54
|
private eventPublisher;
|
|
48
|
-
constructor(prisma: PrismaClient,
|
|
55
|
+
constructor(prisma: PrismaClient, automationRegistry?: AutomationRegistry, eventPublisher?: EventPublisher);
|
|
49
56
|
/**
|
|
50
|
-
* Register
|
|
57
|
+
* Register an automation for RUN_AUTOMATION handlers
|
|
51
58
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
59
|
+
* Automations are business logic functions that can be triggered by events.
|
|
60
|
+
* Example: "calculateLateFee", "sendWelcomePackage", "archiveContract"
|
|
54
61
|
*/
|
|
55
|
-
|
|
62
|
+
registerAutomation(name: string, handler: (inputs: Record<string, unknown>, tenantId: string) => Promise<unknown>): void;
|
|
56
63
|
/**
|
|
57
64
|
* Emit an event
|
|
58
65
|
*
|
|
@@ -116,10 +123,10 @@ export declare class WorkflowEventService {
|
|
|
116
123
|
id: string;
|
|
117
124
|
createdAt: Date;
|
|
118
125
|
status: import("./workflow-types").ExecutionStatus;
|
|
126
|
+
handlerId: string;
|
|
119
127
|
completedAt: Date | null;
|
|
120
128
|
error: string | null;
|
|
121
129
|
eventId: string;
|
|
122
|
-
handlerId: string;
|
|
123
130
|
attempt: number;
|
|
124
131
|
input: import("@prisma/client/runtime/client").JsonValue | null;
|
|
125
132
|
output: import("@prisma/client/runtime/client").JsonValue | null;
|
|
@@ -144,44 +151,63 @@ export declare class WorkflowEventService {
|
|
|
144
151
|
}) | null>;
|
|
145
152
|
/**
|
|
146
153
|
* Execute a handler based on its type
|
|
154
|
+
*
|
|
155
|
+
* Handler types are business-friendly names that abstract the underlying implementation:
|
|
156
|
+
* - SEND_EMAIL: Send email via notification service (SNS → SQS → SES)
|
|
157
|
+
* - SEND_SMS: Send SMS via notification service
|
|
158
|
+
* - SEND_PUSH: Send push notification via notification service
|
|
159
|
+
* - CALL_WEBHOOK: Make HTTP request to external URL
|
|
160
|
+
* - ADVANCE_WORKFLOW: Move workflow steps forward/backward
|
|
161
|
+
* - RUN_AUTOMATION: Execute registered business logic automation
|
|
147
162
|
*/
|
|
148
163
|
private executeHandler;
|
|
149
164
|
/**
|
|
150
|
-
* Execute
|
|
165
|
+
* Execute SEND_EMAIL handler
|
|
166
|
+
*
|
|
167
|
+
* Sends an email via the notification service using SNS → SQS → SES.
|
|
168
|
+
* Business users configure: template, recipient, and template data.
|
|
151
169
|
*/
|
|
152
|
-
private
|
|
170
|
+
private executeSendEmailHandler;
|
|
153
171
|
/**
|
|
154
|
-
* Execute
|
|
172
|
+
* Execute SEND_SMS handler
|
|
173
|
+
*
|
|
174
|
+
* Sends an SMS via the notification service.
|
|
175
|
+
* Business users configure: template, recipient phone, and template data.
|
|
155
176
|
*/
|
|
156
|
-
private
|
|
177
|
+
private executeSendSmsHandler;
|
|
157
178
|
/**
|
|
158
|
-
* Execute
|
|
179
|
+
* Execute SEND_PUSH handler
|
|
159
180
|
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
181
|
+
* Sends a push notification via the notification service.
|
|
182
|
+
* Business users configure: template, recipient user, and template data.
|
|
162
183
|
*/
|
|
163
|
-
private
|
|
184
|
+
private executeSendPushHandler;
|
|
164
185
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* This would integrate with a notification service.
|
|
168
|
-
* Returns what would be sent for logging purposes.
|
|
186
|
+
* Build notification payload from config and event payload
|
|
169
187
|
*/
|
|
170
|
-
private
|
|
188
|
+
private buildNotificationPayload;
|
|
171
189
|
/**
|
|
172
|
-
* Execute
|
|
190
|
+
* Execute CALL_WEBHOOK handler
|
|
173
191
|
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
192
|
+
* Makes an HTTP request to an external URL.
|
|
193
|
+
* Business users configure: URL, method, headers, and body mapping.
|
|
194
|
+
*/
|
|
195
|
+
private executeCallWebhookHandler;
|
|
196
|
+
/**
|
|
197
|
+
* Execute ADVANCE_WORKFLOW handler
|
|
176
198
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
199
|
+
* Advances or modifies workflow state.
|
|
200
|
+
* Business users configure: action (approve/reject/skip), step path, and data.
|
|
179
201
|
*/
|
|
180
|
-
private
|
|
202
|
+
private executeAdvanceWorkflowHandler;
|
|
181
203
|
/**
|
|
182
|
-
*
|
|
204
|
+
* Execute RUN_AUTOMATION handler
|
|
205
|
+
*
|
|
206
|
+
* Runs a registered business logic automation.
|
|
207
|
+
* Business users select from pre-defined automations like
|
|
208
|
+
* "Calculate Mortgage Payment", "Generate Contract", etc.
|
|
183
209
|
*/
|
|
184
|
-
private
|
|
210
|
+
private executeRunAutomationHandler;
|
|
185
211
|
/**
|
|
186
212
|
* Evaluate a filter condition against the payload
|
|
187
213
|
*/
|
|
@@ -194,12 +220,11 @@ export declare class WorkflowEventService {
|
|
|
194
220
|
* Resolve a dot-notation path in an object
|
|
195
221
|
*/
|
|
196
222
|
private resolvePath;
|
|
197
|
-
/**
|
|
198
|
-
* Resolve recipients from config, potentially using payload variables
|
|
199
|
-
*/
|
|
200
|
-
private resolveRecipients;
|
|
201
223
|
}
|
|
202
224
|
/**
|
|
203
225
|
* Create a workflow event service instance
|
|
226
|
+
*
|
|
227
|
+
* @param prisma - Prisma client for database access
|
|
228
|
+
* @param automationRegistry - Optional registry of business automations
|
|
204
229
|
*/
|
|
205
|
-
export declare function createWorkflowEventService(prisma: PrismaClient,
|
|
230
|
+
export declare function createWorkflowEventService(prisma: PrismaClient, automationRegistry?: AutomationRegistry): WorkflowEventService;
|
|
@@ -32,7 +32,19 @@
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
import { EventPublisher } from './event-publisher';
|
|
35
|
-
import {
|
|
35
|
+
import { NotificationChannel } from './notification-enums';
|
|
36
|
+
/**
|
|
37
|
+
* Simple in-memory automation registry
|
|
38
|
+
*/
|
|
39
|
+
class InMemoryAutomationRegistry {
|
|
40
|
+
automations = new Map();
|
|
41
|
+
get(automationName) {
|
|
42
|
+
return this.automations.get(automationName);
|
|
43
|
+
}
|
|
44
|
+
register(automationName, handler) {
|
|
45
|
+
this.automations.set(automationName, handler);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
36
48
|
/**
|
|
37
49
|
* Simple in-memory service registry
|
|
38
50
|
*/
|
|
@@ -47,21 +59,21 @@ class InMemoryServiceRegistry {
|
|
|
47
59
|
}
|
|
48
60
|
export class WorkflowEventService {
|
|
49
61
|
prisma;
|
|
50
|
-
|
|
62
|
+
automationRegistry;
|
|
51
63
|
eventPublisher;
|
|
52
|
-
constructor(prisma,
|
|
64
|
+
constructor(prisma, automationRegistry, eventPublisher) {
|
|
53
65
|
this.prisma = prisma;
|
|
54
|
-
this.
|
|
66
|
+
this.automationRegistry = automationRegistry || new InMemoryAutomationRegistry();
|
|
55
67
|
this.eventPublisher = eventPublisher || new EventPublisher('workflow-event-service');
|
|
56
68
|
}
|
|
57
69
|
/**
|
|
58
|
-
* Register
|
|
70
|
+
* Register an automation for RUN_AUTOMATION handlers
|
|
59
71
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
72
|
+
* Automations are business logic functions that can be triggered by events.
|
|
73
|
+
* Example: "calculateLateFee", "sendWelcomePackage", "archiveContract"
|
|
62
74
|
*/
|
|
63
|
-
|
|
64
|
-
this.
|
|
75
|
+
registerAutomation(name, handler) {
|
|
76
|
+
this.automationRegistry.register(name, handler);
|
|
65
77
|
}
|
|
66
78
|
/**
|
|
67
79
|
* Emit an event
|
|
@@ -350,53 +362,155 @@ export class WorkflowEventService {
|
|
|
350
362
|
// ==========================================
|
|
351
363
|
/**
|
|
352
364
|
* Execute a handler based on its type
|
|
365
|
+
*
|
|
366
|
+
* Handler types are business-friendly names that abstract the underlying implementation:
|
|
367
|
+
* - SEND_EMAIL: Send email via notification service (SNS → SQS → SES)
|
|
368
|
+
* - SEND_SMS: Send SMS via notification service
|
|
369
|
+
* - SEND_PUSH: Send push notification via notification service
|
|
370
|
+
* - CALL_WEBHOOK: Make HTTP request to external URL
|
|
371
|
+
* - ADVANCE_WORKFLOW: Move workflow steps forward/backward
|
|
372
|
+
* - RUN_AUTOMATION: Execute registered business logic automation
|
|
353
373
|
*/
|
|
354
374
|
async executeHandler(handlerType, config, payload, tenantId) {
|
|
355
375
|
switch (handlerType) {
|
|
356
|
-
case '
|
|
357
|
-
return this.
|
|
358
|
-
case '
|
|
359
|
-
return this.
|
|
360
|
-
case '
|
|
361
|
-
return this.
|
|
362
|
-
case '
|
|
363
|
-
return this.
|
|
364
|
-
case '
|
|
365
|
-
return this.
|
|
366
|
-
case '
|
|
367
|
-
|
|
368
|
-
throw new Error('Script handlers not yet implemented');
|
|
376
|
+
case 'SEND_EMAIL':
|
|
377
|
+
return this.executeSendEmailHandler(config, payload, tenantId);
|
|
378
|
+
case 'SEND_SMS':
|
|
379
|
+
return this.executeSendSmsHandler(config, payload, tenantId);
|
|
380
|
+
case 'SEND_PUSH':
|
|
381
|
+
return this.executeSendPushHandler(config, payload, tenantId);
|
|
382
|
+
case 'CALL_WEBHOOK':
|
|
383
|
+
return this.executeCallWebhookHandler(config, payload);
|
|
384
|
+
case 'ADVANCE_WORKFLOW':
|
|
385
|
+
return this.executeAdvanceWorkflowHandler(config, payload, tenantId);
|
|
386
|
+
case 'RUN_AUTOMATION':
|
|
387
|
+
return this.executeRunAutomationHandler(config, payload, tenantId);
|
|
369
388
|
default:
|
|
370
389
|
throw new Error(`Unknown handler type: ${handlerType}`);
|
|
371
390
|
}
|
|
372
391
|
}
|
|
373
392
|
/**
|
|
374
|
-
* Execute
|
|
393
|
+
* Execute SEND_EMAIL handler
|
|
394
|
+
*
|
|
395
|
+
* Sends an email via the notification service using SNS → SQS → SES.
|
|
396
|
+
* Business users configure: template, recipient, and template data.
|
|
375
397
|
*/
|
|
376
|
-
async
|
|
377
|
-
//
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
398
|
+
async executeSendEmailHandler(config, payload, tenantId) {
|
|
399
|
+
// Build the notification payload
|
|
400
|
+
const notificationPayload = this.buildNotificationPayload(config, payload);
|
|
401
|
+
// Resolve recipient email from the payload
|
|
402
|
+
if (config.recipientPath) {
|
|
403
|
+
const email = this.resolvePath(payload, config.recipientPath.replace(/^\$\./, ''));
|
|
404
|
+
if (email && typeof email === 'string') {
|
|
405
|
+
notificationPayload.to_email = email;
|
|
406
|
+
}
|
|
381
407
|
}
|
|
382
|
-
//
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
408
|
+
// Publish to SNS via EventPublisher
|
|
409
|
+
const messageId = await this.eventPublisher.publish(config.notificationType, NotificationChannel.EMAIL, notificationPayload, {
|
|
410
|
+
tenantId,
|
|
411
|
+
correlationId: payload.correlationId || undefined,
|
|
412
|
+
userId: payload.userId || payload.actorId || undefined,
|
|
413
|
+
});
|
|
414
|
+
return {
|
|
415
|
+
success: true,
|
|
416
|
+
messageId,
|
|
417
|
+
notificationType: config.notificationType,
|
|
418
|
+
channel: 'email',
|
|
419
|
+
payload: notificationPayload,
|
|
420
|
+
tenantId,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Execute SEND_SMS handler
|
|
425
|
+
*
|
|
426
|
+
* Sends an SMS via the notification service.
|
|
427
|
+
* Business users configure: template, recipient phone, and template data.
|
|
428
|
+
*/
|
|
429
|
+
async executeSendSmsHandler(config, payload, tenantId) {
|
|
430
|
+
// Build the notification payload
|
|
431
|
+
const notificationPayload = this.buildNotificationPayload(config, payload);
|
|
432
|
+
// Resolve recipient phone from the payload
|
|
433
|
+
if (config.recipientPath) {
|
|
434
|
+
const phone = this.resolvePath(payload, config.recipientPath.replace(/^\$\./, ''));
|
|
435
|
+
if (phone && typeof phone === 'string') {
|
|
436
|
+
notificationPayload.to_phone = phone;
|
|
437
|
+
}
|
|
386
438
|
}
|
|
387
|
-
//
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
: payload
|
|
391
|
-
|
|
392
|
-
|
|
439
|
+
// Publish to SNS via EventPublisher
|
|
440
|
+
const messageId = await this.eventPublisher.publish(config.notificationType, NotificationChannel.SMS, notificationPayload, {
|
|
441
|
+
tenantId,
|
|
442
|
+
correlationId: payload.correlationId || undefined,
|
|
443
|
+
userId: payload.userId || payload.actorId || undefined,
|
|
444
|
+
});
|
|
445
|
+
return {
|
|
446
|
+
success: true,
|
|
447
|
+
messageId,
|
|
448
|
+
notificationType: config.notificationType,
|
|
449
|
+
channel: 'sms',
|
|
450
|
+
payload: notificationPayload,
|
|
451
|
+
tenantId,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Execute SEND_PUSH handler
|
|
456
|
+
*
|
|
457
|
+
* Sends a push notification via the notification service.
|
|
458
|
+
* Business users configure: template, recipient user, and template data.
|
|
459
|
+
*/
|
|
460
|
+
async executeSendPushHandler(config, payload, tenantId) {
|
|
461
|
+
// Build the notification payload
|
|
462
|
+
const notificationPayload = this.buildNotificationPayload(config, payload);
|
|
463
|
+
// Resolve recipient user ID from the payload
|
|
464
|
+
if (config.recipientPath) {
|
|
465
|
+
const userId = this.resolvePath(payload, config.recipientPath.replace(/^\$\./, ''));
|
|
466
|
+
if (userId && typeof userId === 'string') {
|
|
467
|
+
notificationPayload.to_user_id = userId;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
// Publish to SNS via EventPublisher
|
|
471
|
+
const messageId = await this.eventPublisher.publish(config.notificationType, NotificationChannel.PUSH, notificationPayload, {
|
|
472
|
+
tenantId,
|
|
473
|
+
correlationId: payload.correlationId || undefined,
|
|
474
|
+
userId: payload.userId || payload.actorId || undefined,
|
|
475
|
+
});
|
|
476
|
+
return {
|
|
477
|
+
success: true,
|
|
478
|
+
messageId,
|
|
479
|
+
notificationType: config.notificationType,
|
|
480
|
+
channel: 'push',
|
|
481
|
+
payload: notificationPayload,
|
|
482
|
+
tenantId,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Build notification payload from config and event payload
|
|
487
|
+
*/
|
|
488
|
+
buildNotificationPayload(config, payload) {
|
|
489
|
+
const result = {};
|
|
490
|
+
// Apply static template data first
|
|
491
|
+
if (config.staticData) {
|
|
492
|
+
Object.assign(result, config.staticData);
|
|
493
|
+
}
|
|
494
|
+
// Apply template data mapping (map from event payload to notification payload)
|
|
495
|
+
if (config.templateData) {
|
|
496
|
+
for (const [targetField, sourcePath] of Object.entries(config.templateData)) {
|
|
497
|
+
const value = this.resolvePath(payload, sourcePath.replace(/^\$\./, ''));
|
|
498
|
+
if (value !== undefined) {
|
|
499
|
+
result[targetField] = value;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return result;
|
|
393
504
|
}
|
|
394
505
|
/**
|
|
395
|
-
* Execute
|
|
506
|
+
* Execute CALL_WEBHOOK handler
|
|
507
|
+
*
|
|
508
|
+
* Makes an HTTP request to an external URL.
|
|
509
|
+
* Business users configure: URL, method, headers, and body mapping.
|
|
396
510
|
*/
|
|
397
|
-
async
|
|
398
|
-
const transformedPayload = config.
|
|
399
|
-
? this.transformPayload(payload, config.
|
|
511
|
+
async executeCallWebhookHandler(config, payload) {
|
|
512
|
+
const transformedPayload = config.bodyMapping
|
|
513
|
+
? this.transformPayload(payload, config.bodyMapping)
|
|
400
514
|
: payload;
|
|
401
515
|
const controller = new AbortController();
|
|
402
516
|
const timeoutId = setTimeout(() => controller.abort(), config.timeoutMs || 30000);
|
|
@@ -428,106 +542,50 @@ export class WorkflowEventService {
|
|
|
428
542
|
}
|
|
429
543
|
}
|
|
430
544
|
/**
|
|
431
|
-
* Execute
|
|
545
|
+
* Execute ADVANCE_WORKFLOW handler
|
|
432
546
|
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
547
|
+
* Advances or modifies workflow state.
|
|
548
|
+
* Business users configure: action (approve/reject/skip), step path, and data.
|
|
435
549
|
*/
|
|
436
|
-
async
|
|
550
|
+
async executeAdvanceWorkflowHandler(config, payload, tenantId) {
|
|
551
|
+
// Resolve step ID from payload if path is provided
|
|
552
|
+
let stepId = config.stepId;
|
|
553
|
+
if (config.stepIdPath) {
|
|
554
|
+
const resolved = this.resolvePath(payload, config.stepIdPath.replace(/^\$\./, ''));
|
|
555
|
+
if (resolved && typeof resolved === 'string') {
|
|
556
|
+
stepId = resolved;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
437
559
|
// Return the workflow action data
|
|
438
|
-
// The workflow service should listen for
|
|
560
|
+
// The workflow service should listen for ADVANCE_WORKFLOW handler results
|
|
439
561
|
return {
|
|
440
562
|
action: config.action,
|
|
441
563
|
workflowId: config.workflowId,
|
|
442
564
|
phaseId: config.phaseId,
|
|
443
|
-
stepId
|
|
565
|
+
stepId,
|
|
444
566
|
data: { ...config.data, ...payload },
|
|
445
567
|
tenantId,
|
|
446
568
|
};
|
|
447
569
|
}
|
|
448
570
|
/**
|
|
449
|
-
* Execute
|
|
571
|
+
* Execute RUN_AUTOMATION handler
|
|
450
572
|
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
573
|
+
* Runs a registered business logic automation.
|
|
574
|
+
* Business users select from pre-defined automations like
|
|
575
|
+
* "Calculate Mortgage Payment", "Generate Contract", etc.
|
|
453
576
|
*/
|
|
454
|
-
async
|
|
455
|
-
//
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
channels: config.channels,
|
|
460
|
-
recipients: this.resolveRecipients(config.recipients, payload),
|
|
461
|
-
priority: config.priority || 'normal',
|
|
462
|
-
data: payload,
|
|
463
|
-
tenantId,
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Execute an SNS handler
|
|
468
|
-
*
|
|
469
|
-
* Publishes to SNS topic using the EventPublisher, which triggers
|
|
470
|
-
* the notification-service via SNS->SQS subscription.
|
|
471
|
-
*
|
|
472
|
-
* The config specifies the notification type, channel, and how to
|
|
473
|
-
* map the event payload to the notification payload.
|
|
474
|
-
*/
|
|
475
|
-
async executeSnsHandler(config, payload, tenantId) {
|
|
476
|
-
// Build the notification payload from config
|
|
477
|
-
const notificationPayload = this.buildSnsPayload(config, payload);
|
|
478
|
-
// Map channel string to NotificationChannel enum
|
|
479
|
-
const channelMap = {
|
|
480
|
-
email: NotificationChannel.EMAIL,
|
|
481
|
-
sms: NotificationChannel.SMS,
|
|
482
|
-
push: NotificationChannel.PUSH,
|
|
483
|
-
};
|
|
484
|
-
const channel = channelMap[config.channel] || NotificationChannel.EMAIL;
|
|
485
|
-
// Validate notification type is a valid enum value
|
|
486
|
-
const notificationType = config.notificationType;
|
|
487
|
-
if (!Object.values(NotificationType).includes(notificationType)) {
|
|
488
|
-
throw new Error(`Invalid notification type: ${config.notificationType}`);
|
|
577
|
+
async executeRunAutomationHandler(config, payload, tenantId) {
|
|
578
|
+
// Get the automation function from the registry
|
|
579
|
+
const automationFn = this.automationRegistry.get(config.automation);
|
|
580
|
+
if (!automationFn) {
|
|
581
|
+
throw new Error(`Automation '${config.automation}' not found in registry`);
|
|
489
582
|
}
|
|
490
|
-
//
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return {
|
|
497
|
-
success: true,
|
|
498
|
-
messageId,
|
|
499
|
-
notificationType: config.notificationType,
|
|
500
|
-
channel: config.channel,
|
|
501
|
-
payload: notificationPayload,
|
|
502
|
-
tenantId,
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Build the notification payload for SNS from config and event payload
|
|
507
|
-
*/
|
|
508
|
-
buildSnsPayload(config, payload) {
|
|
509
|
-
const result = {};
|
|
510
|
-
// Apply static payload first
|
|
511
|
-
if (config.staticPayload) {
|
|
512
|
-
Object.assign(result, config.staticPayload);
|
|
513
|
-
}
|
|
514
|
-
// Apply payload mapping (map from event payload to notification payload)
|
|
515
|
-
if (config.payloadMapping) {
|
|
516
|
-
for (const [targetField, sourcePath] of Object.entries(config.payloadMapping)) {
|
|
517
|
-
const value = this.resolvePath(payload, sourcePath.replace(/^\$\./, ''));
|
|
518
|
-
if (value !== undefined) {
|
|
519
|
-
result[targetField] = value;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
// If recipientPath is specified, extract the recipient email
|
|
524
|
-
if (config.recipientPath) {
|
|
525
|
-
const email = this.resolvePath(payload, config.recipientPath.replace(/^\$\./, ''));
|
|
526
|
-
if (email && typeof email === 'string') {
|
|
527
|
-
result.to_email = email;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
return result;
|
|
583
|
+
// Transform payload if mapping is defined
|
|
584
|
+
const transformedPayload = config.inputMapping
|
|
585
|
+
? this.transformPayload(payload, config.inputMapping)
|
|
586
|
+
: payload;
|
|
587
|
+
// Call the automation function with inputs and tenantId
|
|
588
|
+
return automationFn(transformedPayload, tenantId);
|
|
531
589
|
}
|
|
532
590
|
// ==========================================
|
|
533
591
|
// UTILITY METHODS
|
|
@@ -612,49 +670,13 @@ export class WorkflowEventService {
|
|
|
612
670
|
}
|
|
613
671
|
return current;
|
|
614
672
|
}
|
|
615
|
-
/**
|
|
616
|
-
* Resolve recipients from config, potentially using payload variables
|
|
617
|
-
*/
|
|
618
|
-
resolveRecipients(recipients, payload) {
|
|
619
|
-
if (!recipients)
|
|
620
|
-
return undefined;
|
|
621
|
-
const resolved = {};
|
|
622
|
-
// Resolve email recipients
|
|
623
|
-
if (recipients.email) {
|
|
624
|
-
resolved.email = recipients.email.map((addr) => {
|
|
625
|
-
if (addr.startsWith('$.')) {
|
|
626
|
-
const value = this.resolvePath(payload, addr);
|
|
627
|
-
return typeof value === 'string' ? value : addr;
|
|
628
|
-
}
|
|
629
|
-
return addr;
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
// Resolve phone recipients
|
|
633
|
-
if (recipients.phone) {
|
|
634
|
-
resolved.phone = recipients.phone.map((phone) => {
|
|
635
|
-
if (phone.startsWith('$.')) {
|
|
636
|
-
const value = this.resolvePath(payload, phone);
|
|
637
|
-
return typeof value === 'string' ? value : phone;
|
|
638
|
-
}
|
|
639
|
-
return phone;
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
// Resolve userId recipients
|
|
643
|
-
if (recipients.userId) {
|
|
644
|
-
resolved.userId = recipients.userId.map((id) => {
|
|
645
|
-
if (id.startsWith('$.')) {
|
|
646
|
-
const value = this.resolvePath(payload, id);
|
|
647
|
-
return typeof value === 'string' ? value : id;
|
|
648
|
-
}
|
|
649
|
-
return id;
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
return resolved;
|
|
653
|
-
}
|
|
654
673
|
}
|
|
655
674
|
/**
|
|
656
675
|
* Create a workflow event service instance
|
|
676
|
+
*
|
|
677
|
+
* @param prisma - Prisma client for database access
|
|
678
|
+
* @param automationRegistry - Optional registry of business automations
|
|
657
679
|
*/
|
|
658
|
-
export function createWorkflowEventService(prisma,
|
|
659
|
-
return new WorkflowEventService(prisma,
|
|
680
|
+
export function createWorkflowEventService(prisma, automationRegistry) {
|
|
681
|
+
return new WorkflowEventService(prisma, automationRegistry);
|
|
660
682
|
}
|