rez_core 2.2.253 → 2.2.255

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.
Files changed (36) hide show
  1. package/dist/module/communication/communication.module.js +2 -0
  2. package/dist/module/communication/communication.module.js.map +1 -1
  3. package/dist/module/communication/factories/telephone.factory.d.ts +3 -1
  4. package/dist/module/communication/factories/telephone.factory.js +9 -3
  5. package/dist/module/communication/factories/telephone.factory.js.map +1 -1
  6. package/dist/module/communication/strategies/telephone/tubelight-voice.strategy.d.ts +15 -0
  7. package/dist/module/communication/strategies/telephone/tubelight-voice.strategy.js +104 -0
  8. package/dist/module/communication/strategies/telephone/tubelight-voice.strategy.js.map +1 -0
  9. package/dist/module/meta/controller/entity.controller.js +11 -6
  10. package/dist/module/meta/controller/entity.controller.js.map +1 -1
  11. package/dist/module/user/service/user.service.d.ts +2 -1
  12. package/dist/module/user/service/user.service.js +2 -0
  13. package/dist/module/user/service/user.service.js.map +1 -1
  14. package/dist/module/workflow/entity/action-data.entity.d.ts +1 -0
  15. package/dist/module/workflow/entity/action-data.entity.js +4 -0
  16. package/dist/module/workflow/entity/action-data.entity.js.map +1 -1
  17. package/dist/module/workflow/entity/action.entity.d.ts +1 -0
  18. package/dist/module/workflow/entity/action.entity.js +4 -0
  19. package/dist/module/workflow/entity/action.entity.js.map +1 -1
  20. package/dist/module/workflow-automation/service/workflow-automation-engine.service.d.ts +1 -1
  21. package/dist/module/workflow-automation/service/workflow-automation-engine.service.js +13 -5
  22. package/dist/module/workflow-automation/service/workflow-automation-engine.service.js.map +1 -1
  23. package/dist/module/workflow-automation/service/workflow-automation.service.d.ts +34 -2
  24. package/dist/module/workflow-automation/service/workflow-automation.service.js +136 -15
  25. package/dist/module/workflow-automation/service/workflow-automation.service.js.map +1 -1
  26. package/dist/tsconfig.build.tsbuildinfo +1 -1
  27. package/package.json +1 -1
  28. package/src/module/communication/communication.module.ts +2 -0
  29. package/src/module/communication/factories/telephone.factory.ts +6 -1
  30. package/src/module/communication/strategies/telephone/tubelight-voice.strategy.ts +122 -0
  31. package/src/module/meta/controller/entity.controller.ts +51 -42
  32. package/src/module/user/service/user.service.ts +2 -1
  33. package/src/module/workflow/entity/action-data.entity.ts +3 -0
  34. package/src/module/workflow/entity/action.entity.ts +3 -0
  35. package/src/module/workflow-automation/service/workflow-automation-engine.service.ts +53 -26
  36. package/src/module/workflow-automation/service/workflow-automation.service.ts +223 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "2.2.253",
3
+ "version": "2.2.255",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -22,6 +22,7 @@ import { KnowlarityStrategy as KnowlaritySMSStrategy } from './strategies/sms/kn
22
22
  import { WhatsAppCloudStrategy } from './strategies/whatsapp/whatsapp-cloud.strategy';
23
23
  import { KnowlarityVoiceStrategy } from './strategies/telephone/knowlarity-voice.strategy';
24
24
  import { OzonetelVoiceStrategy } from './strategies/telephone/ozonetel-voice.strategy';
25
+ import { TubelightVoiceStrategy } from './strategies/telephone/tubelight-voice.strategy';
25
26
 
26
27
  // New unified strategies
27
28
  import { OutlookStrategy } from './strategies/email/outlook.strategy';
@@ -75,6 +76,7 @@ import { WebhookController } from './controller/webhook.controller';
75
76
  WhatsAppCloudStrategy,
76
77
  KnowlarityVoiceStrategy,
77
78
  OzonetelVoiceStrategy,
79
+ TubelightVoiceStrategy,
78
80
 
79
81
  // New unified strategies
80
82
  OutlookStrategy,
