@rytass/bpm-core-nestjs-module 0.13.3 → 0.13.4
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/CHANGELOG.md +42 -0
- package/package.json +13 -6
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/attachment/attachment-storage.provider.d.ts +11 -0
- package/src/lib/attachment/attachment-storage.provider.js +15 -1
- package/src/lib/attachment/attachment-storage.provider.js.map +1 -1
- package/src/lib/bpm/bpm-root-options.d.ts +134 -0
- package/src/lib/bpm/bpm-root-options.js +61 -0
- package/src/lib/bpm/bpm-root-options.js.map +1 -0
- package/src/lib/bpm/bpm-root.module.d.ts +101 -137
- package/src/lib/bpm/bpm-root.module.js +83 -118
- package/src/lib/bpm/bpm-root.module.js.map +1 -1
- package/src/lib/bpm/index.d.ts +1 -0
- package/src/lib/bpm/index.js +1 -0
- package/src/lib/bpm/index.js.map +1 -1
- package/src/lib/calendar/business-calendar.provider.d.ts +8 -4
- package/src/lib/calendar/business-calendar.provider.js +15 -6
- package/src/lib/calendar/business-calendar.provider.js.map +1 -1
- package/src/lib/common/outbox.d.ts +26 -0
- package/src/lib/common/outbox.js +64 -0
- package/src/lib/common/outbox.js.map +1 -0
- package/src/lib/delegation/delegation.service.js +7 -7
- package/src/lib/delegation/delegation.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.module.js +2 -4
- package/src/lib/form-data-source/form-data-source.module.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.provider.d.ts +14 -0
- package/src/lib/form-data-source/form-data-source.provider.js +23 -0
- package/src/lib/form-data-source/form-data-source.provider.js.map +1 -0
- package/src/lib/form-data-source/index.d.ts +1 -0
- package/src/lib/form-data-source/index.js +1 -0
- package/src/lib/form-data-source/index.js.map +1 -1
- package/src/lib/identity/default-member-resolver.d.ts +39 -0
- package/src/lib/identity/default-member-resolver.js +81 -0
- package/src/lib/identity/default-member-resolver.js.map +1 -0
- package/src/lib/identity/identity.module.d.ts +12 -2
- package/src/lib/identity/identity.module.js +2 -1
- package/src/lib/identity/identity.module.js.map +1 -1
- package/src/lib/identity/index.d.ts +1 -0
- package/src/lib/identity/index.js +1 -0
- package/src/lib/identity/index.js.map +1 -1
- package/src/lib/migrations/0000000023000-workflow-webhook-deliveries.d.ts +17 -0
- package/src/lib/migrations/0000000023000-workflow-webhook-deliveries.js +61 -0
- package/src/lib/migrations/0000000023000-workflow-webhook-deliveries.js.map +1 -0
- package/src/lib/migrations/0000000024000-workflow-webhook-endpoints.d.ts +16 -0
- package/src/lib/migrations/0000000024000-workflow-webhook-endpoints.js +71 -0
- package/src/lib/migrations/0000000024000-workflow-webhook-endpoints.js.map +1 -0
- package/src/lib/migrations/index.d.ts +2 -0
- package/src/lib/migrations/index.js +6 -0
- package/src/lib/migrations/index.js.map +1 -1
- package/src/lib/notification/notification-delivery.service.js +4 -57
- package/src/lib/notification/notification-delivery.service.js.map +1 -1
- package/src/lib/notification/notification.service.js +4 -3
- package/src/lib/notification/notification.service.js.map +1 -1
- package/src/lib/template/template.service.d.ts +9 -0
- package/src/lib/template/template.service.js +31 -0
- package/src/lib/template/template.service.js.map +1 -1
- package/src/lib/template/workflow-definition.validator.js +54 -1
- package/src/lib/template/workflow-definition.validator.js.map +1 -1
- package/src/lib/workflow-engine/workflow-engine.enums.d.ts +3 -1
- package/src/lib/workflow-engine/workflow-engine.enums.js +2 -0
- package/src/lib/workflow-engine/workflow-engine.enums.js.map +1 -1
- package/src/lib/workflow-engine/workflow-engine.module.js +3 -0
- package/src/lib/workflow-engine/workflow-engine.module.js.map +1 -1
- package/src/lib/workflow-engine/workflow-engine.service.d.ts +5 -2
- package/src/lib/workflow-engine/workflow-engine.service.js +142 -26
- package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -1
- package/src/lib/workflow-webhook/index.d.ts +24 -0
- package/src/lib/workflow-webhook/index.js +28 -0
- package/src/lib/workflow-webhook/index.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-allowlist.d.ts +38 -0
- package/src/lib/workflow-webhook/workflow-webhook-allowlist.js +296 -0
- package/src/lib/workflow-webhook/workflow-webhook-allowlist.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-database-source.d.ts +33 -0
- package/src/lib/workflow-webhook/workflow-webhook-database-source.js +129 -0
- package/src/lib/workflow-webhook/workflow-webhook-database-source.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery-scheduler.service.d.ts +25 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery-scheduler.service.js +92 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery-scheduler.service.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.entity.d.ts +53 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.entity.js +97 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.entity.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.enums.d.ts +28 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.enums.js +36 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.enums.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.object.d.ts +28 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.object.js +88 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.object.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.resolver.d.ts +9 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.resolver.js +66 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.resolver.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.d.ts +116 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.js +705 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.subscriber.d.ts +23 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.subscriber.js +85 -0
- package/src/lib/workflow-webhook/workflow-webhook-delivery.subscriber.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.dto.d.ts +85 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.dto.js +345 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.dto.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.resolver.d.ts +23 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.resolver.js +159 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.resolver.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.service.d.ts +93 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.service.js +464 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint-admin.service.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint.entity.d.ts +52 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint.entity.js +135 -0
- package/src/lib/workflow-webhook/workflow-webhook-endpoint.entity.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.d.ts +46 -0
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.js +134 -0
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-options.d.ts +86 -0
- package/src/lib/workflow-webhook/workflow-webhook-options.js +88 -0
- package/src/lib/workflow-webhook/workflow-webhook-options.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-options.module.d.ts +18 -0
- package/src/lib/workflow-webhook/workflow-webhook-options.module.js +57 -0
- package/src/lib/workflow-webhook/workflow-webhook-options.module.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook-secret-cipher.d.ts +20 -0
- package/src/lib/workflow-webhook/workflow-webhook-secret-cipher.js +66 -0
- package/src/lib/workflow-webhook/workflow-webhook-secret-cipher.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.errors.d.ts +35 -0
- package/src/lib/workflow-webhook/workflow-webhook.errors.js +41 -0
- package/src/lib/workflow-webhook/workflow-webhook.errors.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.d.ts +9 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.js +62 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.provider.d.ts +12 -0
- package/src/lib/workflow-webhook/workflow-webhook.provider.js +21 -0
- package/src/lib/workflow-webhook/workflow-webhook.provider.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.queries.d.ts +38 -0
- package/src/lib/workflow-webhook/workflow-webhook.queries.js +142 -0
- package/src/lib/workflow-webhook/workflow-webhook.queries.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.service.d.ts +50 -0
- package/src/lib/workflow-webhook/workflow-webhook.service.js +221 -0
- package/src/lib/workflow-webhook/workflow-webhook.service.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.types.d.ts +127 -0
- package/src/lib/workflow-webhook/workflow-webhook.types.js +33 -0
- package/src/lib/workflow-webhook/workflow-webhook.types.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.validator.d.ts +20 -0
- package/src/lib/workflow-webhook/workflow-webhook.validator.js +97 -0
- package/src/lib/workflow-webhook/workflow-webhook.validator.js.map +1 -0
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.WorkflowEngineService = exports.MANUAL_TRANSFER_DELEGATION_REASON = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const form_1 = require("@rytass/bpm-core-shared/form");
|
|
6
|
+
const workflow_graph_1 = require("@rytass/bpm-core-shared/workflow-graph");
|
|
7
|
+
const workflow_webhook_delivery_service_1 = require("../workflow-webhook/workflow-webhook-delivery.service");
|
|
6
8
|
const common_1 = require("@nestjs/common");
|
|
7
9
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
8
10
|
const typeorm_2 = require("typeorm");
|
|
@@ -37,6 +39,7 @@ const workflow_condition_evaluator_1 = require("./workflow-condition-evaluator")
|
|
|
37
39
|
const workflow_dry_run_object_1 = require("./workflow-dry-run.object");
|
|
38
40
|
const workflow_service_task_dispatcher_token_1 = require("./workflow-service-task-dispatcher.token");
|
|
39
41
|
const workflow_token_entity_1 = require("./workflow-token.entity");
|
|
42
|
+
const bpm_root_options_1 = require("../bpm/bpm-root-options");
|
|
40
43
|
const MAX_PROCESSING_STEPS = 500;
|
|
41
44
|
const DEFAULT_APPROVAL_HISTORY_TASK_LIMIT = 50;
|
|
42
45
|
const SYSTEM_DRY_RUN_INSTANCE_ID = 'dry-run-instance';
|
|
@@ -52,7 +55,7 @@ const WORKFLOW_READ_ALL_PERMISSIONS = new Set([
|
|
|
52
55
|
'workflow.read_all',
|
|
53
56
|
]);
|
|
54
57
|
let WorkflowEngineService = class WorkflowEngineService {
|
|
55
|
-
constructor(approvalInstanceRepository, workflowTokenRepository, taskRepository, taskCandidateRepository, taskDecisionRepository, notificationRepository, activityLogRepository, adhocDirectiveRepository, approvalTemplateRepository, approvalTemplateVersionRepository, formDefinitionVersionRepository, attachmentService, conditionService, delegationService, notificationService, signatureService, slaScheduleService,
|
|
58
|
+
constructor(approvalInstanceRepository, workflowTokenRepository, taskRepository, taskCandidateRepository, taskDecisionRepository, notificationRepository, activityLogRepository, adhocDirectiveRepository, approvalTemplateRepository, approvalTemplateVersionRepository, formDefinitionVersionRepository, attachmentService, conditionService, delegationService, notificationService, signatureService, slaScheduleService, injectedServiceTaskDispatcher, formDataSourceValueResolver, rootOptions, webhookDeliveryService) {
|
|
56
59
|
this.approvalInstanceRepository = approvalInstanceRepository;
|
|
57
60
|
this.workflowTokenRepository = workflowTokenRepository;
|
|
58
61
|
this.taskRepository = taskRepository;
|
|
@@ -70,8 +73,22 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
70
73
|
this.notificationService = notificationService;
|
|
71
74
|
this.signatureService = signatureService;
|
|
72
75
|
this.slaScheduleService = slaScheduleService;
|
|
73
|
-
this.serviceTaskDispatcher = serviceTaskDispatcher;
|
|
74
76
|
this.formDataSourceValueResolver = formDataSourceValueResolver;
|
|
77
|
+
this.webhookDeliveryService = webhookDeliveryService;
|
|
78
|
+
// A binding under the token wins: that is either the host's own provider
|
|
79
|
+
// or `workflowServiceTaskDispatcherProvider`, both of them deliberate. The
|
|
80
|
+
// runtime instance from `BPMRootModule`'s async factory comes next.
|
|
81
|
+
//
|
|
82
|
+
// Resolving it here rather than registering a default provider is what
|
|
83
|
+
// keeps a host that binds the token from its own global module working —
|
|
84
|
+
// a module-local default would shadow that binding, silently sending
|
|
85
|
+
// WEBHOOK service tasks through the built-in `fetch` dispatcher instead of
|
|
86
|
+
// the host's signing or queueing one. It also reaches every copy of this
|
|
87
|
+
// service, which a provider registered on one module does not.
|
|
88
|
+
this.serviceTaskDispatcher =
|
|
89
|
+
injectedServiceTaskDispatcher ??
|
|
90
|
+
rootOptions?.workflowServiceTaskDispatcher ??
|
|
91
|
+
new workflow_service_task_dispatcher_token_1.DefaultWorkflowServiceTaskDispatcher();
|
|
75
92
|
}
|
|
76
93
|
async submitApprovalInstance(input, authContext) {
|
|
77
94
|
const formData = parseJsonObject(input.formDataJson, 'formDataJson');
|
|
@@ -472,11 +489,13 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
472
489
|
instanceId: instance.id,
|
|
473
490
|
manager,
|
|
474
491
|
});
|
|
475
|
-
|
|
476
|
-
|
|
492
|
+
// Same prototype-preserving reason as `cancelAdhocDirective`: this
|
|
493
|
+
// entity is returned to the resolver and `ApprovalInstance` exposes
|
|
494
|
+
// five getter-backed JSON fields.
|
|
495
|
+
const cancelledInstance = await instanceRepository.save(Object.assign(new approval_instance_entity_1.ApprovalInstanceEntity(), instance, {
|
|
477
496
|
completedAt: cancelledAt,
|
|
478
497
|
state: workflow_engine_enums_1.ApprovalInstanceStateEnum.CANCELLED,
|
|
479
|
-
});
|
|
498
|
+
}));
|
|
480
499
|
await this.dispatchAdhocCompletionNotifications(manager, cancelledInstance, workflow_engine_enums_1.ApprovalInstanceStateEnum.CANCELLED);
|
|
481
500
|
await this.cancelPendingAdhocDirectives(manager, instance.id, null);
|
|
482
501
|
await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
|
|
@@ -660,10 +679,14 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
660
679
|
if (directive.status !== adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING) {
|
|
661
680
|
throw new common_1.ConflictException(`Ad-hoc directive ${directive.id} is not pending`);
|
|
662
681
|
}
|
|
663
|
-
|
|
664
|
-
|
|
682
|
+
// Assigning onto a fresh entity keeps the prototype, so the row handed
|
|
683
|
+
// back to the resolver still carries the getters the GraphQL schema
|
|
684
|
+
// exposes (e.g. `targetValueJson`). Spreading it into a plain object
|
|
685
|
+
// drops them, and the mutation then fails with an opaque
|
|
686
|
+
// INTERNAL_SERVER_ERROR *after* the cancellation has been committed.
|
|
687
|
+
const cancelledDirective = await directiveRepository.save(Object.assign(new adhoc_directive_entity_1.AdhocDirectiveEntity(), directive, {
|
|
665
688
|
status: adhoc_enums_1.AdhocDirectiveStatusEnum.CANCELLED,
|
|
666
|
-
});
|
|
689
|
+
}));
|
|
667
690
|
await this.recordAdhocDirectiveActivity(manager, cancelledDirective, workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_DIRECTIVE_CANCELLED, cancelledByMemberId);
|
|
668
691
|
return cancelledDirective;
|
|
669
692
|
});
|
|
@@ -764,7 +787,18 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
764
787
|
: workflow_engine_enums_1.TaskAssignmentTypeEnum.CANDIDATE_GROUP,
|
|
765
788
|
completedAt: null,
|
|
766
789
|
createdAt: now,
|
|
767
|
-
|
|
790
|
+
// One resolved person is `SINGLE`, which is what this has always been.
|
|
791
|
+
// Several mean a countersign in the sense the UI promises — 下一層需
|
|
792
|
+
// 所有人都完成才會繼續 — so every candidate has to decide before the
|
|
793
|
+
// task closes. Until now a multi-person target produced a single
|
|
794
|
+
// `SINGLE` task, and whoever opened it first decided for the whole
|
|
795
|
+
// group while the task vanished from everybody else's inbox.
|
|
796
|
+
// Rejection is unaffected: `shouldCompleteTaskAfterDecision` closes
|
|
797
|
+
// the task on any non-approval whatever the policy, and that rejection
|
|
798
|
+
// then rejects the instance.
|
|
799
|
+
decisionPolicySnapshot: candidates.length === 1
|
|
800
|
+
? { type: 'SINGLE' }
|
|
801
|
+
: { type: 'PARALLEL_ALL' },
|
|
768
802
|
delegationChain: candidates.length === 1 ? primaryCandidate.delegationChain : [],
|
|
769
803
|
instanceId: instance.id,
|
|
770
804
|
isAdhoc: true,
|
|
@@ -1220,6 +1254,12 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
1220
1254
|
stepIndex: input.steps.length,
|
|
1221
1255
|
}),
|
|
1222
1256
|
];
|
|
1257
|
+
// A NOTIFY node is an asynchronous branch: at runtime it consumes its
|
|
1258
|
+
// token and never advances, so its branch ends here rather than failing
|
|
1259
|
+
// for want of an outgoing edge it is not allowed to have.
|
|
1260
|
+
if ((0, workflow_graph_1.isAsyncNotifyServiceTask)(node)) {
|
|
1261
|
+
return nextSteps;
|
|
1262
|
+
}
|
|
1223
1263
|
if (!entryConditionMet || node.type === 'endEvent') {
|
|
1224
1264
|
if (node.type === 'endEvent') {
|
|
1225
1265
|
return nextSteps;
|
|
@@ -1525,7 +1565,29 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
1525
1565
|
},
|
|
1526
1566
|
})
|
|
1527
1567
|
: [];
|
|
1528
|
-
|
|
1568
|
+
const openTasks = uniqueTasksById([...directTasks, ...candidateTasks]);
|
|
1569
|
+
// Deciding on a group task writes the decider into `assigneeMemberId`, so
|
|
1570
|
+
// a task that stays open for the others — a `PARALLEL_ALL` countersign —
|
|
1571
|
+
// keeps matching `directTasks` for somebody who has already voted. Their
|
|
1572
|
+
// own candidate row is the authority: once it is COMPLETED the task is no
|
|
1573
|
+
// longer theirs to act on, and opening it again only raises "was already
|
|
1574
|
+
// decided by this member".
|
|
1575
|
+
//
|
|
1576
|
+
// Scoped to the tasks already in hand: unscoped it would load every row
|
|
1577
|
+
// this member has ever settled, on every inbox load. TRANSFERRED is not
|
|
1578
|
+
// among the statuses because transferring sets the task itself to
|
|
1579
|
+
// TRANSFERRED, which both queries above already exclude.
|
|
1580
|
+
const settledCandidateRows = openTasks.length
|
|
1581
|
+
? await this.taskCandidateRepository.find({
|
|
1582
|
+
where: {
|
|
1583
|
+
memberId: assigneeMemberId,
|
|
1584
|
+
status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
|
|
1585
|
+
taskId: (0, typeorm_2.In)(openTasks.map((task) => task.id)),
|
|
1586
|
+
},
|
|
1587
|
+
})
|
|
1588
|
+
: [];
|
|
1589
|
+
const settledTaskIds = new Set(settledCandidateRows.map((candidate) => candidate.taskId));
|
|
1590
|
+
return this.attachTaskCandidateSummaries(openTasks.filter((task) => !settledTaskIds.has(task.id)));
|
|
1529
1591
|
}
|
|
1530
1592
|
async listApprovalHistoryTasks(assigneeMemberId) {
|
|
1531
1593
|
const directTasks = await this.taskRepository.find({
|
|
@@ -1539,13 +1601,17 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
1539
1601
|
status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
|
|
1540
1602
|
},
|
|
1541
1603
|
});
|
|
1604
|
+
// No status filter on this branch: the candidate row is already COMPLETED,
|
|
1605
|
+
// which means this member has cast their decision. Requiring the task
|
|
1606
|
+
// itself to be COMPLETED hid their own vote for as long as a multi-person
|
|
1607
|
+
// countersign waited on somebody else — and the inbox has, correctly,
|
|
1608
|
+
// stopped showing it to them by then, so it appeared nowhere at all.
|
|
1542
1609
|
const candidateTasks = candidateRows.length
|
|
1543
1610
|
? await this.taskRepository.find({
|
|
1544
1611
|
order: { completedAt: 'DESC', createdAt: 'DESC' },
|
|
1545
1612
|
take: DEFAULT_APPROVAL_HISTORY_TASK_LIMIT,
|
|
1546
1613
|
where: {
|
|
1547
1614
|
id: (0, typeorm_2.In)(candidateRows.map((candidate) => candidate.taskId)),
|
|
1548
|
-
status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED,
|
|
1549
1615
|
},
|
|
1550
1616
|
})
|
|
1551
1617
|
: [];
|
|
@@ -1709,25 +1775,39 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
1709
1775
|
if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
|
|
1710
1776
|
return;
|
|
1711
1777
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1778
|
+
// Both open statuses, not just WAITING. Today this is reached only from
|
|
1779
|
+
// `processRunningInstance`, whose loop has already established that no
|
|
1780
|
+
// ACTIVE token remains, so the extra status changes no current behaviour —
|
|
1781
|
+
// but nothing enforces that invariant, and a second caller arriving
|
|
1782
|
+
// without it would complete a case while somebody still had a task open.
|
|
1783
|
+
const openTokens = await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).find({
|
|
1715
1784
|
where: {
|
|
1716
1785
|
instanceId: instance.id,
|
|
1717
|
-
status:
|
|
1786
|
+
status: (0, typeorm_2.In)([
|
|
1787
|
+
workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
|
|
1788
|
+
workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
|
|
1789
|
+
]),
|
|
1718
1790
|
},
|
|
1719
1791
|
});
|
|
1720
|
-
if (
|
|
1792
|
+
if (openTokens.length > 0) {
|
|
1721
1793
|
return;
|
|
1722
1794
|
}
|
|
1723
1795
|
const completedAt = new Date();
|
|
1796
|
+
// Update the tracked entity in place instead of saving a copy of it. The
|
|
1797
|
+
// caller still holds this object and may return it — `submitApprovalInstance`
|
|
1798
|
+
// hands it straight back to the resolver — so writing a copy left the
|
|
1799
|
+
// mutation reporting `RUNNING` with a null `completedAt` on a case the same
|
|
1800
|
+
// transaction had already committed as `APPROVED`. Assigning in place also
|
|
1801
|
+
// preserves whatever the caller handed in, so an entity stays an entity
|
|
1802
|
+
// and its getter-backed `*Json` fields survive. It cannot upgrade a plain
|
|
1803
|
+
// object, and the resubmit and return paths do pass a plain copy of their
|
|
1804
|
+
// own — a separate problem this does not fix.
|
|
1724
1805
|
const completedInstance = await manager
|
|
1725
1806
|
.getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
|
|
1726
|
-
.save({
|
|
1727
|
-
...instance,
|
|
1807
|
+
.save(Object.assign(instance, {
|
|
1728
1808
|
completedAt,
|
|
1729
1809
|
state: workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED,
|
|
1730
|
-
});
|
|
1810
|
+
}));
|
|
1731
1811
|
await this.notificationService.createInstanceCompletedNotification({
|
|
1732
1812
|
instance: completedInstance,
|
|
1733
1813
|
manager,
|
|
@@ -1957,11 +2037,14 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
1957
2037
|
(candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
|
|
1958
2038
|
candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING));
|
|
1959
2039
|
if (!hasOpenToken) {
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2040
|
+
// Update the tracked entity in place. Saving a copy left the caller's
|
|
2041
|
+
// `instance.state` on `RUNNING`, so the `state !== RUNNING` guard in
|
|
2042
|
+
// `completeInstanceIfNoOpenRuntimeState` did not short-circuit and a
|
|
2043
|
+
// *second* completion ran on the way out of `processRunningInstance`:
|
|
2044
|
+
// a duplicate completion notification on every approved case, and — far
|
|
2045
|
+
// worse — an `endState: 'REJECTED'` row overwritten with `APPROVED`
|
|
2046
|
+
// immediately after it had been committed.
|
|
2047
|
+
const completedInstance = await instanceRepository.save(Object.assign(instance, { completedAt, state: instanceState }));
|
|
1965
2048
|
if (instanceState === workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED) {
|
|
1966
2049
|
await this.notificationService.createInstanceCompletedNotification({
|
|
1967
2050
|
instance: completedInstance,
|
|
@@ -2533,7 +2616,13 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2533
2616
|
await this.advanceTokenToOutgoingNodes(manager, updatedInstance, token, node);
|
|
2534
2617
|
return;
|
|
2535
2618
|
}
|
|
2536
|
-
|
|
2619
|
+
// A webhook-only NOTIFY node names nobody. Resolving its empty DIRECT
|
|
2620
|
+
// resolver would throw and roll back the submit or decision that reached
|
|
2621
|
+
// it, so skip member resolution; webhooks are delivered separately.
|
|
2622
|
+
const recipients = (0, workflow_graph_1.isNotifyRecipientsEmpty)(action.recipients) &&
|
|
2623
|
+
(0, workflow_graph_1.readNotifyWebhookTargets)(action).length > 0
|
|
2624
|
+
? []
|
|
2625
|
+
: await this.resolveApproverResolver(manager, instance, action.recipients, `知會節點「${node.data.label}」`);
|
|
2537
2626
|
const recipientMemberIds = uniqueTexts(recipients.map((recipient) => recipient.memberId));
|
|
2538
2627
|
await this.notificationService.createServiceTaskNotifications({
|
|
2539
2628
|
instance,
|
|
@@ -2541,6 +2630,26 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2541
2630
|
node,
|
|
2542
2631
|
recipientMemberIds,
|
|
2543
2632
|
});
|
|
2633
|
+
// Queued in this transaction and sent only after it commits (ADR 18
|
|
2634
|
+
// §3.5). Without the webhook module there is nothing to queue into; the
|
|
2635
|
+
// publish lint already refuses a template that references an endpoint
|
|
2636
|
+
// in that case.
|
|
2637
|
+
const webhookDeliveryIds = this.webhookDeliveryService &&
|
|
2638
|
+
(0, workflow_graph_1.readNotifyWebhookTargets)(action).length > 0
|
|
2639
|
+
? await this.webhookDeliveryService.enqueueNotifyWebhooks(manager, {
|
|
2640
|
+
instance: {
|
|
2641
|
+
formData: instance.formData,
|
|
2642
|
+
id: instance.id,
|
|
2643
|
+
initiatorMemberId: instance.initiatorMemberId,
|
|
2644
|
+
templateId: instance.templateId,
|
|
2645
|
+
templateVersionId: instance.templateVersionId,
|
|
2646
|
+
title: instance.title,
|
|
2647
|
+
},
|
|
2648
|
+
node: { id: node.id, label: node.data.label },
|
|
2649
|
+
occurredAt: new Date(),
|
|
2650
|
+
tokenId: token.id,
|
|
2651
|
+
}, action.webhooks)
|
|
2652
|
+
: [];
|
|
2544
2653
|
await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).save({
|
|
2545
2654
|
...token,
|
|
2546
2655
|
consumedAt: new Date(),
|
|
@@ -2555,6 +2664,7 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2555
2664
|
action: 'NOTIFY',
|
|
2556
2665
|
recipientMemberIds,
|
|
2557
2666
|
tokenId: token.id,
|
|
2667
|
+
...(webhookDeliveryIds.length ? { webhookDeliveryIds } : {}),
|
|
2558
2668
|
},
|
|
2559
2669
|
taskId: null,
|
|
2560
2670
|
}));
|
|
@@ -2844,6 +2954,9 @@ exports.WorkflowEngineService = WorkflowEngineService = tslib_1.__decorate([
|
|
|
2844
2954
|
tslib_1.__param(17, (0, common_1.Inject)(workflow_service_task_dispatcher_token_1.BPM_WORKFLOW_SERVICE_TASK_DISPATCHER)),
|
|
2845
2955
|
tslib_1.__param(18, (0, common_1.Optional)()),
|
|
2846
2956
|
tslib_1.__param(18, (0, common_1.Inject)(form_data_source_types_1.BPM_FORM_DATA_SOURCE_VALUE_RESOLVER)),
|
|
2957
|
+
tslib_1.__param(19, (0, common_1.Optional)()),
|
|
2958
|
+
tslib_1.__param(19, (0, common_1.Inject)(bpm_root_options_1.BPM_ROOT_OPTIONS)),
|
|
2959
|
+
tslib_1.__param(20, (0, common_1.Optional)()),
|
|
2847
2960
|
tslib_1.__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
2848
2961
|
typeorm_2.Repository,
|
|
2849
2962
|
typeorm_2.Repository,
|
|
@@ -2860,7 +2973,7 @@ exports.WorkflowEngineService = WorkflowEngineService = tslib_1.__decorate([
|
|
|
2860
2973
|
delegation_service_1.DelegationService,
|
|
2861
2974
|
notification_service_1.NotificationService,
|
|
2862
2975
|
signature_service_1.SignatureService,
|
|
2863
|
-
sla_schedule_service_1.BPMSlaScheduleService, Object, Object])
|
|
2976
|
+
sla_schedule_service_1.BPMSlaScheduleService, Object, Object, Object, workflow_webhook_delivery_service_1.WorkflowWebhookDeliveryService])
|
|
2864
2977
|
], WorkflowEngineService);
|
|
2865
2978
|
function parseJsonObject(value, label) {
|
|
2866
2979
|
try {
|
|
@@ -3936,6 +4049,9 @@ function readDryRunStepMessage(node, entryConditionMet) {
|
|
|
3936
4049
|
if (node.type === 'endEvent') {
|
|
3937
4050
|
return `流程結束:${node.data.endState ?? 'APPROVED'}`;
|
|
3938
4051
|
}
|
|
4052
|
+
if ((0, workflow_graph_1.isAsyncNotifyServiceTask)(node)) {
|
|
4053
|
+
return '將發送知會,此分支不會繼續往下。';
|
|
4054
|
+
}
|
|
3939
4055
|
return '節點條件通過。';
|
|
3940
4056
|
}
|
|
3941
4057
|
function mapDecisionToResolution(action) {
|