@rytass/bpm-core-nestjs-module 0.18.1 → 0.20.0
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 +52 -0
- package/package.json +3 -2
- 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.service.d.ts +23 -1
- package/src/lib/attachment/attachment.service.js +111 -24
- package/src/lib/attachment/attachment.service.js.map +1 -1
- package/src/lib/bpm/bpm-root-options.d.ts +13 -0
- package/src/lib/bpm/bpm-root-options.js.map +1 -1
- package/src/lib/bpm/bpm-root.module.d.ts +12 -0
- package/src/lib/bpm/bpm-root.module.js +5 -0
- package/src/lib/bpm/bpm-root.module.js.map +1 -1
- package/src/lib/condition/adapters/cel-legacy.adapter.d.ts +62 -0
- package/src/lib/condition/adapters/cel-legacy.adapter.js +141 -0
- package/src/lib/condition/adapters/cel-legacy.adapter.js.map +1 -0
- package/src/lib/condition/adapters/cel-v2.adapter.d.ts +76 -0
- package/src/lib/condition/adapters/cel-v2.adapter.js +371 -0
- package/src/lib/condition/adapters/cel-v2.adapter.js.map +1 -0
- package/src/lib/condition/condition.service.d.ts +90 -5
- package/src/lib/condition/condition.service.js +287 -71
- package/src/lib/condition/condition.service.js.map +1 -1
- package/src/lib/condition/expression-ast.d.ts +176 -0
- package/src/lib/condition/expression-ast.js +1254 -0
- package/src/lib/condition/expression-ast.js.map +1 -0
- package/src/lib/condition/expression-budget.d.ts +44 -0
- package/src/lib/condition/expression-budget.js +61 -0
- package/src/lib/condition/expression-budget.js.map +1 -0
- package/src/lib/condition/expression-dialect.d.ts +29 -0
- package/src/lib/condition/expression-dialect.js +44 -0
- package/src/lib/condition/expression-dialect.js.map +1 -0
- package/src/lib/condition/expression-differential.corpus.d.ts +34 -0
- package/src/lib/condition/expression-differential.corpus.js +995 -0
- package/src/lib/condition/expression-differential.corpus.js.map +1 -0
- package/src/lib/condition/expression-differential.d.ts +33 -0
- package/src/lib/condition/expression-differential.js +48 -0
- package/src/lib/condition/expression-differential.js.map +1 -0
- package/src/lib/condition/expression-engine-failure.d.ts +15 -0
- package/src/lib/condition/expression-engine-failure.js +22 -0
- package/src/lib/condition/expression-engine-failure.js.map +1 -0
- package/src/lib/condition/expression-errors.d.ts +40 -0
- package/src/lib/condition/expression-errors.js +105 -0
- package/src/lib/condition/expression-errors.js.map +1 -0
- package/src/lib/condition/expression-function-invocation.d.ts +50 -0
- package/src/lib/condition/expression-function-invocation.js +290 -0
- package/src/lib/condition/expression-function-invocation.js.map +1 -0
- package/src/lib/condition/expression-result.walker.d.ts +18 -0
- package/src/lib/condition/expression-result.walker.js +152 -0
- package/src/lib/condition/expression-result.walker.js.map +1 -0
- package/src/lib/condition/expression-runtime.d.ts +70 -0
- package/src/lib/condition/expression-runtime.js +134 -0
- package/src/lib/condition/expression-runtime.js.map +1 -0
- package/src/lib/condition/index.d.ts +4 -0
- package/src/lib/condition/index.js +8 -0
- package/src/lib/condition/index.js.map +1 -1
- package/src/lib/delegation/delegation-rule.entity.d.ts +7 -0
- package/src/lib/delegation/delegation-rule.entity.js +10 -0
- package/src/lib/delegation/delegation-rule.entity.js.map +1 -1
- package/src/lib/delegation/delegation.service.d.ts +7 -1
- package/src/lib/delegation/delegation.service.js +22 -5
- package/src/lib/delegation/delegation.service.js.map +1 -1
- package/src/lib/delegation/dto/delegation-rule.input.d.ts +2 -0
- package/src/lib/delegation/dto/delegation-rule.input.js +10 -0
- package/src/lib/delegation/dto/delegation-rule.input.js.map +1 -1
- package/src/lib/expression-function/expression-function.built-ins.d.ts +9 -0
- package/src/lib/expression-function/expression-function.built-ins.js +117 -0
- package/src/lib/expression-function/expression-function.built-ins.js.map +1 -0
- package/src/lib/expression-function/expression-function.module.d.ts +19 -0
- package/src/lib/expression-function/expression-function.module.js +34 -0
- package/src/lib/expression-function/expression-function.module.js.map +1 -0
- package/src/lib/expression-function/expression-function.provider.d.ts +17 -0
- package/src/lib/expression-function/expression-function.provider.js +39 -0
- package/src/lib/expression-function/expression-function.provider.js.map +1 -0
- package/src/lib/expression-function/expression-function.registry.d.ts +36 -0
- package/src/lib/expression-function/expression-function.registry.js +231 -0
- package/src/lib/expression-function/expression-function.registry.js.map +1 -0
- package/src/lib/expression-function/expression-function.types.d.ts +30 -0
- package/src/lib/expression-function/expression-function.types.js +8 -0
- package/src/lib/expression-function/expression-function.types.js.map +1 -0
- package/src/lib/expression-function/index.d.ts +5 -0
- package/src/lib/expression-function/index.js +9 -0
- package/src/lib/expression-function/index.js.map +1 -0
- package/src/lib/form/form-schema.validator.js +53 -6
- package/src/lib/form/form-schema.validator.js.map +1 -1
- package/src/lib/form-data-source/form-data-source-value-resolver.service.d.ts +12 -1
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js +45 -26
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.expression.d.ts +95 -0
- package/src/lib/form-data-source/form-data-source.expression.js +267 -0
- package/src/lib/form-data-source/form-data-source.expression.js.map +1 -0
- package/src/lib/form-data-source/form-data-source.module.js +4 -0
- package/src/lib/form-data-source/form-data-source.module.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.service.d.ts +22 -3
- package/src/lib/form-data-source/form-data-source.service.js +78 -18
- package/src/lib/form-data-source/form-data-source.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.validation.d.ts +47 -4
- package/src/lib/form-data-source/form-data-source.validation.js +151 -23
- package/src/lib/form-data-source/form-data-source.validation.js.map +1 -1
- package/src/lib/migrations/0000000030000-delegation-scope-condition-dialect.d.ts +22 -0
- package/src/lib/migrations/0000000030000-delegation-scope-condition-dialect.js +76 -0
- package/src/lib/migrations/0000000030000-delegation-scope-condition-dialect.js.map +1 -0
- package/src/lib/migrations/index.d.ts +1 -0
- package/src/lib/migrations/index.js +3 -0
- package/src/lib/migrations/index.js.map +1 -1
- package/src/lib/notification/notification.service.d.ts +1 -1
- package/src/lib/notification/notification.service.js +7 -3
- package/src/lib/notification/notification.service.js.map +1 -1
- package/src/lib/template/dto/expression-designer.dto.d.ts +107 -0
- package/src/lib/template/dto/expression-designer.dto.js +415 -0
- package/src/lib/template/dto/expression-designer.dto.js.map +1 -0
- package/src/lib/template/expression-designer.queries.d.ts +16 -0
- package/src/lib/template/expression-designer.queries.js +66 -0
- package/src/lib/template/expression-designer.queries.js.map +1 -0
- package/src/lib/template/expression-designer.service.d.ts +72 -0
- package/src/lib/template/expression-designer.service.js +543 -0
- package/src/lib/template/expression-designer.service.js.map +1 -0
- package/src/lib/template/index.d.ts +2 -0
- package/src/lib/template/index.js +2 -0
- package/src/lib/template/index.js.map +1 -1
- package/src/lib/template/template.module.js +4 -0
- package/src/lib/template/template.module.js.map +1 -1
- package/src/lib/template/template.service.d.ts +37 -1
- package/src/lib/template/template.service.js +317 -29
- package/src/lib/template/template.service.js.map +1 -1
- package/src/lib/template/workflow-definition.validator.d.ts +7 -0
- package/src/lib/template/workflow-definition.validator.js +26 -2
- package/src/lib/template/workflow-definition.validator.js.map +1 -1
- package/src/lib/workflow-engine/time-budget.d.ts +13 -0
- package/src/lib/workflow-engine/time-budget.js +58 -0
- package/src/lib/workflow-engine/time-budget.js.map +1 -0
- package/src/lib/workflow-engine/workflow-engine.service.d.ts +24 -0
- package/src/lib/workflow-engine/workflow-engine.service.js +249 -65
- package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -1
- package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js +4 -0
- package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.d.ts +3 -1
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.js +11 -2
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.d.ts +8 -1
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.js +70 -27
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook-expression.d.ts +57 -0
- package/src/lib/workflow-webhook/workflow-webhook-expression.js +123 -0
- package/src/lib/workflow-webhook/workflow-webhook-expression.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.js +4 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook.validator.d.ts +7 -0
- package/src/lib/workflow-webhook/workflow-webhook.validator.js +29 -6
- package/src/lib/workflow-webhook/workflow-webhook.validator.js.map +1 -1
|
@@ -48,9 +48,17 @@ const task_entity_1 = require("./task.entity");
|
|
|
48
48
|
const workflow_engine_enums_1 = require("./workflow-engine.enums");
|
|
49
49
|
const workflow_condition_evaluator_1 = require("./workflow-condition-evaluator");
|
|
50
50
|
const workflow_dry_run_object_1 = require("./workflow-dry-run.object");
|
|
51
|
+
const time_budget_1 = require("./time-budget");
|
|
52
|
+
const expression_budget_1 = require("../condition/expression-budget");
|
|
53
|
+
const expression_errors_1 = require("../condition/expression-errors");
|
|
54
|
+
const expression_dialect_1 = require("../condition/expression-dialect");
|
|
51
55
|
const workflow_service_task_dispatcher_token_1 = require("./workflow-service-task-dispatcher.token");
|
|
52
56
|
const workflow_token_entity_1 = require("./workflow-token.entity");
|
|
53
57
|
const bpm_root_options_1 = require("../bpm/bpm-root-options");
|
|
58
|
+
const MAX_DRY_RUN_INPUT_LENGTH = 1_048_576;
|
|
59
|
+
const MAX_DRY_RUN_VISITS = 10_000;
|
|
60
|
+
const DRY_RUN_TIME_BUDGET_MS = 2_000;
|
|
61
|
+
const DRY_RUN_TOO_COMPLEX_MESSAGE = '流程或條件的運算量過大,無法完成試跑,請減少連續的閘道分支或簡化條件';
|
|
54
62
|
const MAX_PROCESSING_STEPS = 500;
|
|
55
63
|
exports.MAX_APPROVAL_INSTANCE_BUSINESS_KEY_LENGTH = 200;
|
|
56
64
|
const APPROVAL_INSTANCE_BUSINESS_KEY_INDEX = 'approval_instances_business_key_key';
|
|
@@ -58,6 +66,40 @@ const APPROVAL_INSTANCE_BUSINESS_KEY_INDEX = 'approval_instances_business_key_ke
|
|
|
58
66
|
const ORG_MANAGER_CHAIN_MAX_HOPS = 20;
|
|
59
67
|
const DEFAULT_APPROVAL_HISTORY_TASK_LIMIT = 50;
|
|
60
68
|
const SYSTEM_DRY_RUN_INSTANCE_ID = 'dry-run-instance';
|
|
69
|
+
// The dry run checks each expression against the roots its use point may read,
|
|
70
|
+
// as publish does. Keep these in step with `WORKFLOW_EXPRESSION_ROOT_IDENTIFIERS`
|
|
71
|
+
// in template.service.ts; P2 moves both to one place.
|
|
72
|
+
const DRY_RUN_WORKFLOW_ROOT_IDENTIFIERS = [
|
|
73
|
+
'env',
|
|
74
|
+
'form',
|
|
75
|
+
'formData',
|
|
76
|
+
'initiator',
|
|
77
|
+
'instance',
|
|
78
|
+
];
|
|
79
|
+
const DRY_RUN_EDGE_ROOT_IDENTIFIERS = [
|
|
80
|
+
...DRY_RUN_WORKFLOW_ROOT_IDENTIFIERS,
|
|
81
|
+
'lastDecision',
|
|
82
|
+
];
|
|
83
|
+
const hasExpressionText = (expression) => typeof expression === 'string' && expression.trim() !== '';
|
|
84
|
+
/**
|
|
85
|
+
* Whether a definition's dialect can be read: missing (`cel-legacy`) or one
|
|
86
|
+
* this version knows. Never throws, unlike `readWorkflowExpressionDialect`.
|
|
87
|
+
*/
|
|
88
|
+
const isReadableExpressionDialect = (definition) => {
|
|
89
|
+
const dialect = definition?.expressionDialect;
|
|
90
|
+
return (dialect === undefined || dialect === null || (0, expression_dialect_1.isExpressionDialect)(dialect));
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Options for evaluating an expression that may be blank. The dialect is read
|
|
94
|
+
* only when there is an expression: an unknown dialect is refused because the
|
|
95
|
+
* expression cannot be evaluated as its author tested it, and a blank one is
|
|
96
|
+
* never evaluated at all. Publishing does not check the field yet (Phase 23
|
|
97
|
+
* P2), so reading it eagerly would turn a stray field on a version with no
|
|
98
|
+
* expressions into an error, where before dialects existed it was ignored.
|
|
99
|
+
*/
|
|
100
|
+
const readExpressionDialectOptions = (expression, definition) => hasExpressionText(expression)
|
|
101
|
+
? { dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(definition) }
|
|
102
|
+
: {};
|
|
61
103
|
/**
|
|
62
104
|
* Delegation-chain reason recorded for an approver-initiated transfer, as
|
|
63
105
|
* opposed to {@link SLA_ESCALATION_DELEGATION_REASON}.
|
|
@@ -152,6 +194,8 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
152
194
|
this.formDataSourceValueResolver = formDataSourceValueResolver;
|
|
153
195
|
this.webhookDeliveryService = webhookDeliveryService;
|
|
154
196
|
this.logger = new common_1.Logger(WorkflowEngineService_1.name);
|
|
197
|
+
/** Webhooks each open engine transaction will send once it has committed. */
|
|
198
|
+
this.deferredWebhookDispatches = new WeakMap();
|
|
155
199
|
// A binding under the token wins: that is either the host's own provider
|
|
156
200
|
// or `workflowServiceTaskDispatcherProvider`, both of them deliberate. The
|
|
157
201
|
// runtime instance from `BPMRootModule`'s async factory comes next.
|
|
@@ -177,6 +221,59 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
177
221
|
readOrganizationToday() {
|
|
178
222
|
return (0, organization_time_zone_1.formatDateOnlyInTimeZone)(new Date(), this.organizationTimeZone);
|
|
179
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Opens an engine transaction whose webhooks wait for its commit. A webhook
|
|
226
|
+
* sent from inside would hold the case lock and a pooled connection for as
|
|
227
|
+
* long as the far end takes to answer, so a slow endpoint could stall the
|
|
228
|
+
* case and then, a connection at a time, the whole API. A transaction that
|
|
229
|
+
* rolls back sends nothing.
|
|
230
|
+
*
|
|
231
|
+
* The caller still waits for the sends, so the outcome is in the activity log
|
|
232
|
+
* by the time a mutation returns, as it always has been.
|
|
233
|
+
*/
|
|
234
|
+
async runEngineTransaction(work) {
|
|
235
|
+
// Mutable on purpose: every step of the transaction appends to it, and
|
|
236
|
+
// threading it through each of them would change a dozen signatures.
|
|
237
|
+
const dispatches = [];
|
|
238
|
+
// One transaction is one operation: its evaluations share one budget.
|
|
239
|
+
const result = await (0, expression_budget_1.runExpressionOperation)(() => this.approvalInstanceRepository.manager.transaction((manager) => {
|
|
240
|
+
this.deferredWebhookDispatches.set(manager, dispatches);
|
|
241
|
+
return work(manager);
|
|
242
|
+
}));
|
|
243
|
+
await dispatches.reduce(async (previous, dispatch) => {
|
|
244
|
+
await previous;
|
|
245
|
+
await this.sendCommittedWebhook(dispatch);
|
|
246
|
+
}, Promise.resolve());
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Queues a webhook for after the commit. A manager this service did not open
|
|
251
|
+
* has no queue and no commit it can see, so that webhook goes out on the spot
|
|
252
|
+
* rather than being dropped.
|
|
253
|
+
*/
|
|
254
|
+
async dispatchWebhookAfterCommit(manager, dispatch) {
|
|
255
|
+
const queue = this.deferredWebhookDispatches.get(manager);
|
|
256
|
+
if (queue) {
|
|
257
|
+
queue.push(dispatch);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
|
|
261
|
+
const result = await dispatch.send();
|
|
262
|
+
await activityRepository.save(activityRepository.create(dispatch.readActivityLog(result)));
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The transaction has committed and the mutation has succeeded, so nothing
|
|
266
|
+
* here may fail it: a result that cannot be recorded is only logged.
|
|
267
|
+
*/
|
|
268
|
+
async sendCommittedWebhook(dispatch) {
|
|
269
|
+
try {
|
|
270
|
+
const result = await dispatch.send();
|
|
271
|
+
await this.activityLogRepository.save(this.activityLogRepository.create(dispatch.readActivityLog(result)));
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
this.logger.error(`Could not record a webhook result for instance ${dispatch.instanceId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
180
277
|
async submitApprovalInstance(input, authContext) {
|
|
181
278
|
if (input.submittedByMemberId !== undefined &&
|
|
182
279
|
input.submittedByMemberId !== null &&
|
|
@@ -219,7 +316,9 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
219
316
|
const initiatorMetadataSnapshot = input.initiatorMetadataSnapshotJson
|
|
220
317
|
? parseJsonObject(input.initiatorMetadataSnapshotJson, 'initiatorMetadataSnapshotJson')
|
|
221
318
|
: await this.readDefaultInitiatorMetadataSnapshot(input.initiatorMemberId);
|
|
222
|
-
if (!this.conditionService.evaluateBoolean(templateVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel'
|
|
319
|
+
if (!this.conditionService.evaluateBoolean(templateVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel',
|
|
320
|
+
// The policy is written in its version's dialect (ADR 22 §3.3).
|
|
321
|
+
readExpressionDialectOptions(templateVersion.initiatorPolicyCel, templateVersion.workflowDefinition))) {
|
|
223
322
|
throw new common_1.ConflictException('Current member is not allowed to start this approval template');
|
|
224
323
|
}
|
|
225
324
|
// Named here, before the transaction, so the completion notice that later
|
|
@@ -233,7 +332,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
233
332
|
if (!startNode) {
|
|
234
333
|
throw new common_1.BadRequestException('Published workflow does not include a startEvent');
|
|
235
334
|
}
|
|
236
|
-
return this.
|
|
335
|
+
return this.runEngineTransaction(async (manager) => {
|
|
237
336
|
const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
|
|
238
337
|
const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
|
|
239
338
|
const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
|
|
@@ -261,6 +360,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
261
360
|
await this.attachmentService.bindFormDataAttachmentsToInstance(manager, {
|
|
262
361
|
formData,
|
|
263
362
|
instanceId: instance.id,
|
|
363
|
+
schema: formDefinitionVersion.schema,
|
|
264
364
|
});
|
|
265
365
|
const token = await tokenRepository.save(tokenRepository.create({
|
|
266
366
|
consumedAt: null,
|
|
@@ -369,7 +469,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
369
469
|
return { formDataOptionSnapshot, instance };
|
|
370
470
|
}
|
|
371
471
|
async processInstance(instanceId) {
|
|
372
|
-
await this.
|
|
472
|
+
await this.runEngineTransaction(async (manager) => {
|
|
373
473
|
await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
|
|
374
474
|
instanceId,
|
|
375
475
|
]);
|
|
@@ -396,7 +496,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
396
496
|
});
|
|
397
497
|
}
|
|
398
498
|
async decideTask(input, options = {}) {
|
|
399
|
-
return this.
|
|
499
|
+
return this.runEngineTransaction(async (manager) => {
|
|
400
500
|
const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
|
|
401
501
|
const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
|
|
402
502
|
const taskDecisionRepository = manager.getRepository(task_decision_entity_1.TaskDecisionEntity);
|
|
@@ -759,7 +859,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
759
859
|
});
|
|
760
860
|
}
|
|
761
861
|
async cancelApprovalInstance(input) {
|
|
762
|
-
return this.
|
|
862
|
+
return this.runEngineTransaction(async (manager) => {
|
|
763
863
|
await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
|
|
764
864
|
input.instanceId,
|
|
765
865
|
]);
|
|
@@ -800,7 +900,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
800
900
|
* returned unchanged.
|
|
801
901
|
*/
|
|
802
902
|
async expireApprovalInstance(input) {
|
|
803
|
-
return this.
|
|
903
|
+
return this.runEngineTransaction(async (manager) => {
|
|
804
904
|
await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
|
|
805
905
|
input.instanceId,
|
|
806
906
|
]);
|
|
@@ -857,7 +957,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
857
957
|
const preparation = this.formDataSourceValueResolver
|
|
858
958
|
? await this.prepareResubmitFormDataOptions(input, submittedFormData, authContext ?? createWorkflowAuthContext(input.initiatorMemberId))
|
|
859
959
|
: null;
|
|
860
|
-
return this.
|
|
960
|
+
return this.runEngineTransaction(async (manager) => {
|
|
861
961
|
await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
|
|
862
962
|
input.instanceId,
|
|
863
963
|
]);
|
|
@@ -911,6 +1011,14 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
911
1011
|
await this.attachmentService.bindFormDataAttachmentsToInstance(manager, {
|
|
912
1012
|
formData,
|
|
913
1013
|
instanceId: instance.id,
|
|
1014
|
+
schema: resubmitSchema,
|
|
1015
|
+
});
|
|
1016
|
+
// A file the applicant removed from a field leaves the instance too,
|
|
1017
|
+
// so its attachment list matches the form it was resubmitted with.
|
|
1018
|
+
await this.attachmentService.releaseUnreferencedFormAttachments(manager, {
|
|
1019
|
+
formData,
|
|
1020
|
+
instanceId: instance.id,
|
|
1021
|
+
schema: resubmitSchema,
|
|
914
1022
|
});
|
|
915
1023
|
const returnedContext = await this.readLatestReturnedInstanceContext(manager, instance.id);
|
|
916
1024
|
const runtimeInstance = returnedContext?.resubmitStrategy === 'FROM_RETURN_POINT'
|
|
@@ -938,7 +1046,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
938
1046
|
});
|
|
939
1047
|
}
|
|
940
1048
|
async requestAdhocCountersign({ comment, requestedByMemberId, target, taskId, }) {
|
|
941
|
-
return this.
|
|
1049
|
+
return this.runEngineTransaction(async (manager) => {
|
|
942
1050
|
const { instance, node, task } = await this.loadAdhocOperationContext(manager, taskId, requestedByMemberId, { requireAllowAddSigner: true });
|
|
943
1051
|
const targetValue = buildAdhocTargetValue(target);
|
|
944
1052
|
// Rejects WEBHOOK targets — countersigners must be members.
|
|
@@ -970,7 +1078,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
970
1078
|
});
|
|
971
1079
|
}
|
|
972
1080
|
async requestAdhocPreApproval({ comment, onReject, requestedByMemberId, target, taskId, }) {
|
|
973
|
-
return this.
|
|
1081
|
+
return this.runEngineTransaction(async (manager) => {
|
|
974
1082
|
const { instance, node, task } = await this.loadAdhocOperationContext(manager, taskId, requestedByMemberId, { requireAllowAddSigner: true });
|
|
975
1083
|
const targetValue = buildAdhocTargetValue(target);
|
|
976
1084
|
// Rejects WEBHOOK targets — pre-approvers must be members.
|
|
@@ -997,7 +1105,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
997
1105
|
});
|
|
998
1106
|
}
|
|
999
1107
|
async configureAdhocNotification({ channels, requestedByMemberId, target, taskId, type, }) {
|
|
1000
|
-
return this.
|
|
1108
|
+
return this.runEngineTransaction(async (manager) => {
|
|
1001
1109
|
const { instance, node, task } = await this.loadAdhocOperationContext(manager, taskId, requestedByMemberId, { requireAllowAddSigner: false });
|
|
1002
1110
|
const targetValue = buildAdhocTargetValue(target);
|
|
1003
1111
|
const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
|
|
@@ -1020,7 +1128,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1020
1128
|
});
|
|
1021
1129
|
}
|
|
1022
1130
|
async cancelAdhocDirective({ cancelledByMemberId, directiveId, }) {
|
|
1023
|
-
return this.
|
|
1131
|
+
return this.runEngineTransaction(async (manager) => {
|
|
1024
1132
|
const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
|
|
1025
1133
|
const directive = await directiveRepository.findOne({
|
|
1026
1134
|
where: { id: directiveId },
|
|
@@ -1584,30 +1692,34 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1584
1692
|
const targetValue = readAdhocTargetValue(directive);
|
|
1585
1693
|
const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
|
|
1586
1694
|
if (targetValue.kind === adhoc_enums_1.AdhocTargetKindEnum.WEBHOOK) {
|
|
1587
|
-
const
|
|
1695
|
+
const webhookPayload = {
|
|
1588
1696
|
...payload,
|
|
1589
1697
|
instanceId: instance.id,
|
|
1590
1698
|
instanceTitle: instance.title,
|
|
1591
1699
|
message,
|
|
1592
1700
|
triggeredAt: new Date().toISOString(),
|
|
1593
|
-
}
|
|
1594
|
-
await
|
|
1595
|
-
actorMemberId: null,
|
|
1596
|
-
eventType: result.ok
|
|
1597
|
-
? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
|
|
1598
|
-
: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
|
|
1701
|
+
};
|
|
1702
|
+
await this.dispatchWebhookAfterCommit(manager, {
|
|
1599
1703
|
instanceId: instance.id,
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1704
|
+
readActivityLog: (result) => ({
|
|
1705
|
+
actorMemberId: null,
|
|
1706
|
+
eventType: result.ok
|
|
1707
|
+
? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
|
|
1708
|
+
: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
|
|
1709
|
+
instanceId: instance.id,
|
|
1710
|
+
nodeId: directive.originNodeId,
|
|
1711
|
+
payload: {
|
|
1712
|
+
action: 'ADHOC_WEBHOOK',
|
|
1713
|
+
directiveId: directive.id,
|
|
1714
|
+
error: result.error,
|
|
1715
|
+
ok: result.ok,
|
|
1716
|
+
status: result.status,
|
|
1717
|
+
url: targetValue.webhookUrl,
|
|
1718
|
+
},
|
|
1719
|
+
taskId: null,
|
|
1720
|
+
}),
|
|
1721
|
+
send: () => executeAdhocWebhookDispatch(this.serviceTaskDispatcher, targetValue, webhookPayload),
|
|
1722
|
+
});
|
|
1611
1723
|
return;
|
|
1612
1724
|
}
|
|
1613
1725
|
try {
|
|
@@ -1658,29 +1770,38 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1658
1770
|
})));
|
|
1659
1771
|
}
|
|
1660
1772
|
dryRunApprovalWorkflow(input) {
|
|
1773
|
+
// Checked first: parsing happens outside the time budget below.
|
|
1774
|
+
if ([
|
|
1775
|
+
input.workflowDefinitionJson,
|
|
1776
|
+
input.formDataJson,
|
|
1777
|
+
input.initiatorMetadataSnapshotJson ?? '',
|
|
1778
|
+
].some((json) => json.length > MAX_DRY_RUN_INPUT_LENGTH)) {
|
|
1779
|
+
return createFailedDryRunResult('試跑資料過大,請縮小流程或測試資料後再試');
|
|
1780
|
+
}
|
|
1661
1781
|
const workflowDefinition = parseWorkflowDefinition(input.workflowDefinitionJson);
|
|
1662
1782
|
const formData = parseJsonObject(input.formDataJson, 'formDataJson');
|
|
1663
1783
|
const initiatorMetadataSnapshot = input.initiatorMetadataSnapshotJson
|
|
1664
1784
|
? parseJsonObject(input.initiatorMetadataSnapshotJson, 'initiatorMetadataSnapshotJson')
|
|
1665
1785
|
: readDefaultInitiatorMetadataSnapshot(input.initiatorMemberId);
|
|
1666
1786
|
try {
|
|
1787
|
+
// The CEL engine loads outside the budget so an interrupt can never land
|
|
1788
|
+
// halfway through loading a module.
|
|
1789
|
+
this.conditionService.prepareEngine((0, expression_dialect_1.readWorkflowExpressionDialect)(workflowDefinition));
|
|
1790
|
+
// The outer budget bounds the whole walk, and the operation budget
|
|
1791
|
+
// bounds the expressions inside it. Both are charged to this dry run only.
|
|
1667
1792
|
return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunResultObject(), {
|
|
1668
1793
|
errors: [],
|
|
1669
|
-
steps: this.simulateWorkflow({
|
|
1794
|
+
steps: (0, expression_budget_1.runExpressionOperation)(() => (0, time_budget_1.runWithinTimeBudget)(() => this.simulateWorkflow({
|
|
1670
1795
|
formData,
|
|
1671
1796
|
initiatorMemberId: input.initiatorMemberId,
|
|
1672
1797
|
initiatorMetadataSnapshot,
|
|
1673
1798
|
workflowDefinition,
|
|
1674
|
-
}),
|
|
1799
|
+
}), DRY_RUN_TIME_BUDGET_MS)),
|
|
1675
1800
|
valid: true,
|
|
1676
1801
|
});
|
|
1677
1802
|
}
|
|
1678
1803
|
catch (error) {
|
|
1679
|
-
return
|
|
1680
|
-
errors: [error instanceof Error ? error.message : 'Dry run failed'],
|
|
1681
|
-
steps: [],
|
|
1682
|
-
valid: false,
|
|
1683
|
-
});
|
|
1804
|
+
return createFailedDryRunResult(readDryRunFailureMessage(error));
|
|
1684
1805
|
}
|
|
1685
1806
|
}
|
|
1686
1807
|
simulateWorkflow(input) {
|
|
@@ -1688,8 +1809,18 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1688
1809
|
if (!startNode) {
|
|
1689
1810
|
throw new common_1.BadRequestException('Workflow does not include a startEvent');
|
|
1690
1811
|
}
|
|
1812
|
+
// Every branch re-walks whatever follows it, so a chain of gateways doubles
|
|
1813
|
+
// the work at each one while the depth limit never trips. Closure state
|
|
1814
|
+
// because all the branches of one dry run share this one count.
|
|
1815
|
+
let visits = 0;
|
|
1691
1816
|
return this.simulateWorkflowPath({
|
|
1692
1817
|
...input,
|
|
1818
|
+
chargeVisit: () => {
|
|
1819
|
+
visits += 1;
|
|
1820
|
+
if (visits > MAX_DRY_RUN_VISITS) {
|
|
1821
|
+
throw new common_1.ConflictException(DRY_RUN_TOO_COMPLEX_MESSAGE);
|
|
1822
|
+
}
|
|
1823
|
+
},
|
|
1693
1824
|
depth: 0,
|
|
1694
1825
|
incomingEdge: null,
|
|
1695
1826
|
lastDecision: null,
|
|
@@ -1702,6 +1833,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1702
1833
|
if (input.depth > MAX_PROCESSING_STEPS) {
|
|
1703
1834
|
throw new common_1.ConflictException('Dry run exceeded maximum processing steps');
|
|
1704
1835
|
}
|
|
1836
|
+
input.chargeVisit();
|
|
1705
1837
|
const node = readWorkflowNodeOrThrow(input.workflowDefinition, input.nodeId);
|
|
1706
1838
|
const visitKey = `${input.nodeId}:${input.incomingEdge?.id ?? 'start'}`;
|
|
1707
1839
|
const entryCondition = readNodeEntryCondition(node);
|
|
@@ -1723,7 +1855,10 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1723
1855
|
];
|
|
1724
1856
|
}
|
|
1725
1857
|
const nextVisitedNodeIds = new Set([...input.visitedNodeIds, visitKey]);
|
|
1726
|
-
const entryConditionMet = this.conditionService.evaluateBoolean(entryCondition, buildDryRunExpressionContext(input, input.lastDecision), `workflow.nodes.${node.id}.data.entryCondition
|
|
1858
|
+
const entryConditionMet = this.conditionService.evaluateBoolean(entryCondition, buildDryRunExpressionContext(input, input.lastDecision), `workflow.nodes.${node.id}.data.entryCondition`, {
|
|
1859
|
+
allowedRootIdentifiers: DRY_RUN_WORKFLOW_ROOT_IDENTIFIERS,
|
|
1860
|
+
dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(input.workflowDefinition),
|
|
1861
|
+
});
|
|
1727
1862
|
const assignment = entryConditionMet && node.type === 'userTask'
|
|
1728
1863
|
? this.resolveDryRunAssignment(input, node)
|
|
1729
1864
|
: null;
|
|
@@ -1817,7 +1952,10 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1817
1952
|
evaluateDryRunEdge(input, edge, lastDecision) {
|
|
1818
1953
|
const expression = edge.data.condition?.trim();
|
|
1819
1954
|
if (expression && !isListValuedStructuredCondition(edge, input.formData)) {
|
|
1820
|
-
return this.conditionService.evaluateBoolean(expression, buildDryRunExpressionContext(input, lastDecision), `workflow.edges.${edge.id}.data.condition
|
|
1955
|
+
return this.conditionService.evaluateBoolean(expression, buildDryRunExpressionContext(input, lastDecision), `workflow.edges.${edge.id}.data.condition`, {
|
|
1956
|
+
allowedRootIdentifiers: DRY_RUN_EDGE_ROOT_IDENTIFIERS,
|
|
1957
|
+
dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(input.workflowDefinition),
|
|
1958
|
+
});
|
|
1821
1959
|
}
|
|
1822
1960
|
return (0, workflow_condition_evaluator_1.evaluateWorkflowEdgeCondition)(edge, { formData: input.formData });
|
|
1823
1961
|
}
|
|
@@ -1917,7 +2055,10 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
1917
2055
|
return findMemberIdsInValue(readValueAtPath(input.formData, resolver.formPath));
|
|
1918
2056
|
}
|
|
1919
2057
|
if (resolver.type === 'EXPRESSION') {
|
|
1920
|
-
return findMemberIdsInValue(this.conditionService.evaluateValue(resolver.expression, buildDryRunExpressionContext(input, null), `${label}.expression
|
|
2058
|
+
return findMemberIdsInValue(this.conditionService.evaluateValue(resolver.expression, buildDryRunExpressionContext(input, null), `${label}.expression`, {
|
|
2059
|
+
allowedRootIdentifiers: DRY_RUN_WORKFLOW_ROOT_IDENTIFIERS,
|
|
2060
|
+
dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(input.workflowDefinition),
|
|
2061
|
+
}));
|
|
1921
2062
|
}
|
|
1922
2063
|
if (resolver.type === 'ORG_MANAGER') {
|
|
1923
2064
|
const managerMemberIds = findMemberIdsInValue(readManagerMemberIdFromInitiatorSnapshot(input.initiatorMetadataSnapshot, resolver.levelsUp));
|
|
@@ -2129,7 +2270,18 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
2129
2270
|
if (!currentVersion?.formDefinitionVersionId) {
|
|
2130
2271
|
return false;
|
|
2131
2272
|
}
|
|
2132
|
-
|
|
2273
|
+
// A policy in a dialect this version does not know cannot be evaluated,
|
|
2274
|
+
// so the template is not launchable (fail closed), but only that one:
|
|
2275
|
+
// publishing does not check the field yet (Phase 23 P2), and one such
|
|
2276
|
+
// version must not break the list for every member.
|
|
2277
|
+
if (hasExpressionText(currentVersion.initiatorPolicyCel) &&
|
|
2278
|
+
!isReadableExpressionDialect(currentVersion.workflowDefinition)) {
|
|
2279
|
+
this.logger.warn(`template=${template.id} version=${currentVersion.id} left out of launchable templates: unknown expression dialect`);
|
|
2280
|
+
return false;
|
|
2281
|
+
}
|
|
2282
|
+
// Each template is its own operation, so one slow template cannot use
|
|
2283
|
+
// up the allowance of the rest of the list.
|
|
2284
|
+
return (0, expression_budget_1.runExpressionOperation)(() => this.conditionService.evaluateBoolean(currentVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel', readExpressionDialectOptions(currentVersion.initiatorPolicyCel, currentVersion.workflowDefinition)));
|
|
2133
2285
|
});
|
|
2134
2286
|
}
|
|
2135
2287
|
async listWorkflowTokens(instanceId, scope) {
|
|
@@ -2583,7 +2735,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
2583
2735
|
}
|
|
2584
2736
|
isNodeEntryConditionMet(instance, node) {
|
|
2585
2737
|
const entryCondition = readNodeEntryCondition(node);
|
|
2586
|
-
return this.conditionService.evaluateBoolean(entryCondition, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.entryCondition
|
|
2738
|
+
return this.conditionService.evaluateBoolean(entryCondition, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.entryCondition`, readExpressionDialectOptions(entryCondition, instance.workflowSnapshot));
|
|
2587
2739
|
}
|
|
2588
2740
|
async skipNodeByEntryCondition(manager, instance, token, node) {
|
|
2589
2741
|
await this.skipNode(manager, instance, token, node, {
|
|
@@ -2656,7 +2808,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
2656
2808
|
return this.conditionService.evaluateBoolean(expression, {
|
|
2657
2809
|
...buildWorkflowExpressionContext(instance),
|
|
2658
2810
|
lastDecision: await this.readLastDecisionContext(manager, instance.id, node.id),
|
|
2659
|
-
}, `workflow.edges.${edge.id}.data.condition
|
|
2811
|
+
}, `workflow.edges.${edge.id}.data.condition`, { dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(instance.workflowSnapshot) });
|
|
2660
2812
|
}
|
|
2661
2813
|
return (0, workflow_condition_evaluator_1.evaluateWorkflowEdgeCondition)(edge, { formData: instance.formData });
|
|
2662
2814
|
}
|
|
@@ -3482,7 +3634,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
3482
3634
|
});
|
|
3483
3635
|
}
|
|
3484
3636
|
async executeTaskReassignment(preRead, options) {
|
|
3485
|
-
return this.
|
|
3637
|
+
return this.runEngineTransaction(async (manager) => {
|
|
3486
3638
|
try {
|
|
3487
3639
|
const plan = await this.planTaskReassignment(manager, preRead.id, options);
|
|
3488
3640
|
if (options.dryRun) {
|
|
@@ -3859,7 +4011,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
3859
4011
|
async delegateResolvedCandidates(manager, instance, nodeId, resolvedCandidates, gradeGated) {
|
|
3860
4012
|
// One batch for the node, so the host directory is asked once for every
|
|
3861
4013
|
// candidate's delegation agents rather than once per candidate.
|
|
3862
|
-
const resolutions = await this.delegationService.resolveAssignees(resolvedCandidates.map((candidate) => candidate.memberId), this.buildDelegationContext(instance, nodeId));
|
|
4014
|
+
const resolutions = await this.delegationService.resolveAssignees(resolvedCandidates.map((candidate) => candidate.memberId), { ...this.buildDelegationContext(instance, nodeId), manager });
|
|
3863
4015
|
const resolved = resolvedCandidates.map((candidate, index) => ({
|
|
3864
4016
|
candidate,
|
|
3865
4017
|
resolution: resolutions[index],
|
|
@@ -3881,7 +4033,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
3881
4033
|
}
|
|
3882
4034
|
/** One member's delegation, grade-gated as {@link applyGradeGate} says. */
|
|
3883
4035
|
async resolveDelegation(manager, principalMemberId, context, gradeGated) {
|
|
3884
|
-
const resolution = await this.delegationService.resolveAssignee(principalMemberId, context);
|
|
4036
|
+
const resolution = await this.delegationService.resolveAssignee(principalMemberId, { ...context, manager });
|
|
3885
4037
|
const memberIds = gradeGated
|
|
3886
4038
|
? readGradeGateMemberIds(principalMemberId, resolution)
|
|
3887
4039
|
: [];
|
|
@@ -3937,7 +4089,7 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
3937
4089
|
return readMemberIdsFromValue(readValueAtPath(instance.formData, resolver.formPath), label, subject, `依表單欄位「${resolver.formPath}」`);
|
|
3938
4090
|
}
|
|
3939
4091
|
if (resolver.type === 'EXPRESSION') {
|
|
3940
|
-
return readMemberIdsFromValue(this.conditionService.evaluateValue(resolver.expression, buildWorkflowExpressionContext(instance), `${label}.expression
|
|
4092
|
+
return readMemberIdsFromValue(this.conditionService.evaluateValue(resolver.expression, buildWorkflowExpressionContext(instance), `${label}.expression`, { dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(instance.workflowSnapshot) }), label, subject, '依運算式');
|
|
3941
4093
|
}
|
|
3942
4094
|
if (resolver.type === 'POSITION') {
|
|
3943
4095
|
return this.resolvePositionCandidates(manager, resolver.positionId, label, subject);
|
|
@@ -4459,34 +4611,39 @@ let WorkflowEngineService = WorkflowEngineService_1 = class WorkflowEngineServic
|
|
|
4459
4611
|
const action = node.data.action;
|
|
4460
4612
|
if (action.type === 'WEBHOOK') {
|
|
4461
4613
|
const payload = typeof action.payload === 'string' && action.payload.trim()
|
|
4462
|
-
? this.conditionService.evaluateValue(action.payload, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.payload
|
|
4614
|
+
? this.conditionService.evaluateValue(action.payload, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.payload`, {
|
|
4615
|
+
dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(instance.workflowSnapshot),
|
|
4616
|
+
})
|
|
4463
4617
|
: {
|
|
4464
4618
|
instance: buildWorkflowExpressionContext(instance).instance,
|
|
4465
4619
|
triggeredAt: new Date().toISOString(),
|
|
4466
4620
|
};
|
|
4467
|
-
|
|
4468
|
-
await activityRepository.save(activityRepository.create({
|
|
4469
|
-
actorMemberId: null,
|
|
4470
|
-
eventType: result.ok
|
|
4471
|
-
? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
|
|
4472
|
-
: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
|
|
4621
|
+
await this.dispatchWebhookAfterCommit(manager, {
|
|
4473
4622
|
instanceId: instance.id,
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4623
|
+
readActivityLog: (result) => ({
|
|
4624
|
+
actorMemberId: null,
|
|
4625
|
+
eventType: result.ok
|
|
4626
|
+
? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
|
|
4627
|
+
: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
|
|
4628
|
+
instanceId: instance.id,
|
|
4629
|
+
nodeId: node.id,
|
|
4630
|
+
payload: {
|
|
4631
|
+
action: 'WEBHOOK',
|
|
4632
|
+
error: result.error,
|
|
4633
|
+
ok: result.ok,
|
|
4634
|
+
status: result.status,
|
|
4635
|
+
tokenId: token.id,
|
|
4636
|
+
url: action.url,
|
|
4637
|
+
},
|
|
4638
|
+
taskId: null,
|
|
4639
|
+
}),
|
|
4640
|
+
send: () => executeWebhookServiceAction(this.serviceTaskDispatcher, action, payload),
|
|
4641
|
+
});
|
|
4485
4642
|
await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
|
|
4486
4643
|
return;
|
|
4487
4644
|
}
|
|
4488
4645
|
if (action.type === 'SET_FORM_FIELD') {
|
|
4489
|
-
const value = this.conditionService.evaluateValue(action.value, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.value
|
|
4646
|
+
const value = this.conditionService.evaluateValue(action.value, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.value`, { dialect: (0, expression_dialect_1.readWorkflowExpressionDialect)(instance.workflowSnapshot) });
|
|
4490
4647
|
assertServiceTaskTextValue(instance.formDefinitionSnapshot, action.fieldPath, value, node.data.label);
|
|
4491
4648
|
const updatedFormData = writeValueAtPath(instance.formData, action.fieldPath, value);
|
|
4492
4649
|
// A service task runs inside this transaction, so it cannot call a host
|
|
@@ -5608,6 +5765,33 @@ function normalizeFormFieldPath(path) {
|
|
|
5608
5765
|
}
|
|
5609
5766
|
return segments;
|
|
5610
5767
|
}
|
|
5768
|
+
/**
|
|
5769
|
+
* The message a failed dry run shows its caller. Only the fixed budget text, an
|
|
5770
|
+
* HTTP exception's own message, or an expression error's label and code reach
|
|
5771
|
+
* this point. Any other error is replaced with a generic message, since its text
|
|
5772
|
+
* may carry engine or context details.
|
|
5773
|
+
*/
|
|
5774
|
+
function readDryRunFailureMessage(error) {
|
|
5775
|
+
if (error instanceof time_budget_1.TimeBudgetExceededError) {
|
|
5776
|
+
return DRY_RUN_TOO_COMPLEX_MESSAGE;
|
|
5777
|
+
}
|
|
5778
|
+
if (error instanceof expression_errors_1.ExpressionRuntimeError) {
|
|
5779
|
+
return error.keyName
|
|
5780
|
+
? `${error.message}(找不到「${error.keyName}」)`
|
|
5781
|
+
: error.message;
|
|
5782
|
+
}
|
|
5783
|
+
if (error instanceof common_1.HttpException) {
|
|
5784
|
+
return error.message;
|
|
5785
|
+
}
|
|
5786
|
+
return 'Dry run failed';
|
|
5787
|
+
}
|
|
5788
|
+
function createFailedDryRunResult(message) {
|
|
5789
|
+
return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunResultObject(), {
|
|
5790
|
+
errors: [message],
|
|
5791
|
+
steps: [],
|
|
5792
|
+
valid: false,
|
|
5793
|
+
});
|
|
5794
|
+
}
|
|
5611
5795
|
async function executeWebhookServiceAction(serviceTaskDispatcher, action, payload) {
|
|
5612
5796
|
try {
|
|
5613
5797
|
return await serviceTaskDispatcher.dispatchWebhook({
|