@rytass/bpm-core-nestjs-module 0.13.3 → 0.13.5
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 +56 -0
- package/package.json +14 -7
- 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 +150 -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,
|
|
@@ -2148,12 +2231,20 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2148
2231
|
status: workflow_engine_enums_1.TaskStatusEnum.TRANSFERRED,
|
|
2149
2232
|
});
|
|
2150
2233
|
const nextTask = await taskRepository.save(taskRepository.create({
|
|
2234
|
+
// A transferred ad-hoc task stays the same ad-hoc task for its new
|
|
2235
|
+
// holder: the pre-approval rejection path (RETURN_TO_ORIGIN) reads these
|
|
2236
|
+
// off the task being decided, so without them a rejection after a
|
|
2237
|
+
// transfer rejected the whole instance instead.
|
|
2238
|
+
adhocDirectiveId: task.adhocDirectiveId,
|
|
2239
|
+
adhocOriginTaskId: task.adhocOriginTaskId,
|
|
2240
|
+
adhocType: task.adhocType,
|
|
2151
2241
|
assigneeMemberId: transferToMemberId,
|
|
2152
2242
|
assignmentType: workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER,
|
|
2153
2243
|
completedAt: null,
|
|
2154
2244
|
decisionPolicySnapshot: { type: 'SINGLE' },
|
|
2155
2245
|
delegationChain: nextDelegationChain,
|
|
2156
2246
|
instanceId: task.instanceId,
|
|
2247
|
+
isAdhoc: task.isAdhoc,
|
|
2157
2248
|
nodeId: task.nodeId,
|
|
2158
2249
|
openedAt: null,
|
|
2159
2250
|
originalAssigneeMemberId: task.originalAssigneeMemberId,
|
|
@@ -2533,7 +2624,13 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2533
2624
|
await this.advanceTokenToOutgoingNodes(manager, updatedInstance, token, node);
|
|
2534
2625
|
return;
|
|
2535
2626
|
}
|
|
2536
|
-
|
|
2627
|
+
// A webhook-only NOTIFY node names nobody. Resolving its empty DIRECT
|
|
2628
|
+
// resolver would throw and roll back the submit or decision that reached
|
|
2629
|
+
// it, so skip member resolution; webhooks are delivered separately.
|
|
2630
|
+
const recipients = (0, workflow_graph_1.isNotifyRecipientsEmpty)(action.recipients) &&
|
|
2631
|
+
(0, workflow_graph_1.readNotifyWebhookTargets)(action).length > 0
|
|
2632
|
+
? []
|
|
2633
|
+
: await this.resolveApproverResolver(manager, instance, action.recipients, `知會節點「${node.data.label}」`);
|
|
2537
2634
|
const recipientMemberIds = uniqueTexts(recipients.map((recipient) => recipient.memberId));
|
|
2538
2635
|
await this.notificationService.createServiceTaskNotifications({
|
|
2539
2636
|
instance,
|
|
@@ -2541,6 +2638,26 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2541
2638
|
node,
|
|
2542
2639
|
recipientMemberIds,
|
|
2543
2640
|
});
|
|
2641
|
+
// Queued in this transaction and sent only after it commits (ADR 18
|
|
2642
|
+
// §3.5). Without the webhook module there is nothing to queue into; the
|
|
2643
|
+
// publish lint already refuses a template that references an endpoint
|
|
2644
|
+
// in that case.
|
|
2645
|
+
const webhookDeliveryIds = this.webhookDeliveryService &&
|
|
2646
|
+
(0, workflow_graph_1.readNotifyWebhookTargets)(action).length > 0
|
|
2647
|
+
? await this.webhookDeliveryService.enqueueNotifyWebhooks(manager, {
|
|
2648
|
+
instance: {
|
|
2649
|
+
formData: instance.formData,
|
|
2650
|
+
id: instance.id,
|
|
2651
|
+
initiatorMemberId: instance.initiatorMemberId,
|
|
2652
|
+
templateId: instance.templateId,
|
|
2653
|
+
templateVersionId: instance.templateVersionId,
|
|
2654
|
+
title: instance.title,
|
|
2655
|
+
},
|
|
2656
|
+
node: { id: node.id, label: node.data.label },
|
|
2657
|
+
occurredAt: new Date(),
|
|
2658
|
+
tokenId: token.id,
|
|
2659
|
+
}, action.webhooks)
|
|
2660
|
+
: [];
|
|
2544
2661
|
await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).save({
|
|
2545
2662
|
...token,
|
|
2546
2663
|
consumedAt: new Date(),
|
|
@@ -2555,6 +2672,7 @@ let WorkflowEngineService = class WorkflowEngineService {
|
|
|
2555
2672
|
action: 'NOTIFY',
|
|
2556
2673
|
recipientMemberIds,
|
|
2557
2674
|
tokenId: token.id,
|
|
2675
|
+
...(webhookDeliveryIds.length ? { webhookDeliveryIds } : {}),
|
|
2558
2676
|
},
|
|
2559
2677
|
taskId: null,
|
|
2560
2678
|
}));
|
|
@@ -2844,6 +2962,9 @@ exports.WorkflowEngineService = WorkflowEngineService = tslib_1.__decorate([
|
|
|
2844
2962
|
tslib_1.__param(17, (0, common_1.Inject)(workflow_service_task_dispatcher_token_1.BPM_WORKFLOW_SERVICE_TASK_DISPATCHER)),
|
|
2845
2963
|
tslib_1.__param(18, (0, common_1.Optional)()),
|
|
2846
2964
|
tslib_1.__param(18, (0, common_1.Inject)(form_data_source_types_1.BPM_FORM_DATA_SOURCE_VALUE_RESOLVER)),
|
|
2965
|
+
tslib_1.__param(19, (0, common_1.Optional)()),
|
|
2966
|
+
tslib_1.__param(19, (0, common_1.Inject)(bpm_root_options_1.BPM_ROOT_OPTIONS)),
|
|
2967
|
+
tslib_1.__param(20, (0, common_1.Optional)()),
|
|
2847
2968
|
tslib_1.__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
2848
2969
|
typeorm_2.Repository,
|
|
2849
2970
|
typeorm_2.Repository,
|
|
@@ -2860,7 +2981,7 @@ exports.WorkflowEngineService = WorkflowEngineService = tslib_1.__decorate([
|
|
|
2860
2981
|
delegation_service_1.DelegationService,
|
|
2861
2982
|
notification_service_1.NotificationService,
|
|
2862
2983
|
signature_service_1.SignatureService,
|
|
2863
|
-
sla_schedule_service_1.BPMSlaScheduleService, Object, Object])
|
|
2984
|
+
sla_schedule_service_1.BPMSlaScheduleService, Object, Object, Object, workflow_webhook_delivery_service_1.WorkflowWebhookDeliveryService])
|
|
2864
2985
|
], WorkflowEngineService);
|
|
2865
2986
|
function parseJsonObject(value, label) {
|
|
2866
2987
|
try {
|
|
@@ -3936,6 +4057,9 @@ function readDryRunStepMessage(node, entryConditionMet) {
|
|
|
3936
4057
|
if (node.type === 'endEvent') {
|
|
3937
4058
|
return `流程結束:${node.data.endState ?? 'APPROVED'}`;
|
|
3938
4059
|
}
|
|
4060
|
+
if ((0, workflow_graph_1.isAsyncNotifyServiceTask)(node)) {
|
|
4061
|
+
return '將發送知會,此分支不會繼續往下。';
|
|
4062
|
+
}
|
|
3939
4063
|
return '節點條件通過。';
|
|
3940
4064
|
}
|
|
3941
4065
|
function mapDecisionToResolution(action) {
|