@@ -3,12 +3,14 @@ import { BaseFactory } from './base.factory';
3
3
  import { CommunicationStrategy } from '../strategies/communication.strategy';
4
4
  import { KnowlarityStrategy as TelephoneKnowlarityStrategy } from '../strategies/telephone/knowlarity-multi.strategy';
5
5
  import { OzonetelVoiceStrategy } from '../strategies/telephone/ozonetel-voice.strategy';
6
+ import { TubelightVoiceStrategy } from '../strategies/telephone/tubelight-voice.strategy';
6
7
 
7
8
  @Injectable()
8
9
  export class TelephoneFactory implements BaseFactory {
9
10
  constructor(
10
11
  private knowlarityStrategy: TelephoneKnowlarityStrategy,
11
12
  private ozonetelVoiceStrategy: OzonetelVoiceStrategy,
13
+ private tubelightVoiceStrategy: TubelightVoiceStrategy,
12
14
  ) {}
13
15
 
14
16
  createProvider(service: string, provider: string): CommunicationStrategy {
@@ -19,6 +21,8 @@ export class TelephoneFactory implements BaseFactory {
19
21
  return this.knowlarityStrategy;
20
22
  case 'third_party_ozonetel':
21
23
  return this.ozonetelVoiceStrategy;
24
+ case 'third_party_tubelight':
25
+ return this.tubelightVoiceStrategy;
22
26
 
23
27
  default:
24
28
  throw new Error(
@@ -30,7 +34,8 @@ export class TelephoneFactory implements BaseFactory {
30
34
  getSupportedCombinations(): Array<{ service: string; provider: string }> {
31
35
  return [
32
36
  { service: 'THIRD_PARTY', provider: 'knowlarity' },
33
- { service: 'THIRD_PARTY', provider: 'ozonetel' }
37
+ { service: 'THIRD_PARTY', provider: 'ozonetel' },
38
+ { service: 'THIRD_PARTY', provider: 'tubelight' }
34
39
  ];
35
40
  }
36
41
 
@@ -0,0 +1,122 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import axios from 'axios';
3
+ import {
4
+ CommunicationResult,
5
+ CommunicationStrategy,
6
+ } from '../communication.strategy';
7
+
8
+ interface TubelightVoiceConfig {
9
+ userName: string;
10
+ password: string;
11
+ agentVerificationKey: string;
12
+ tenantId: string;
13
+ agentLoginUrl?: string;
14
+ }
15
+
16
+ @Injectable()
17
+ export class TubelightVoiceStrategy implements CommunicationStrategy {
18
+ private readonly baseUrl = 'https://portal.tubelightcommunications.com/voice/api/v1';
19
+
20
+ async sendMessage(
21
+ to: string,
22
+ message: string,
23
+ config: TubelightVoiceConfig,
24
+ ): Promise<CommunicationResult> {
25
+ if (!this.validateConfig(config)) {
26
+ return {
27
+ success: false,
28
+ provider: 'tubelight',
29
+ service: 'THIRD_PARTY',
30
+ error: 'Invalid Tubelight Voice configuration',
31
+ timestamp: new Date(),
32
+ };
33
+ }
34
+
35
+ try {
36
+ const token = await this.authenticate(config);
37
+ if (!token) {
38
+ return {
39
+ success: false,
40
+ provider: 'tubelight',
41
+ service: 'THIRD_PARTY',
42
+ error: 'Failed to authenticate with Tubelight',
43
+ timestamp: new Date(),
44
+ };
45
+ }
46
+
47
+ const url = `${this.baseUrl}/user/sso/agent/outbound-call`;
48
+ await axios.post(
49
+ url,
50
+ {
51
+ agentVerificationKey: config.agentVerificationKey,
52
+ customerNumber: to,
53
+ },
54
+ {
55
+ headers: {
56
+ 'X-TENANT-ID': config.tenantId,
57
+ 'Content-Type': 'application/json',
58
+ Authorization: `Bearer ${token}`,
59
+ },
60
+ },
61
+ );
62
+
63
+ return {
64
+ success: true,
65
+ messageId: `tubelight-voice-${Date.now()}`,
66
+ provider: 'tubelight',
67
+ service: 'THIRD_PARTY',
68
+ timestamp: new Date(),
69
+ };
70
+ } catch (error) {
71
+ let errorMsg = 'Unknown error';
72
+ if (axios.isAxiosError(error)) {
73
+ errorMsg = error.response?.data?.message || error.message;
74
+ } else if (error instanceof Error) {
75
+ errorMsg = error.message;
76
+ }
77
+ return {
78
+ success: false,
79
+ provider: 'tubelight',
80
+ service: 'THIRD_PARTY',
81
+ error: errorMsg,
82
+ timestamp: new Date(),
83
+ };
84
+ }
85
+ }
86
+
87
+ private async authenticate(
88
+ config: TubelightVoiceConfig,
89
+ ): Promise<string | null> {
90
+ try {
91
+ const response = await axios.post(
92
+ `${this.baseUrl}/auth/login`,
93
+ {
94
+ user_name: config.userName,
95
+ password: config.password,
96
+ },
97
+ {
98
+ headers: {
99
+ 'Content-Type': 'application/json',
100
+ 'X-TENANT-ID': config.tenantId,
101
+ },
102
+ },
103
+ );
104
+
105
+ const data = response.data as { bearer_token?: string; access_token?: string; token?: string };
106
+ return data?.bearer_token || data?.access_token || data?.token || null;
107
+ } catch (error) {
108
+ console.error('Failed to authenticate with Tubelight:', error);
109
+ return null;
110
+ }
111
+ }
112
+
113
+ validateConfig(config: Partial<TubelightVoiceConfig>): boolean {
114
+ return !!(
115
+ config &&
116
+ config.userName &&
117
+ config.password &&
118
+ config.agentVerificationKey &&
119
+ config.tenantId
120
+ );
121
+ }
122
+ }
@@ -142,47 +142,47 @@ export class EntityController {
142
142
  @Req() req: Request & { user: any },
143
143
  ) {
144
144
  const loggedInUser = req.user.userData;
145
- const appcode = req.user.userData.appcode;
146
-
145
+ const appcode = loggedInUser.appcode;
146
+
147
147
  if (!entityType) {
148
- throw new BadRequestException(
149
- `Query parameter "entity_type" is required`,
150
- );
148
+ throw new BadRequestException(`Query parameter "entity_type" is required`);
151
149
  }
152
-
150
+
153
151
  const entityMaster = await this.entityMasterService.getEntityData(
154
152
  entityType,
155
153
  loggedInUser,
156
154
  );
157
- const entityService =
158
- await this.reflectionHelper.getBean<EntityServiceImpl>(
159
- entityMaster.entity_service,
160
- );
161
-
155
+
156
+ const entityService = await this.reflectionHelper.getBean<EntityServiceImpl>(
157
+ entityMaster.entity_service,
158
+ );
159
+
162
160
  if (!entityService) {
163
161
  throw new InternalServerErrorException(
164
- `No service found for Entity Type ${entityType}`,
162
+ `No service found for entity_type "${entityType}"`,
165
163
  );
166
164
  }
167
-
168
- let savedData = await entityService.createEntity(
165
+
166
+ // Create
167
+ const savedData = await entityService.createEntity(
169
168
  entityData,
170
169
  loggedInUser as UserData,
171
170
  null,
172
171
  appcode,
173
172
  );
174
-
173
+
174
+ // ✅ Run workflow automation (no preUpdateStates for CREATE)
175
175
  await this.workflowAutomationEngineService.handleEntityEvent(
176
- entityData.entity_type,
176
+ entityType,
177
177
  'CREATE',
178
178
  savedData,
179
- null,
180
179
  loggedInUser,
180
+ null,
181
181
  );
182
-
182
+
183
183
  return savedData;
184
184
  }
185
-
185
+
186
186
  @Post('update/:id')
187
187
  @HttpCode(200)
188
188
  async update(
@@ -192,58 +192,67 @@ export class EntityController {
192
192
  @Req() req: Request & { user: any },
193
193
  ) {
194
194
  const loggedInUser = req.user.userData;
195
-
195
+
196
196
  if (!entityType) {
197
- throw new BadRequestException(
198
- 'Query parameter "entity_type" is required',
199
- );
197
+ throw new BadRequestException('Query parameter "entity_type" is required');
200
198
  }
201
-
199
+
202
200
  const entityMaster = await this.entityMasterService.getEntityData(
203
201
  entityType,
204
202
  loggedInUser,
205
203
  );
206
-
207
- const entityService =
208
- await this.reflectionHelper.getBean<EntityServiceImpl>(
209
- entityMaster.entity_service,
210
- );
211
-
204
+
205
+ const entityService = await this.reflectionHelper.getBean<EntityServiceImpl>(
206
+ entityMaster.entity_service,
207
+ );
208
+
212
209
  if (!entityService) {
213
210
  throw new InternalServerErrorException(
214
211
  `No service found for entity_type "${entityType}"`,
215
212
  );
216
213
  }
217
-
214
+
215
+ // 1️⃣ Get old state
218
216
  const existingEntity = await entityService.getEntityData(
219
217
  entityType,
220
218
  id,
221
219
  loggedInUser,
222
220
  );
223
-
224
221
  if (!existingEntity) {
225
222
  throw new NotFoundException(`No entity found for id "${id}"`);
226
223
  }
227
-
228
- let updatedData = await entityService.updateEntity(
224
+
225
+ // 2️⃣ Pre-evaluate criteria
226
+ const workflows = await this.workflowAutomationEngineService['wfService'].getActiveRules(
227
+ entityType,
228
+ 'UPDATE',
229
+ );
230
+ const preUpdateStates: Record<number, boolean> = {};
231
+ for (const wf of workflows) {
232
+ preUpdateStates[wf.id] = await this.workflowAutomationEngineService[
233
+ 'filterEvaluator'
234
+ ].evaluateCriteria(entityType, wf.condition_filter_code, existingEntity.id, loggedInUser);
235
+ }
236
+
237
+ // 3️⃣ Update
238
+ const updatedData = await entityService.updateEntity(
229
239
  entityData,
230
240
  loggedInUser as UserData,
231
241
  );
232
-
242
+
243
+ // 4️⃣ Run workflow automation (pass preUpdateStates)
233
244
  await this.workflowAutomationEngineService.handleEntityEvent(
234
- entityData.entity_type,
245
+ entityType,
235
246
  'UPDATE',
236
247
  updatedData,
237
- existingEntity,
238
248
  loggedInUser,
249
+ preUpdateStates,
239
250
  );
240
-
251
+
241
252
  return updatedData;
242
- // return {
243
- // success: true,
244
- // data: updatedEntity,
245
- // };
246
253
  }
254
+
255
+
247
256
 
248
257
  @Post('delete/:id')
249
258
  @HttpCode(200)
@@ -31,7 +31,8 @@ import { Action } from 'src/module/workflow-automation/interface/action.interfac
31
31
  import { ActionHandler } from 'src/module/workflow-automation/interface/action.decorator';
32
32
 
33
33
  @Injectable()
34
- export class UserService extends EntityServiceImpl {
34
+ @ActionHandler('User')
35
+ export class UserService extends EntityServiceImpl implements Action {
35
36
  constructor(
36
37
  private userRepository: UserRepository,
37
38
  private userRoleMappingService: UserRoleMappingService,
@@ -46,4 +46,7 @@ export class ActionDataEntity extends BaseEntity {
46
46
 
47
47
  @Column({ nullable: true, default: 0 })
48
48
  resubmit_count: number;
49
+
50
+ @Column({ type: 'int', nullable: true })
51
+ dependent_action_id: number;
49
52
  }
@@ -38,4 +38,7 @@ export class ActionEntity extends BaseEntity {
38
38
 
39
39
  @Column({ type: 'boolean', nullable: true })
40
40
  is_template: boolean;
41
+
42
+ @Column({ type: 'int', nullable: true })
43
+ dependent_action_id: number;
41
44
  }
@@ -20,59 +20,86 @@ export class WorkflowAutomationEngineService {
20
20
 
21
21
  /**
22
22
  * Called from entity hooks (CREATE / UPDATE / DELETE)
23
+ * @param preUpdateStates optional, used for UPDATE events to pass pre-update criteria results
23
24
  */
24
- async handleEntityEvent(entityType, eventType, newEntity, oldEntity, user) {
25
- const workflows = await this.wfService.getActiveRules(
26
- entityType,
27
- eventType,
28
- );
29
-
25
+ async handleEntityEvent(
26
+ entityType: string,
27
+ eventType: 'CREATE' | 'UPDATE' | 'DELETE',
28
+ newEntity: any,
29
+ user: any,
30
+ preUpdateStates?: Record<number, boolean> | null,
31
+ ) {
32
+ const workflows = await this.wfService.getActiveRules(entityType, eventType);
33
+
30
34
  for (const wf of workflows) {
31
- const eventMatched = await this.filterEvaluator.evaluateTriggerAttributes(
32
- oldEntity,
33
- newEntity,
34
- wf.condition_filter_code,
35
+ // Step 1️⃣ Condition / Trigger evaluation
36
+ let triggerMatched = false;
37
+
38
+ if (eventType === 'CREATE') {
39
+ triggerMatched = await this.filterEvaluator.evaluateCriteria(
40
+ entityType,
41
+ wf.condition_filter_code,
42
+ newEntity.id,
43
+ user,
44
+ );
45
+ } else if (eventType === 'UPDATE' && preUpdateStates) {
46
+ const before = preUpdateStates[wf.id] ?? false;
47
+ const after = await this.filterEvaluator.evaluateCriteria(
48
+ entityType,
49
+ wf.condition_filter_code,
50
+ newEntity.id,
51
+ user,
52
+ );
53
+ triggerMatched = before !== after && after === true;
54
+ }
55
+
56
+ // 🔍 Log Step 1 result
57
+ console.log(
58
+ `[Workflow:${wf.id}] Step 1 - Trigger matched:`,
59
+ triggerMatched,
35
60
  );
36
- console.log(eventMatched, 'eventMatched');
37
- if (!eventMatched) continue;
38
-
61
+
62
+ if (!triggerMatched) continue;
63
+
64
+ // Step 2️⃣ Final criteria evaluation
39
65
  const criteriaMatched = await this.filterEvaluator.evaluateCriteria(
40
66
  entityType,
41
67
  wf.criteria_filter_code,
42
68
  newEntity.id,
43
69
  user,
44
70
  );
45
- console.log(criteriaMatched, 'criteriaMatched');
71
+
72
+ // 🔍 Log Step 2 result
73
+ console.log(
74
+ `[Workflow:${wf.id}] Step 2 - Criteria matched:`,
75
+ criteriaMatched,
76
+ );
77
+
46
78
  if (!criteriaMatched) continue;
47
-
79
+
80
+ // Step 3️⃣ Execute workflow actions
48
81
  await this.executeActions(wf.id, newEntity, user);
49
82
  }
50
83
  }
84
+
51
85
 
52
86
  private async executeActions(
53
87
  workflow_automation_id: number,
54
88
  entity: any,
55
89
  user: any,
56
90
  ) {
57
- // 1. Load actions for this rule from DB
58
- const actions = await this.wfService.getActionsForRule(
59
- workflow_automation_id,
60
- );
91
+ // Load actions for this rule from DB
92
+ const actions = await this.wfService.getActionsForRule(workflow_automation_id);
61
93
  console.log(actions, 'actions found');
62
- // (this should fetch from cr_workflow_automation_action)
63
94
 
64
95
  for (const action of actions) {
65
96
  const impl = this.actions.get(String(action.action_decorator)); // action_code = registered name
66
97
  if (!impl) {
67
- console.warn(
68
- `⚠️ No implementation found for action: ${action.action_decorator}`,
69
- );
98
+ console.warn(`⚠️ No implementation found for action: ${action.action_decorator}`);
70
99
  continue;
71
100
  }
72
101
 
73
- console.log(
74
- `🚀 Executing action ${action.action_decorator} for entity ${entity.id}`,
75
- );
102
+ console.log(`🚀 Executing action ${action.action_decorator} for entity ${entity.id}`);
76
103
  await impl.execute({ entity, user, config: action.payload });
77
104
  }
78
105
  }