rez_core 2.2.227 → 2.2.228
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/app.module.js +0 -2
- package/dist/app.module.js.map +1 -1
- package/dist/constant/global.constant.d.ts +1 -0
- package/dist/constant/global.constant.js +2 -1
- package/dist/constant/global.constant.js.map +1 -1
- package/dist/core.module.js +3 -0
- package/dist/core.module.js.map +1 -1
- package/dist/module/enterprise/enterprise.module.js +1 -1
- package/dist/module/filter/filter.module.js +3 -1
- package/dist/module/filter/filter.module.js.map +1 -1
- package/dist/module/filter/service/filter-evaluator.service.d.ts +9 -0
- package/dist/module/filter/service/filter-evaluator.service.js +61 -0
- package/dist/module/filter/service/filter-evaluator.service.js.map +1 -0
- package/dist/module/filter/service/saved-filter.service.d.ts +1 -1
- package/dist/module/filter/service/saved-filter.service.js.map +1 -1
- package/dist/module/meta/controller/entity.controller.d.ts +3 -1
- package/dist/module/meta/controller/entity.controller.js +11 -4
- package/dist/module/meta/controller/entity.controller.js.map +1 -1
- package/dist/module/meta/entity.module.js +2 -0
- package/dist/module/meta/entity.module.js.map +1 -1
- package/dist/module/meta/service/entity-service-impl.service.js +2 -1
- package/dist/module/meta/service/entity-service-impl.service.js.map +1 -1
- package/dist/module/user/service/user.service.d.ts +2 -0
- package/dist/module/user/service/user.service.js +4 -0
- package/dist/module/user/service/user.service.js.map +1 -1
- package/dist/module/workflow-automation/entity/workflow-automation-action.entity.d.ts +9 -0
- package/dist/module/workflow-automation/entity/workflow-automation-action.entity.js +47 -0
- package/dist/module/workflow-automation/entity/workflow-automation-action.entity.js.map +1 -0
- package/dist/module/workflow-automation/entity/workflow-automation.entity.d.ts +0 -1
- package/dist/module/workflow-automation/entity/workflow-automation.entity.js +0 -4
- package/dist/module/workflow-automation/entity/workflow-automation.entity.js.map +1 -1
- package/dist/module/workflow-automation/interface/action.decorator.d.ts +2 -0
- package/dist/module/workflow-automation/interface/action.decorator.js +8 -0
- package/dist/module/workflow-automation/interface/action.decorator.js.map +1 -0
- package/dist/module/workflow-automation/interface/action.interface.d.ts +4 -0
- package/dist/module/workflow-automation/interface/action.interface.js +3 -0
- package/dist/module/workflow-automation/interface/action.interface.js.map +1 -0
- package/dist/module/workflow-automation/service/action-registery.service.d.ts +11 -0
- package/dist/module/workflow-automation/service/action-registery.service.js +46 -0
- package/dist/module/workflow-automation/service/action-registery.service.js.map +1 -0
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.d.ts +12 -0
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js +60 -0
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js.map +1 -0
- package/dist/module/workflow-automation/service/workflow-automation.service.d.ts +13 -5
- package/dist/module/workflow-automation/service/workflow-automation.service.js +42 -7
- package/dist/module/workflow-automation/service/workflow-automation.service.js.map +1 -1
- package/dist/module/workflow-automation/workflow-automation.module.js +16 -3
- package/dist/module/workflow-automation/workflow-automation.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/app.module.ts +0 -1
- package/src/constant/global.constant.ts +1 -0
- package/src/core.module.ts +3 -0
- package/src/module/enterprise/enterprise.module.ts +1 -1
- package/src/module/filter/filter.module.ts +4 -1
- package/src/module/filter/service/filter-evaluator.service.ts +86 -0
- package/src/module/filter/service/saved-filter.service.ts +4 -3
- package/src/module/meta/controller/entity.controller.ts +23 -2
- package/src/module/meta/entity.module.ts +2 -0
- package/src/module/meta/service/entity-service-impl.service.ts +5 -1
- package/src/module/user/service/user.service.ts +7 -0
- package/src/module/workflow-automation/entity/workflow-automation-action.entity.ts +26 -0
- package/src/module/workflow-automation/entity/workflow-automation.entity.ts +0 -3
- package/src/module/workflow-automation/interface/action.decorator.ts +7 -0
- package/src/module/workflow-automation/interface/action.interface.ts +5 -0
- package/src/module/workflow-automation/service/action-registery.service.ts +35 -0
- package/src/module/workflow-automation/service/workflow-automation-engine.service.ts +76 -0
- package/src/module/workflow-automation/service/workflow-automation.service.ts +53 -5
- package/src/module/workflow-automation/workflow-automation.module.ts +16 -3
- package/src/module/user/test/user.controller.spec.ts +0 -61
- package/src/module/user/test/user.service.spec.ts +0 -62
package/package.json
CHANGED
package/src/app.module.ts
CHANGED
|
@@ -48,6 +48,7 @@ export const STAGE = 'WFS';
|
|
|
48
48
|
export const STAGE_GROUP = 'WFSG';
|
|
49
49
|
export const ACTION_CATEGORY = 'WFAC';
|
|
50
50
|
export const WORKFLOW_AUTOMATION = 'WFAM';
|
|
51
|
+
export const WORKFLOW_AUTOMATION_ACTION = 'WFAA';
|
|
51
52
|
export const STAGE_ACTION_MAPPING = 'WFSA';
|
|
52
53
|
export const COMM_TEMPLATE = 'TEM';
|
|
53
54
|
export const ACTION_TEMPLATE_MAPPING = 'WFAT';
|
package/src/core.module.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { AuthModule } from './module/auth/auth.module';
|
|
|
14
14
|
import { LeadModule } from './module/lead/lead.module';
|
|
15
15
|
import { LayoutPreferenceModule } from './module/layout_preference/layout_preference.module';
|
|
16
16
|
import { WorkflowModule } from './module/workflow/workflow.module';
|
|
17
|
+
import { WorkflowAutomationModule } from './module/workflow-automation/workflow-automation.module';
|
|
17
18
|
|
|
18
19
|
@Global()
|
|
19
20
|
@Module({})
|
|
@@ -35,6 +36,7 @@ export class CoreModule {
|
|
|
35
36
|
LeadModule,
|
|
36
37
|
LayoutPreferenceModule,
|
|
37
38
|
WorkflowModule,
|
|
39
|
+
WorkflowAutomationModule
|
|
38
40
|
];
|
|
39
41
|
|
|
40
42
|
const exportsArray = [
|
|
@@ -49,6 +51,7 @@ export class CoreModule {
|
|
|
49
51
|
LeadModule,
|
|
50
52
|
LayoutPreferenceModule,
|
|
51
53
|
WorkflowModule,
|
|
54
|
+
WorkflowAutomationModule
|
|
52
55
|
];
|
|
53
56
|
|
|
54
57
|
if (isSso) {
|
|
@@ -16,7 +16,7 @@ import { SchoolRepository } from './repository/school.repository';
|
|
|
16
16
|
TypeOrmModule.forFeature([OrganizationData, OrganizationAppMapping]),
|
|
17
17
|
forwardRef(() => UserModule),
|
|
18
18
|
UtilsModule,
|
|
19
|
-
EntityModule
|
|
19
|
+
EntityModule
|
|
20
20
|
],
|
|
21
21
|
controllers: [OrganizationController],
|
|
22
22
|
providers: [
|
|
@@ -7,18 +7,21 @@ import { SavedFilterRepositoryService } from './repository/saved-filter.reposito
|
|
|
7
7
|
import { EntityModule } from '../meta/entity.module';
|
|
8
8
|
import { FilterService } from './service/filter.service';
|
|
9
9
|
import { FilterController } from './controller/filter.controller';
|
|
10
|
+
import { FilterEvaluatorService } from './service/filter-evaluator.service';
|
|
10
11
|
|
|
11
12
|
@Module({
|
|
12
13
|
imports: [
|
|
13
14
|
TypeOrmModule.forFeature([SavedFilterDetail, SavedFilterMaster]),
|
|
14
15
|
forwardRef(() => EntityModule),
|
|
16
|
+
|
|
15
17
|
],
|
|
16
18
|
controllers: [FilterController],
|
|
17
19
|
providers: [
|
|
18
20
|
{ provide: 'SavedFilterService', useClass: SavedFilterService },
|
|
19
21
|
SavedFilterRepositoryService,
|
|
20
22
|
FilterService,
|
|
23
|
+
FilterEvaluatorService
|
|
21
24
|
],
|
|
22
|
-
exports: ['SavedFilterService', SavedFilterRepositoryService, FilterService],
|
|
25
|
+
exports: ['SavedFilterService', SavedFilterRepositoryService, FilterService,FilterEvaluatorService],
|
|
23
26
|
})
|
|
24
27
|
export class FilterModule {}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
3
|
+
import { SavedFilterDetail } from 'src/module/filter/entity/saved-filter-detail.entity';
|
|
4
|
+
import { FilterService } from './filter.service';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class FilterEvaluatorService {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly dataSource: DataSource,
|
|
10
|
+
private readonly filterService: FilterService,
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if any of the trigger attributes are modified in this event.
|
|
15
|
+
* @param oldEntity previous state
|
|
16
|
+
* @param newEntity new state
|
|
17
|
+
* @param savedFilterCode code of saved filter that contains trigger attributes
|
|
18
|
+
* @returns true if at least one trigger attribute changed (and optionally matches value)
|
|
19
|
+
*/
|
|
20
|
+
async evaluateTriggerAttributes(
|
|
21
|
+
oldEntity: Record<string, any> | null,
|
|
22
|
+
newEntity: Record<string, any>,
|
|
23
|
+
savedFilterCode: string,
|
|
24
|
+
): Promise<boolean> {
|
|
25
|
+
// Fetch trigger filter details
|
|
26
|
+
const triggerFilters: SavedFilterDetail[] = await this.dataSource.query(
|
|
27
|
+
`SELECT *
|
|
28
|
+
FROM cr_saved_filter_detail
|
|
29
|
+
WHERE mapped_filter_code = ?`,
|
|
30
|
+
[savedFilterCode],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// If no trigger filters defined, just return true (so criteria can run)
|
|
34
|
+
if (!triggerFilters || triggerFilters.length === 0) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const oldState = oldEntity || {};
|
|
39
|
+
|
|
40
|
+
// Check each trigger attribute
|
|
41
|
+
for (const filter of triggerFilters) {
|
|
42
|
+
const attr = filter.filter_attribute;
|
|
43
|
+
const expectedValue = filter.filter_value;
|
|
44
|
+
|
|
45
|
+
const oldVal = oldState[attr];
|
|
46
|
+
const newVal = newEntity[attr];
|
|
47
|
+
|
|
48
|
+
// If value changed and (no expected value defined OR matches expected)
|
|
49
|
+
if (oldVal !== newVal && (!expectedValue || newVal == expectedValue)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// No trigger attribute matched
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Evaluate the criteria for an entity using the filter service
|
|
60
|
+
* @param entityType type of entity
|
|
61
|
+
* @param entityId id of entity to evaluate
|
|
62
|
+
* @param criteriaFilterCode saved filter code for criteria
|
|
63
|
+
* @param filterService instance of your FilterService
|
|
64
|
+
*/
|
|
65
|
+
async evaluateCriteria(
|
|
66
|
+
entityType: string,
|
|
67
|
+
criteriaFilterCode: string,
|
|
68
|
+
entityId: number,
|
|
69
|
+
loggedInUser: any,
|
|
70
|
+
): Promise<boolean> {
|
|
71
|
+
if (!criteriaFilterCode) return true; // no criteria = always true
|
|
72
|
+
|
|
73
|
+
const filterRequest = {
|
|
74
|
+
entity_type: entityType,
|
|
75
|
+
savedFilterCode: criteriaFilterCode,
|
|
76
|
+
queryParams: { id: entityId },
|
|
77
|
+
page: 1,
|
|
78
|
+
size: 1,
|
|
79
|
+
loggedInUser: loggedInUser || { organization_id: null },
|
|
80
|
+
} as any;
|
|
81
|
+
|
|
82
|
+
const result = await this.filterService.applyFilter(filterRequest);
|
|
83
|
+
|
|
84
|
+
return result?.data?.entity_list?.length > 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
2
|
-
import { EntityServiceImpl } from '
|
|
1
|
+
import { BadRequestException, forwardRef, Inject, Injectable } from '@nestjs/common';
|
|
2
|
+
import { EntityServiceImpl } from 'src/module/meta/service/entity-service-impl.service';
|
|
3
3
|
import { BaseEntity } from '../../meta/entity/base-entity.entity';
|
|
4
4
|
import { UserData } from '../../user/entity/user.entity';
|
|
5
5
|
import { SavedFilterMaster } from '../entity/saved-filter-master.entity';
|
|
@@ -9,7 +9,8 @@ import { SavedFilterRepositoryService } from '../repository/saved-filter.reposit
|
|
|
9
9
|
|
|
10
10
|
@Injectable()
|
|
11
11
|
export class SavedFilterService extends EntityServiceImpl {
|
|
12
|
-
constructor(private readonly savedFilterRepo: SavedFilterRepositoryService
|
|
12
|
+
constructor(private readonly savedFilterRepo: SavedFilterRepositoryService
|
|
13
|
+
) {
|
|
13
14
|
super();
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -27,6 +27,7 @@ import * as path from 'path';
|
|
|
27
27
|
import { UserService } from '../../user/service/user.service';
|
|
28
28
|
import { ENTITYTYPE_USER } from '../../../constant/global.constant';
|
|
29
29
|
import { UserData } from '../../user/entity/user.entity';
|
|
30
|
+
import { WorkflowAutomationEngineService } from 'src/module/workflow-automation/service/workflow-automation-engine.service';
|
|
30
31
|
|
|
31
32
|
@UseGuards(JwtAuthGuard)
|
|
32
33
|
@Controller('entity')
|
|
@@ -35,6 +36,7 @@ export class EntityController {
|
|
|
35
36
|
private entityService: EntityServiceImpl,
|
|
36
37
|
private reflectionHelper: ReflectionHelper,
|
|
37
38
|
private entityMasterService: EntityMasterService,
|
|
39
|
+
private readonly workflowAutomationEngineService: WorkflowAutomationEngineService,
|
|
38
40
|
) {}
|
|
39
41
|
|
|
40
42
|
@Get('getById/:id')
|
|
@@ -163,12 +165,22 @@ export class EntityController {
|
|
|
163
165
|
);
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
|
|
168
|
+
let savedData = await entityService.createEntity(
|
|
167
169
|
entityData,
|
|
168
170
|
loggedInUser as UserData,
|
|
169
171
|
null,
|
|
170
172
|
appcode,
|
|
171
173
|
);
|
|
174
|
+
|
|
175
|
+
await this.workflowAutomationEngineService.handleEntityEvent(
|
|
176
|
+
entityData.entity_type,
|
|
177
|
+
'CREATE',
|
|
178
|
+
entityData,
|
|
179
|
+
null,
|
|
180
|
+
loggedInUser,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return savedData;
|
|
172
184
|
}
|
|
173
185
|
|
|
174
186
|
@Post('update/:id')
|
|
@@ -213,11 +225,20 @@ export class EntityController {
|
|
|
213
225
|
throw new NotFoundException(`No entity found for id "${id}"`);
|
|
214
226
|
}
|
|
215
227
|
|
|
216
|
-
|
|
228
|
+
let updatedData = await entityService.updateEntity(
|
|
217
229
|
entityData,
|
|
218
230
|
loggedInUser as UserData,
|
|
219
231
|
);
|
|
220
232
|
|
|
233
|
+
await this.workflowAutomationEngineService.handleEntityEvent(
|
|
234
|
+
entityData.entity_type,
|
|
235
|
+
'UPDATE',
|
|
236
|
+
entityData,
|
|
237
|
+
existingEntity,
|
|
238
|
+
loggedInUser,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
return updatedData;
|
|
221
242
|
// return {
|
|
222
243
|
// success: true,
|
|
223
244
|
// data: updatedEntity,
|
|
@@ -58,6 +58,7 @@ import { EntityRelationController } from './controller/entity-relation.controlle
|
|
|
58
58
|
import { CommonService } from './service/common.service';
|
|
59
59
|
import { EntityMasterRepository } from './repository/entity-master.repository';
|
|
60
60
|
import { EntityMasterController } from './controller/entity-master.controller';
|
|
61
|
+
import { WorkflowAutomationModule } from '../workflow-automation/workflow-automation.module';
|
|
61
62
|
|
|
62
63
|
@Module({
|
|
63
64
|
imports: [
|
|
@@ -77,6 +78,7 @@ import { EntityMasterController } from './controller/entity-master.controller';
|
|
|
77
78
|
forwardRef(() => ListMasterModule),
|
|
78
79
|
forwardRef(() => FilterModule),
|
|
79
80
|
UtilsModule,
|
|
81
|
+
WorkflowAutomationModule
|
|
80
82
|
],
|
|
81
83
|
providers: [
|
|
82
84
|
EntityMasterService,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EntityService } from './entity.service';
|
|
2
2
|
import {
|
|
3
3
|
BadRequestException,
|
|
4
|
+
forwardRef,
|
|
4
5
|
Inject,
|
|
5
6
|
Injectable,
|
|
6
7
|
NotFoundException,
|
|
@@ -30,6 +31,7 @@ import { ListMasterItemService } from 'src/module/listmaster/service/list-master
|
|
|
30
31
|
import { AttributeMasterService } from './attribute-master.service';
|
|
31
32
|
import { ResolverService } from './resolver.service';
|
|
32
33
|
import { NotFound } from '@aws-sdk/client-s3';
|
|
34
|
+
import { WorkflowAutomationEngineService } from 'src/module/workflow-automation/service/workflow-automation-engine.service';
|
|
33
35
|
|
|
34
36
|
@Injectable()
|
|
35
37
|
export class EntityServiceImpl implements EntityService<BaseEntity> {
|
|
@@ -124,7 +126,9 @@ export class EntityServiceImpl implements EntityService<BaseEntity> {
|
|
|
124
126
|
if (!entityData.status) entityData.status = statusList[0].id;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
|
|
129
|
+
let savedData = repo.save(entityData);
|
|
130
|
+
|
|
131
|
+
return savedData;
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
async deleteEntity(entityType: string, entityId: number, loggedInUser) {
|
|
@@ -27,6 +27,8 @@ import { ServiceResult } from 'src/dtos/response.dto';
|
|
|
27
27
|
import { ListMasterService } from 'src/module/listmaster/service/list-master.service';
|
|
28
28
|
import { OrganizationRepository } from 'src/module/enterprise/repository/organization.repository';
|
|
29
29
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
30
|
+
import { Action } from 'src/module/workflow-automation/interface/action.interface';
|
|
31
|
+
import { ActionHandler } from 'src/module/workflow-automation/interface/action.decorator';
|
|
30
32
|
|
|
31
33
|
@Injectable()
|
|
32
34
|
export class UserService extends EntityServiceImpl {
|
|
@@ -135,6 +137,11 @@ export class UserService extends EntityServiceImpl {
|
|
|
135
137
|
return { success: true, data: savedData };
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
name : string = 'UserService';
|
|
141
|
+
async execute(payload: any): Promise<any> {
|
|
142
|
+
console.log('payload', payload);
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
async getEntityData(
|
|
139
146
|
entityType: string,
|
|
140
147
|
id: number,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { WORKFLOW_AUTOMATION_ACTION } from 'src/constant/global.constant';
|
|
2
|
+
import { BaseEntity } from 'src/module/meta/entity/base-entity.entity';
|
|
3
|
+
import { Column, Entity } from 'typeorm';
|
|
4
|
+
|
|
5
|
+
@Entity({ name: 'cr_workflow_automation_action' })
|
|
6
|
+
export class WorkflowAutomationActionEntity extends BaseEntity {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.entity_type = WORKFLOW_AUTOMATION_ACTION;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Column({ type: 'int', nullable: true })
|
|
13
|
+
workflow_automation_id: number;
|
|
14
|
+
|
|
15
|
+
@Column({ type: 'int', nullable: true })
|
|
16
|
+
action_category_id: number;
|
|
17
|
+
|
|
18
|
+
@Column({ type: 'varchar', nullable: true })
|
|
19
|
+
payload: string;
|
|
20
|
+
|
|
21
|
+
@Column({ type: 'varchar', nullable: true })
|
|
22
|
+
action_decorator: string;
|
|
23
|
+
|
|
24
|
+
@Column({ type: 'varchar', nullable: true })
|
|
25
|
+
entity_method: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/module/workflow-automation/service/action-registry.service.ts
|
|
2
|
+
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
3
|
+
import { DiscoveryService, ModuleRef, Reflector } from '@nestjs/core';
|
|
4
|
+
import { WorkflowAutomationEngineService } from './workflow-automation-engine.service';
|
|
5
|
+
import { Action } from '../interface/action.interface';
|
|
6
|
+
import { ACTION_HANDLER } from '../interface/action.decorator';
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class ActionRegistryService implements OnModuleInit {
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly discoveryService: DiscoveryService,
|
|
12
|
+
private readonly moduleRef: ModuleRef,
|
|
13
|
+
private readonly reflector: Reflector,
|
|
14
|
+
private readonly engine: WorkflowAutomationEngineService,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
onModuleInit() {
|
|
18
|
+
const providers = this.discoveryService.getProviders();
|
|
19
|
+
|
|
20
|
+
for (const wrapper of providers) {
|
|
21
|
+
const { instance } = wrapper;
|
|
22
|
+
if (!instance) continue;
|
|
23
|
+
|
|
24
|
+
const actionName = this.reflector.get<string>(
|
|
25
|
+
ACTION_HANDLER,
|
|
26
|
+
instance.constructor,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (actionName) {
|
|
30
|
+
this.engine.registerAction(actionName, instance as Action);
|
|
31
|
+
console.log(`✅ Registered action handler: ${actionName}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// package/src/services/workflow-automation-engine.service.ts
|
|
2
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
3
|
+
import { WorkflowAutomationService } from './workflow-automation.service';
|
|
4
|
+
import { FilterEvaluatorService } from '../../filter/service/filter-evaluator.service';
|
|
5
|
+
import { Action } from '../interface/action.interface';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class WorkflowAutomationEngineService {
|
|
9
|
+
private readonly actions = new Map<string, Action>();
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
@Inject('WorkflowAutomationService')
|
|
13
|
+
private readonly wfService: WorkflowAutomationService,
|
|
14
|
+
private readonly filterEvaluator: FilterEvaluatorService,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
registerAction(actionName: string, actionInstance: Action) {
|
|
18
|
+
this.actions.set(actionName, actionInstance);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Called from entity hooks (CREATE / UPDATE / DELETE)
|
|
23
|
+
*/
|
|
24
|
+
async handleEntityEvent(entityType, eventType, newEntity, oldEntity, user) {
|
|
25
|
+
const workflows = await this.wfService.getActiveRules(
|
|
26
|
+
entityType,
|
|
27
|
+
eventType,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
for (const wf of workflows) {
|
|
31
|
+
const eventMatched = await this.filterEvaluator.evaluateTriggerAttributes(
|
|
32
|
+
oldEntity,
|
|
33
|
+
newEntity,
|
|
34
|
+
wf.condition_filter_code,
|
|
35
|
+
);
|
|
36
|
+
if (!eventMatched) continue;
|
|
37
|
+
|
|
38
|
+
const criteriaMatched = await this.filterEvaluator.evaluateCriteria(
|
|
39
|
+
entityType,
|
|
40
|
+
wf.criteria_filter_code,
|
|
41
|
+
newEntity.id,
|
|
42
|
+
user,
|
|
43
|
+
);
|
|
44
|
+
if (!criteriaMatched) continue;
|
|
45
|
+
|
|
46
|
+
await this.executeActions(wf.id, newEntity, user);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async executeActions(
|
|
51
|
+
workflow_automation_id: number,
|
|
52
|
+
entity: any,
|
|
53
|
+
user: any,
|
|
54
|
+
) {
|
|
55
|
+
// 1. Load actions for this rule from DB
|
|
56
|
+
const actions = await this.wfService.getActionsForRule(
|
|
57
|
+
workflow_automation_id,
|
|
58
|
+
);
|
|
59
|
+
// (this should fetch from cr_workflow_automation_action)
|
|
60
|
+
|
|
61
|
+
for (const action of actions) {
|
|
62
|
+
const impl = this.actions.get(String(action.action_decorator)); // action_code = registered name
|
|
63
|
+
if (!impl) {
|
|
64
|
+
console.warn(
|
|
65
|
+
`⚠️ No implementation found for action: ${action.action_decorator}`,
|
|
66
|
+
);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(
|
|
71
|
+
`🚀 Executing action ${action.action_decorator} for entity ${entity.id}`,
|
|
72
|
+
);
|
|
73
|
+
await impl.execute({ entity, user, config: action.payload });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,10 +1,58 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { Repository } from 'typeorm';
|
|
4
|
+
import { WorkflowAutomation } from '../entity/workflow-automation.entity';
|
|
5
|
+
import { WorkflowAutomationActionEntity } from '../entity/workflow-automation-action.entity';
|
|
4
6
|
|
|
5
7
|
@Injectable()
|
|
6
|
-
export class WorkflowAutomationService
|
|
7
|
-
constructor(
|
|
8
|
-
|
|
8
|
+
export class WorkflowAutomationService {
|
|
9
|
+
constructor(
|
|
10
|
+
@InjectRepository(WorkflowAutomation)
|
|
11
|
+
private readonly wfRepo: Repository<WorkflowAutomation>,
|
|
12
|
+
@InjectRepository(WorkflowAutomationActionEntity)
|
|
13
|
+
private readonly actionRepo: Repository<WorkflowAutomationActionEntity>,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
async createRule(
|
|
17
|
+
data: Partial<WorkflowAutomation>,
|
|
18
|
+
): Promise<WorkflowAutomation> {
|
|
19
|
+
const rule = this.wfRepo.create(data);
|
|
20
|
+
return this.wfRepo.save(rule);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async updateRule(
|
|
24
|
+
id: number,
|
|
25
|
+
data: Partial<WorkflowAutomation>,
|
|
26
|
+
): Promise<WorkflowAutomation | null> {
|
|
27
|
+
await this.wfRepo.update(id, data);
|
|
28
|
+
return this.wfRepo.findOneBy({ id });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async deleteRule(id: number): Promise<void> {
|
|
32
|
+
await this.wfRepo.delete(id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getRule(id: number): Promise<WorkflowAutomation | null> {
|
|
36
|
+
return this.wfRepo.findOneBy({ id });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getActiveRules(
|
|
40
|
+
entityType: string,
|
|
41
|
+
event: string,
|
|
42
|
+
): Promise<WorkflowAutomation[]> {
|
|
43
|
+
return this.wfRepo.find({
|
|
44
|
+
where: {
|
|
45
|
+
mapped_entity_type: entityType,
|
|
46
|
+
trigger_event: event,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getActionsForRule(
|
|
52
|
+
workflow_automation_id: number,
|
|
53
|
+
): Promise<WorkflowAutomationActionEntity[]> {
|
|
54
|
+
return this.actionRepo.find({
|
|
55
|
+
where: { workflow_automation_id },
|
|
56
|
+
});
|
|
9
57
|
}
|
|
10
58
|
}
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
2
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
3
|
+
import { DiscoveryModule } from '@nestjs/core';
|
|
3
4
|
import { WorkflowAutomation } from './entity/workflow-automation.entity';
|
|
4
5
|
import { WorkflowAutomationController } from './controller/workflow-automation.controller';
|
|
6
|
+
import { WorkflowAutomationEngineService } from './service/workflow-automation-engine.service';
|
|
7
|
+
import { FilterModule } from '../filter/filter.module';
|
|
5
8
|
import { WorkflowAutomationService } from './service/workflow-automation.service';
|
|
6
|
-
import {
|
|
9
|
+
import { WorkflowAutomationActionEntity } from './entity/workflow-automation-action.entity';
|
|
10
|
+
import { ActionRegistryService } from './service/action-registery.service';
|
|
7
11
|
|
|
8
12
|
@Module({
|
|
9
|
-
imports: [
|
|
13
|
+
imports: [
|
|
14
|
+
TypeOrmModule.forFeature([
|
|
15
|
+
WorkflowAutomation,
|
|
16
|
+
WorkflowAutomationActionEntity,
|
|
17
|
+
]),
|
|
18
|
+
FilterModule,
|
|
19
|
+
DiscoveryModule, // 👈 enables provider scanning
|
|
20
|
+
],
|
|
10
21
|
providers: [
|
|
22
|
+
WorkflowAutomationEngineService,
|
|
11
23
|
{
|
|
12
24
|
provide: 'WorkflowAutomationService',
|
|
13
25
|
useClass: WorkflowAutomationService,
|
|
14
26
|
},
|
|
27
|
+
ActionRegistryService, // 👈 auto-registers actions
|
|
15
28
|
],
|
|
16
|
-
exports: [],
|
|
29
|
+
exports: [WorkflowAutomationEngineService, 'WorkflowAutomationService'],
|
|
17
30
|
controllers: [WorkflowAutomationController],
|
|
18
31
|
})
|
|
19
32
|
export class WorkflowAutomationModule {}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Test, TestingModule } from '@nestjs/testing';
|
|
2
|
-
import { UserService } from '../service/user.service';
|
|
3
|
-
import { ForbiddenException } from '@nestjs/common';
|
|
4
|
-
import { UserController } from '../controller/user.controller';
|
|
5
|
-
|
|
6
|
-
describe('UserController', () => {
|
|
7
|
-
let userController: UserController;
|
|
8
|
-
let userService: UserService;
|
|
9
|
-
|
|
10
|
-
beforeEach(async () => {
|
|
11
|
-
const module: TestingModule = await Test.createTestingModule({
|
|
12
|
-
controllers: [UserController],
|
|
13
|
-
providers: [
|
|
14
|
-
{
|
|
15
|
-
provide: 'UserService',
|
|
16
|
-
useValue: {
|
|
17
|
-
checkEmailExists: jest.fn(),
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
],
|
|
21
|
-
}).compile();
|
|
22
|
-
|
|
23
|
-
userController = module.get<UserController>(UserController);
|
|
24
|
-
userService = module.get<UserService>('UserService');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('checkEmail', () => {
|
|
28
|
-
it('✅ should return success response when email exists', async () => {
|
|
29
|
-
const mockResponse = {
|
|
30
|
-
success: true,
|
|
31
|
-
message:
|
|
32
|
-
'An account already exists for this email address. Login or use a different email address to sign up.',
|
|
33
|
-
userId: 101,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
(userService.checkEmailExists as jest.Mock).mockResolvedValue(
|
|
37
|
-
mockResponse,
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const result = await userController.checkEmail('test@example.com');
|
|
41
|
-
|
|
42
|
-
expect(result).toEqual(mockResponse);
|
|
43
|
-
expect(userService.checkEmailExists).toHaveBeenCalledWith(
|
|
44
|
-
'test@example.com',
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('❌ should throw ForbiddenException when email does not exist', async () => {
|
|
49
|
-
(userService.checkEmailExists as jest.Mock).mockRejectedValue(
|
|
50
|
-
new ForbiddenException('No account found with this email address.'),
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
await expect(
|
|
54
|
-
userController.checkEmail('notfound@example.com'),
|
|
55
|
-
).rejects.toThrow(ForbiddenException);
|
|
56
|
-
expect(userService.checkEmailExists).toHaveBeenCalledWith(
|
|
57
|
-
'notfound@example.com',
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
});
|