openxiangda-contracts 2.43.0 → 2.63.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/dist/browser.d.ts +8 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +8 -0
- package/dist/browser.js.map +1 -1
- package/dist/concurrency.d.ts +2 -2
- package/dist/concurrency.d.ts.map +1 -1
- package/dist/data-field-validation.d.ts.map +1 -1
- package/dist/data-field-validation.js +13 -2
- package/dist/data-field-validation.js.map +1 -1
- package/dist/field-value-schemas.d.ts +1 -1
- package/dist/field-value-schemas.d.ts.map +1 -1
- package/dist/field-value-schemas.js +2 -1
- package/dist/field-value-schemas.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/native-compiler/compiler.d.ts +1 -1
- package/dist/native-compiler/compiler.d.ts.map +1 -1
- package/dist/native-compiler/compiler.js +266 -22
- package/dist/native-compiler/compiler.js.map +1 -1
- package/dist/native-compiler/data-business-command.d.ts +42 -0
- package/dist/native-compiler/data-business-command.d.ts.map +1 -0
- package/dist/native-compiler/data-business-command.js +88 -0
- package/dist/native-compiler/data-business-command.js.map +1 -0
- package/dist/native-compiler/data-capacity.d.ts +8 -0
- package/dist/native-compiler/data-capacity.d.ts.map +1 -0
- package/dist/native-compiler/data-capacity.js +15 -0
- package/dist/native-compiler/data-capacity.js.map +1 -0
- package/dist/native-compiler/data-field.d.ts +2 -0
- package/dist/native-compiler/data-field.d.ts.map +1 -1
- package/dist/native-compiler/data-field.js +7 -2
- package/dist/native-compiler/data-field.js.map +1 -1
- package/dist/native-compiler/data-surface.d.ts +1 -1
- package/dist/native-compiler/data-surface.d.ts.map +1 -1
- package/dist/native-compiler/data-surface.js +2 -0
- package/dist/native-compiler/data-surface.js.map +1 -1
- package/dist/native-compiler/index.cjs +5 -5
- package/dist/native-compiler/index.d.ts +10 -0
- package/dist/native-compiler/index.d.ts.map +1 -1
- package/dist/native-compiler/index.js +5 -5
- package/dist/native-compiler/managed-concurrency.js +2 -2
- package/dist/native-compiler/managed-concurrency.js.map +1 -1
- package/dist/native-compiler/resource-view.d.ts +4 -0
- package/dist/native-compiler/resource-view.d.ts.map +1 -0
- package/dist/native-compiler/resource-view.js +20 -0
- package/dist/native-compiler/resource-view.js.map +1 -0
- package/dist/native-compiler/unique-keys.d.ts +21 -0
- package/dist/native-compiler/unique-keys.d.ts.map +1 -1
- package/dist/native-compiler/unique-keys.js +17 -3
- package/dist/native-compiler/unique-keys.js.map +1 -1
- package/dist/native-compiler/user-candidates.d.ts +72 -0
- package/dist/native-compiler/user-candidates.d.ts.map +1 -0
- package/dist/native-compiler/user-candidates.js +184 -0
- package/dist/native-compiler/user-candidates.js.map +1 -0
- package/dist/native-compiler/workflow-approval-empty.d.ts +28 -0
- package/dist/native-compiler/workflow-approval-empty.d.ts.map +1 -0
- package/dist/native-compiler/workflow-approval-empty.js +34 -0
- package/dist/native-compiler/workflow-approval-empty.js.map +1 -0
- package/dist/native-compiler/workflow-business-command.d.ts +108 -0
- package/dist/native-compiler/workflow-business-command.d.ts.map +1 -0
- package/dist/native-compiler/workflow-business-command.js +58 -0
- package/dist/native-compiler/workflow-business-command.js.map +1 -0
- package/dist/native-compiler/workflow-graph.d.ts +8 -0
- package/dist/native-compiler/workflow-graph.d.ts.map +1 -1
- package/dist/native-compiler/workflow-graph.js +1 -0
- package/dist/native-compiler/workflow-graph.js.map +1 -1
- package/dist/native-compiler/workflow-initiator-approval.d.ts +28 -0
- package/dist/native-compiler/workflow-initiator-approval.d.ts.map +1 -0
- package/dist/native-compiler/workflow-initiator-approval.js +30 -0
- package/dist/native-compiler/workflow-initiator-approval.js.map +1 -0
- package/dist/native-compiler/workflow-instance-policy.d.ts +2 -0
- package/dist/native-compiler/workflow-instance-policy.d.ts.map +1 -1
- package/dist/native-compiler/workflow-instance-policy.js +9 -4
- package/dist/native-compiler/workflow-instance-policy.js.map +1 -1
- package/dist/native-compiler/workflow-launch-preflight.d.ts +22 -0
- package/dist/native-compiler/workflow-launch-preflight.d.ts.map +1 -0
- package/dist/native-compiler/workflow-launch-preflight.js +30 -0
- package/dist/native-compiler/workflow-launch-preflight.js.map +1 -0
- package/dist/native-compiler/workflow-node-administration.d.ts +6 -0
- package/dist/native-compiler/workflow-node-administration.d.ts.map +1 -1
- package/dist/native-compiler/workflow-node-administration.js +9 -2
- package/dist/native-compiler/workflow-node-administration.js.map +1 -1
- package/dist/native-compiler/workflow-rejection-notification.d.ts +31 -0
- package/dist/native-compiler/workflow-rejection-notification.d.ts.map +1 -0
- package/dist/native-compiler/workflow-rejection-notification.js +47 -0
- package/dist/native-compiler/workflow-rejection-notification.js.map +1 -0
- package/dist/native-compiler/workflow-task-page.d.ts +44 -3
- package/dist/native-compiler/workflow-task-page.d.ts.map +1 -1
- package/dist/native-compiler/workflow-task-page.js +108 -9
- package/dist/native-compiler/workflow-task-page.js.map +1 -1
- package/dist/native-version.d.ts +19 -1
- package/dist/native-version.d.ts.map +1 -1
- package/dist/native-version.js +19 -1
- package/dist/native-version.js.map +1 -1
- package/dist/native.d.ts +15 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/schemas.d.ts +3048 -212
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +253 -9
- package/dist/schemas.js.map +1 -1
- package/dist/surface.d.ts +3 -2
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +1 -19
- package/dist/surface.js.map +1 -1
- package/dist/types.d.ts +286 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +42 -5
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { validateUserCandidateReferences, validateWorkflowUserCandidateBindings, UserCandidateContractError } from './user-candidates.js';
|
|
2
|
+
import { NativeUniqueKeyContractError, parseNativeUniqueKeys, nativeUniqueKeysContractVersion } from './unique-keys.js';
|
|
2
3
|
import { validateManagedConcurrency, ManagedConcurrencyContractError } from './managed-concurrency.js';
|
|
3
4
|
import { normalizeDecimalReservationEventDeclaration, decimalReservationEventContext } from './decimal-reservation.js';
|
|
4
5
|
import { NativeDecimalLifecycleContractError, parseDecimalReservationLifecycle, validateDecimalReservationLifecycles } from './decimal-lifecycle.js';
|
|
@@ -9,6 +10,11 @@ import { validateWorkflowInstanceCommandPolicies } from './workflow-instance-pol
|
|
|
9
10
|
import { validateWorkflowReadability } from './workflow-graph.js';
|
|
10
11
|
import { validateWorkflowAdministration } from './workflow-node-administration.js';
|
|
11
12
|
import { validateWorkflowAutomaticCc } from './workflow-automatic-cc.js';
|
|
13
|
+
import { validateWorkflowApprovalEmptyPolicy, workflowBindingAllowsEmptyUsers } from './workflow-approval-empty.js';
|
|
14
|
+
import { validateWorkflowInitiatorApprovalPolicy } from './workflow-initiator-approval.js';
|
|
15
|
+
import { validateWorkflowRejectionNotification } from './workflow-rejection-notification.js';
|
|
16
|
+
import { validateWorkflowCommandHandlers } from './workflow-business-command.js';
|
|
17
|
+
import { validateWorkflowLaunchPreflight } from './workflow-launch-preflight.js';
|
|
12
18
|
import { validateWorkflowTaskPages } from './workflow-task-page.js';
|
|
13
19
|
import { validateWorkflowBusinessSteps, compileWorkflowBusinessStepHandlers, validateWorkflowBusinessStepSubscriptions, WORKFLOW_BUSINESS_STEP_EVENT, WORKFLOW_BUSINESS_STEP_EVENTS } from './workflow-business-step.js';
|
|
14
20
|
import { validateWorkflowAssignmentRoutingBindings, validateWorkflowAssignmentRoutingPolicy } from './workflow-assignment-routing.js';
|
|
@@ -124,6 +130,7 @@ export const DATA_EVENT_TYPES_V2 = [
|
|
|
124
130
|
'openxiangda.data.record.deleted.v2',
|
|
125
131
|
];
|
|
126
132
|
export const WORKFLOW_EVENT_TYPES_V2 = [
|
|
133
|
+
'openxiangda.workflow.node.skipped.v2',
|
|
127
134
|
WORKFLOW_BUSINESS_STEP_EVENT,
|
|
128
135
|
'openxiangda.workflow.step.result_received.v2',
|
|
129
136
|
'openxiangda.workflow.step.completed.v2',
|
|
@@ -284,6 +291,10 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
284
291
|
const roles = config.authz.roles;
|
|
285
292
|
const resources = config.data.resources;
|
|
286
293
|
const operations = config.backend.operations;
|
|
294
|
+
const initiatorPhoneOperations = operations.filter(operation => operation.platformAccess?.directory?.mode === 'current-initiator' &&
|
|
295
|
+
operation.platformAccess.directory.fields.includes('phone'));
|
|
296
|
+
const initiatorPhoneCommands = (config.data.concurrency?.commands || []).filter((command) => command.execution?.directory?.mode === 'current-initiator' &&
|
|
297
|
+
command.execution.directory.fields.includes('phone'));
|
|
287
298
|
const requiresDirectoryV2 = roles.some(role => role.capabilities.some((capability) => capability.endsWith(':directory:read'))) ||
|
|
288
299
|
resources.some(resource => resource.schema.fields.some((field) => [
|
|
289
300
|
'user.single',
|
|
@@ -299,12 +310,26 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
299
310
|
const usesManagedFiles = operations.some(operation => operation.platformAccess?.managedFiles ||
|
|
300
311
|
operation.platformAccess?.managedFileCopies) ||
|
|
301
312
|
config.events.subscriptions.some((subscription) => subscription.platformAccess?.managedFileCopies);
|
|
302
|
-
const usesNotification =
|
|
313
|
+
const usesNotification = config.workflows.definitions.some((item) => item.definition.rejectionNotification !== undefined) ||
|
|
314
|
+
operations.some(operation => operation.platformAccess?.notification) ||
|
|
303
315
|
config.events.subscriptions.some((subscription) => subscription.platformAccess?.notification);
|
|
304
|
-
const
|
|
316
|
+
const hasTaskFile = (item, types, nestedOnly = false) => {
|
|
305
317
|
const fields = resources.find(resource => resource.code === item.definition.subject?.resourceCode)?.schema.fields || [];
|
|
306
|
-
return Object.values(item.definition.taskPages || {}).some((page) => page.fields.some((field) =>
|
|
307
|
-
|
|
318
|
+
return Object.values(item.definition.taskPages || {}).some((page) => page.fields.some((field) => {
|
|
319
|
+
if (field.readonly)
|
|
320
|
+
return false;
|
|
321
|
+
const metadata = fields.find((value) => value.code === field.code);
|
|
322
|
+
if (!nestedOnly && metadata && types.includes(metadata.type))
|
|
323
|
+
return true;
|
|
324
|
+
if (!field.subtable || !metadata?.subtable)
|
|
325
|
+
return false;
|
|
326
|
+
const children = resources.find(resource => resource.code === metadata.subtable.resourceCode)?.schema.fields || [];
|
|
327
|
+
return field.subtable.fields.some((child) => !child.readonly && children.some((value) => value.code === child.code && types.includes(value.type)));
|
|
328
|
+
}));
|
|
329
|
+
};
|
|
330
|
+
const taskManagedFileDefinitions = config.workflows.definitions.filter((item) => hasTaskFile(item, ['file', 'image', 'signature', 'text.rich']));
|
|
331
|
+
const taskRichFieldDefinitions = config.workflows.definitions.filter((item) => hasTaskFile(item, ['signature', 'text.rich']));
|
|
332
|
+
const taskOwnedFileDefinitions = config.workflows.definitions.filter((item) => hasTaskFile(item, ['file', 'image', 'signature', 'text.rich'], true));
|
|
308
333
|
const standardProcessDefinitions = config.workflows.definitions.filter((declaration) => declaration.launch.mode === 'standalone' ||
|
|
309
334
|
declaration.launch.mode === 'hidden-handoff');
|
|
310
335
|
const usesBusinessProcess = operations.some(operation => operation.platformAccess?.workflow) ||
|
|
@@ -359,6 +384,12 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
359
384
|
declaration: resources.filter(resource => resource.decimalReservationLifecycle)
|
|
360
385
|
.map(resource => ({ code: resource.code, decimalReservationLifecycle: resource.decimalReservationLifecycle })) }]
|
|
361
386
|
: []),
|
|
387
|
+
...(operations.some(operation => operation.platformAccess?.dataCommands)
|
|
388
|
+
? [{ code: 'data.business-commands', declaration: operations.filter(operation => operation.platformAccess?.dataCommands) }]
|
|
389
|
+
: []),
|
|
390
|
+
...(operations.some(operation => operation.platformAccess?.recordEdit)
|
|
391
|
+
? [{ code: 'data.record-edit', declaration: operations.filter(operation => operation.platformAccess?.recordEdit) }]
|
|
392
|
+
: []),
|
|
362
393
|
...(operations.some(operation => operation.platformAccess?.decimalReservation) ||
|
|
363
394
|
config.events.subscriptions.some((subscription) => subscription.platformAccess?.decimalReservation)
|
|
364
395
|
? [{
|
|
@@ -403,6 +434,14 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
403
434
|
},
|
|
404
435
|
]
|
|
405
436
|
: []),
|
|
437
|
+
...(resources.some(resource => resource.recordComments !== undefined)
|
|
438
|
+
? [{ code: 'data.record-comments', declaration: dataUsage }] : []),
|
|
439
|
+
...(resources.some(resource => resource.recordDeletion !== undefined)
|
|
440
|
+
? [{ code: 'data.workflow-record-deletion', declaration: dataUsage }] : []),
|
|
441
|
+
...(resources.some(resource => resource.recordPrint !== undefined)
|
|
442
|
+
? [{ code: 'data.record-print', declaration: dataUsage }] : []),
|
|
443
|
+
...(resources.some(resource => resource.workflowHistory !== undefined)
|
|
444
|
+
? [{ code: 'workflow.record-history-read', declaration: dataUsage }] : []),
|
|
406
445
|
...(resources.some(hasDataAuditReadPolicy)
|
|
407
446
|
? [{ code: 'data.audit-read-access', declaration: dataUsage }]
|
|
408
447
|
: []),
|
|
@@ -435,6 +474,13 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
435
474
|
]
|
|
436
475
|
: []),
|
|
437
476
|
...(operations.some(operation => operation.platformAccess?.directory?.mode === 'selected-user') ? [{ code: 'directory.selected-user', declaration: operations.filter(operation => operation.platformAccess?.directory?.mode === 'selected-user').map(operation => ({ code: operation.code, directory: operation.platformAccess.directory })) }] : []),
|
|
477
|
+
...(initiatorPhoneOperations.length || initiatorPhoneCommands.length ? [{
|
|
478
|
+
code: 'directory.current-initiator-phone',
|
|
479
|
+
declaration: {
|
|
480
|
+
operations: initiatorPhoneOperations.map(operation => ({ code: operation.code, directory: operation.platformAccess.directory })),
|
|
481
|
+
commands: initiatorPhoneCommands.map((command) => ({ code: command.code, directory: command.execution.directory })),
|
|
482
|
+
},
|
|
483
|
+
}] : []),
|
|
438
484
|
...(usesManagedFiles
|
|
439
485
|
? [
|
|
440
486
|
{
|
|
@@ -477,6 +523,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
477
523
|
},
|
|
478
524
|
]
|
|
479
525
|
: []),
|
|
526
|
+
...(config.data.resources.some((resource) => resource.schema.fields.some((field) => field.userCandidates !== undefined))
|
|
527
|
+
? [{ code: 'data.user-candidates', declaration: config.data.resources.filter((resource) => resource.schema.fields.some((field) => field.userCandidates !== undefined)) }]
|
|
528
|
+
: []),
|
|
480
529
|
...(config.workflows.activations.length
|
|
481
530
|
? [
|
|
482
531
|
{
|
|
@@ -495,12 +544,37 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
495
544
|
declaration: config.workflows.definitions.filter((item) => item.definition.instanceCommands !== undefined),
|
|
496
545
|
}]
|
|
497
546
|
: []),
|
|
547
|
+
...(config.workflows.definitions.some((item) => item.definition.instanceCommands?.withdraw?.reasonRequired === false)
|
|
548
|
+
? [{ code: 'workflow.optional-withdrawal-reason',
|
|
549
|
+
declaration: config.workflows.definitions.filter((item) => item.definition.instanceCommands?.withdraw?.reasonRequired === false) }]
|
|
550
|
+
: []),
|
|
551
|
+
...(config.workflows.definitions.some((item) => item.definition.rejectionNotification !== undefined)
|
|
552
|
+
? [{
|
|
553
|
+
code: 'workflow.rejection-notification',
|
|
554
|
+
declaration: config.workflows.definitions.filter((item) => item.definition.rejectionNotification !== undefined),
|
|
555
|
+
}]
|
|
556
|
+
: []),
|
|
557
|
+
...(config.workflows.definitions.some((item) => item.definition.commandHandlers !== undefined) || operations.some(operation => operation.platformAccess?.workflow?.businessCommands)
|
|
558
|
+
? [{ code: 'workflow.business-data-command', declaration: {
|
|
559
|
+
definitions: config.workflows.definitions.filter((item) => item.definition.commandHandlers !== undefined),
|
|
560
|
+
operations: operations.filter(operation => operation.platformAccess?.workflow?.businessCommands),
|
|
561
|
+
} }] : []),
|
|
562
|
+
...(config.workflows.definitions.some((item) => item.definition.launchPreflight !== undefined)
|
|
563
|
+
? [{ code: 'workflow.launch-preflight', declaration: config.workflows.definitions.filter((item) => item.definition.launchPreflight !== undefined) }] : []),
|
|
564
|
+
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.operationPolicy?.reject?.commentRequired === false))
|
|
565
|
+
? [{ code: 'workflow.optional-rejection-comment', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.operationPolicy?.reject?.commentRequired === false)) }] : []),
|
|
498
566
|
...(namedInputSourceDefinitions.length
|
|
499
567
|
? [{ code: 'workflow.named-input-sources', declaration: namedInputSourceDefinitions }]
|
|
500
568
|
: []),
|
|
501
569
|
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.administration !== undefined || node.operationPolicy !== undefined))
|
|
502
570
|
? [{ code: 'workflow.node-administration', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.administration !== undefined || node.operationPolicy !== undefined)) }]
|
|
503
571
|
: []),
|
|
572
|
+
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'approval' && node.emptyPolicy !== undefined))
|
|
573
|
+
? [{ code: 'workflow.approval-empty-policy', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'approval' && node.emptyPolicy !== undefined)) }]
|
|
574
|
+
: []),
|
|
575
|
+
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.initiatorApprovalPolicy !== undefined))
|
|
576
|
+
? [{ code: 'workflow.initiator-approval-policy', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.initiatorApprovalPolicy !== undefined)) }]
|
|
577
|
+
: []),
|
|
504
578
|
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'cc'))
|
|
505
579
|
? [{ code: 'workflow.automatic-cc', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'cc')) }]
|
|
506
580
|
: []),
|
|
@@ -509,6 +583,12 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
509
583
|
: []),
|
|
510
584
|
...(taskManagedFileDefinitions.length
|
|
511
585
|
? [{ code: 'workflow.task-managed-files', declaration: taskManagedFileDefinitions }] : []),
|
|
586
|
+
...(taskOwnedFileDefinitions.length
|
|
587
|
+
? [{ code: 'workflow.task-owned-files', declaration: taskOwnedFileDefinitions }] : []),
|
|
588
|
+
...(taskRichFieldDefinitions.length
|
|
589
|
+
? [{ code: 'workflow.task-rich-fields', declaration: taskRichFieldDefinitions }] : []),
|
|
590
|
+
...(config.workflows.definitions.some((item) => Object.values(item.definition.taskPages || {}).some((page) => page.fields.some((field) => field.subtable)))
|
|
591
|
+
? [{ code: 'workflow.task-owned-subtables', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.taskPages || {}).some((page) => page.fields.some((field) => field.subtable))) }] : []),
|
|
512
592
|
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action'))
|
|
513
593
|
? [{ code: 'workflow.durable-business-step', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action')) }]
|
|
514
594
|
: []),
|
|
@@ -537,6 +617,8 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
537
617
|
{
|
|
538
618
|
code: 'notification-hub-v2',
|
|
539
619
|
declaration: {
|
|
620
|
+
...(config.workflows.definitions.some((item) => item.definition.rejectionNotification !== undefined)
|
|
621
|
+
? { rejectionNotifications: config.workflows.definitions.filter((item) => item.definition.rejectionNotification !== undefined) } : {}),
|
|
540
622
|
operations: operations
|
|
541
623
|
.filter(operation => operation.platformAccess?.notification)
|
|
542
624
|
.map(operation => ({
|
|
@@ -573,7 +655,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
573
655
|
return usages
|
|
574
656
|
.map(({ code, declaration }) => ({
|
|
575
657
|
code,
|
|
576
|
-
contractVersion:
|
|
658
|
+
contractVersion: code === 'data.unique-keys'
|
|
659
|
+
? (resources.some(resource => nativeUniqueKeysContractVersion(resource.schema.fields, resource.uniqueKeys) === '1.1.0') ? '1.1.0' : '1.0.0')
|
|
660
|
+
: OPENXIANGDA_V2_PLATFORM_CAPABILITY_CONTRACT_VERSIONS[code],
|
|
577
661
|
usageDigest: `sha256:${sha256Canonical(declaration)}`,
|
|
578
662
|
}))
|
|
579
663
|
.sort((left, right) => left.code === right.code ? 0 : left.code < right.code ? -1 : 1);
|
|
@@ -988,7 +1072,7 @@ function compileExpectedContract(config, configDigest, generatorVersion) {
|
|
|
988
1072
|
field: trigger.field,
|
|
989
1073
|
})),
|
|
990
1074
|
...(config.workflows.activations.length
|
|
991
|
-
? WORKFLOW_EVENT_TYPES_V2.filter(eventType => !WORKFLOW_BUSINESS_STEP_EVENTS.includes(eventType) || config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action'))).map(eventType => ({
|
|
1075
|
+
? WORKFLOW_EVENT_TYPES_V2.filter(eventType => !WORKFLOW_BUSINESS_STEP_EVENTS.includes(eventType) || config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action'))).filter(eventType => eventType !== 'openxiangda.workflow.node.skipped.v2' || config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'approval' && node.emptyPolicy === 'skip'))).map(eventType => ({
|
|
992
1076
|
code: `workflow:${eventType}`,
|
|
993
1077
|
source: 'workflow',
|
|
994
1078
|
eventType,
|
|
@@ -1193,9 +1277,12 @@ function compileCapabilities(config) {
|
|
|
1193
1277
|
};
|
|
1194
1278
|
catalog.set(code, capability);
|
|
1195
1279
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1280
|
+
const resources = config.data.resources.map((raw, resourceIndex) => ({
|
|
1281
|
+
pointer: `/config/data/resources/${resourceIndex}`,
|
|
1282
|
+
resource: validateResource(raw, `/config/data/resources/${resourceIndex}`, config.appCode),
|
|
1283
|
+
}));
|
|
1284
|
+
// CRUD definitions precede all field references, regardless of source order.
|
|
1285
|
+
for (const { pointer, resource } of resources) {
|
|
1199
1286
|
const operationNames = {
|
|
1200
1287
|
read: `读取${resource.name}`,
|
|
1201
1288
|
create: `新建${resource.name}`,
|
|
@@ -1214,6 +1301,10 @@ function compileCapabilities(config) {
|
|
|
1214
1301
|
source: 'data',
|
|
1215
1302
|
});
|
|
1216
1303
|
}
|
|
1304
|
+
}
|
|
1305
|
+
const definedCapabilities = new Set(catalog.keys());
|
|
1306
|
+
const fieldOwners = new Map();
|
|
1307
|
+
for (const { pointer, resource } of resources) {
|
|
1217
1308
|
for (const [fieldCode, rawPolicy] of Object.entries(resource.fieldPolicies)) {
|
|
1218
1309
|
if (isDataAuditMetadataField(fieldCode))
|
|
1219
1310
|
continue;
|
|
@@ -1223,6 +1314,13 @@ function compileCapabilities(config) {
|
|
|
1223
1314
|
continue;
|
|
1224
1315
|
for (const code of uniqueStrings(policy[operation], `${pointer}/fieldPolicies/${fieldCode}/${operation}`, 2000)) {
|
|
1225
1316
|
capabilityCode(code, `${pointer}/fieldPolicies/${fieldCode}/${operation}`, config.appCode);
|
|
1317
|
+
if (definedCapabilities.has(code))
|
|
1318
|
+
continue;
|
|
1319
|
+
const existingOwner = fieldOwners.get(code);
|
|
1320
|
+
if (existingOwner && existingOwner !== resource.code) {
|
|
1321
|
+
fail('NATIVE_CAPABILITY_DUPLICATE', `${pointer}/fieldPolicies/${fieldCode}/${operation}`);
|
|
1322
|
+
}
|
|
1323
|
+
fieldOwners.set(code, resource.code);
|
|
1226
1324
|
if (catalog.has(code))
|
|
1227
1325
|
continue;
|
|
1228
1326
|
catalog.set(code, {
|
|
@@ -1511,6 +1609,15 @@ function compileOperations(config) {
|
|
|
1511
1609
|
const code = stableCode(operation.code, `${pointer}/code`);
|
|
1512
1610
|
validateBackendOperationAi(operation.ai, pointer, code, method, declaredResourceCodes);
|
|
1513
1611
|
const platformAccess = validateOperationPlatformAccess(operation.platformAccess, `${pointer}/platformAccess`, declaredResources, declaredResourceFields, declaredWorkflowCodes, new Set(config.authz.roles.map((role) => String(role.code))));
|
|
1612
|
+
if (platformAccess?.recordEdit) {
|
|
1613
|
+
const owner = config.data.resources.find((item) => item.code === platformAccess.recordEdit.resourceCode);
|
|
1614
|
+
if (owner?.surface?.mutationOwner !== 'action')
|
|
1615
|
+
fail('NATIVE_RECORD_EDIT_ACTION_OWNER_REQUIRED', `${pointer}/platformAccess/recordEdit`);
|
|
1616
|
+
for (const fieldCode of platformAccess.recordEdit.fieldCodes) {
|
|
1617
|
+
if (!owner.fieldPolicies?.[fieldCode]?.update?.length || owner.surface?.fields?.[fieldCode]?.system === true)
|
|
1618
|
+
fail('NATIVE_RECORD_EDIT_FIELD_INVALID', `${pointer}/platformAccess/recordEdit/fieldCodes`);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1514
1621
|
if (platformAccess?.decimalReservation) {
|
|
1515
1622
|
const reservation = platformAccess.decimalReservation;
|
|
1516
1623
|
const resourceCode = String(reservation.resourceCode);
|
|
@@ -1707,11 +1814,33 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
|
|
|
1707
1814
|
if (value === undefined)
|
|
1708
1815
|
return null;
|
|
1709
1816
|
const access = object(value, pointer);
|
|
1710
|
-
exactKeys(access, ['directory', 'managedFiles', 'managedFileCopies', 'notification', 'workflow', 'roleAssertions', 'decimalReservation'], pointer, true);
|
|
1817
|
+
exactKeys(access, ['directory', 'managedFiles', 'managedFileCopies', 'notification', 'workflow', 'roleAssertions', 'decimalReservation', 'dataCommands', 'recordEdit'], pointer, true);
|
|
1711
1818
|
if (Object.keys(access).length === 0) {
|
|
1712
1819
|
fail('NATIVE_OPERATION_PLATFORM_ACCESS_EMPTY', pointer);
|
|
1713
1820
|
}
|
|
1714
1821
|
const result = {};
|
|
1822
|
+
if (access.dataCommands !== undefined) {
|
|
1823
|
+
const commands = object(access.dataCommands, `${pointer}/dataCommands`);
|
|
1824
|
+
exactKeys(commands, ['mode'], `${pointer}/dataCommands`);
|
|
1825
|
+
equal(commands.mode, 'recoverable-native', `${pointer}/dataCommands/mode`);
|
|
1826
|
+
result.dataCommands = { mode: 'recoverable-native' };
|
|
1827
|
+
}
|
|
1828
|
+
if (access.recordEdit !== undefined) {
|
|
1829
|
+
const entryPointer = `${pointer}/recordEdit`;
|
|
1830
|
+
const entry = object(access.recordEdit, entryPointer);
|
|
1831
|
+
exactKeys(entry, ['resourceCode', 'fieldCodes'], entryPointer);
|
|
1832
|
+
const code = resourceCode(entry.resourceCode, `${entryPointer}/resourceCode`);
|
|
1833
|
+
const fields = declaredResourceFields.get(code);
|
|
1834
|
+
const fieldCodes = uniqueStrings(entry.fieldCodes, `${entryPointer}/fieldCodes`, 200);
|
|
1835
|
+
if (!fields || !fieldCodes.length || !result.dataCommands)
|
|
1836
|
+
fail('NATIVE_RECORD_EDIT_DECLARATION_INVALID', entryPointer);
|
|
1837
|
+
for (const fieldCode of fieldCodes) {
|
|
1838
|
+
const field = fields.get(fieldCode);
|
|
1839
|
+
if (!field || field.type === 'serial-number' || field.type === 'subtable')
|
|
1840
|
+
fail('NATIVE_RECORD_EDIT_FIELD_INVALID', `${entryPointer}/fieldCodes`);
|
|
1841
|
+
}
|
|
1842
|
+
result.recordEdit = { resourceCode: code, fieldCodes: uniqueSorted(fieldCodes) };
|
|
1843
|
+
}
|
|
1715
1844
|
if (access.roleAssertions !== undefined) {
|
|
1716
1845
|
const assertions = object(access.roleAssertions, `${pointer}/roleAssertions`);
|
|
1717
1846
|
exactKeys(assertions, ['roleCodes'], `${pointer}/roleAssertions`);
|
|
@@ -1735,8 +1864,9 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
|
|
|
1735
1864
|
'employeeNumber',
|
|
1736
1865
|
'primaryDepartment',
|
|
1737
1866
|
'departments',
|
|
1867
|
+
...(directory.mode === 'current-initiator' ? ['phone'] : []),
|
|
1738
1868
|
]);
|
|
1739
|
-
const fields = uniqueStrings(directory.fields, `${pointer}/directory/fields`,
|
|
1869
|
+
const fields = uniqueStrings(directory.fields, `${pointer}/directory/fields`, allowed.size);
|
|
1740
1870
|
if (!fields.length || fields.some(field => !allowed.has(field))) {
|
|
1741
1871
|
fail('NATIVE_OPERATION_DIRECTORY_FIELDS_INVALID', `${pointer}/directory/fields`);
|
|
1742
1872
|
}
|
|
@@ -1829,12 +1959,16 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
|
|
|
1829
1959
|
}
|
|
1830
1960
|
if (access.workflow !== undefined) {
|
|
1831
1961
|
const workflow = object(access.workflow, `${pointer}/workflow`);
|
|
1832
|
-
exactKeys(workflow, ['codes'], `${pointer}/workflow
|
|
1962
|
+
exactKeys(workflow, ['codes'], `${pointer}/workflow`, ['businessCommands']);
|
|
1833
1963
|
const codes = uniqueStrings(workflow.codes, `${pointer}/workflow/codes`, 16);
|
|
1834
1964
|
if (!codes.length || codes.some(code => !declaredWorkflowCodes.has(code))) {
|
|
1835
1965
|
fail('NATIVE_OPERATION_WORKFLOW_CODE_INVALID', `${pointer}/workflow/codes`);
|
|
1836
1966
|
}
|
|
1837
|
-
|
|
1967
|
+
const businessCommands = workflow.businessCommands === undefined ? undefined : uniqueStrings(workflow.businessCommands, `${pointer}/workflow/businessCommands`, 3);
|
|
1968
|
+
if (businessCommands && (!businessCommands.length || businessCommands.some(command => !['approve', 'reject', 'withdraw'].includes(command)))) {
|
|
1969
|
+
fail('NATIVE_OPERATION_WORKFLOW_BUSINESS_COMMAND_INVALID', `${pointer}/workflow/businessCommands`);
|
|
1970
|
+
}
|
|
1971
|
+
result.workflow = { codes: uniqueSorted(codes), ...(businessCommands ? { businessCommands: uniqueSorted(businessCommands) } : {}) };
|
|
1838
1972
|
}
|
|
1839
1973
|
if (access.decimalReservation !== undefined) {
|
|
1840
1974
|
const reservationPointer = `${pointer}/decimalReservation`;
|
|
@@ -3596,6 +3730,40 @@ function validateAuthorizationReferences(config, capabilities) {
|
|
|
3596
3730
|
config.data.resources.forEach((rawResource, index) => {
|
|
3597
3731
|
const resourcePointer = `/config/data/resources/${index}`;
|
|
3598
3732
|
const resource = object(rawResource, resourcePointer);
|
|
3733
|
+
if (resource.recordDeletion !== undefined) {
|
|
3734
|
+
const pointer = `${resourcePointer}/recordDeletion/delete`;
|
|
3735
|
+
for (const capability of resource.recordDeletion.delete) {
|
|
3736
|
+
capabilityCode(capability, pointer, config.appCode);
|
|
3737
|
+
if (!capabilityCodes.has(capability))
|
|
3738
|
+
fail('NATIVE_CAPABILITY_REFERENCE_MISSING', pointer);
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
if (resource.recordComments !== undefined) {
|
|
3742
|
+
for (const mode of ['read', 'create']) {
|
|
3743
|
+
const pointer = `${resourcePointer}/recordComments/${mode}`;
|
|
3744
|
+
for (const capability of resource.recordComments[mode]) {
|
|
3745
|
+
capabilityCode(capability, pointer, config.appCode);
|
|
3746
|
+
if (!capabilityCodes.has(capability))
|
|
3747
|
+
fail('NATIVE_CAPABILITY_REFERENCE_MISSING', pointer);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
if (resource.recordPrint !== undefined) {
|
|
3752
|
+
const pointer = `${resourcePointer}/recordPrint/read`;
|
|
3753
|
+
for (const capability of resource.recordPrint.read) {
|
|
3754
|
+
capabilityCode(capability, pointer, config.appCode);
|
|
3755
|
+
if (!capabilityCodes.has(capability))
|
|
3756
|
+
fail('NATIVE_CAPABILITY_REFERENCE_MISSING', pointer);
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
if (resource.workflowHistory !== undefined) {
|
|
3760
|
+
const pointer = `${resourcePointer}/workflowHistory/read`;
|
|
3761
|
+
for (const capability of resource.workflowHistory.read) {
|
|
3762
|
+
capabilityCode(capability, pointer, config.appCode);
|
|
3763
|
+
if (!capabilityCodes.has(capability))
|
|
3764
|
+
fail('NATIVE_CAPABILITY_REFERENCE_MISSING', pointer);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3599
3767
|
for (const [fieldCode, rawPolicy] of Object.entries(resource.fieldPolicies || {})) {
|
|
3600
3768
|
if (!isDataAuditMetadataField(fieldCode))
|
|
3601
3769
|
continue;
|
|
@@ -3614,6 +3782,9 @@ function validateAuthorizationReferences(config, capabilities) {
|
|
|
3614
3782
|
return;
|
|
3615
3783
|
const resourceCapabilities = object(resource.capabilities, `${resourcePointer}/capabilities`);
|
|
3616
3784
|
for (const operation of ['create', 'update', 'delete']) {
|
|
3785
|
+
if (operation === 'delete' && Array.isArray(resource.recordDeletion?.delete) &&
|
|
3786
|
+
resource.recordDeletion.delete.length > 0)
|
|
3787
|
+
continue;
|
|
3617
3788
|
forbiddenNativeMutationCapabilities.set(String(resourceCapabilities[operation]), operation);
|
|
3618
3789
|
}
|
|
3619
3790
|
});
|
|
@@ -4082,9 +4253,10 @@ function validateDataPolicyReferences(config) {
|
|
|
4082
4253
|
function validateDataFieldReferences(config) {
|
|
4083
4254
|
try {
|
|
4084
4255
|
validateNativeDataResourceReferencesV2(config.data.resources, '/config/data/resources');
|
|
4256
|
+
config.data.resources.forEach((resource, index) => validateUserCandidateReferences(resource.schema.fields, new Set(config.authz.roles.map((role) => role.code)), new Set(config.authz.scopeDimensions.map((dimension) => dimension.code)), `/config/data/resources/${index}/schema/fields`));
|
|
4085
4257
|
}
|
|
4086
4258
|
catch (error) {
|
|
4087
|
-
if (error instanceof NativeDataFieldContractV2Error) {
|
|
4259
|
+
if (error instanceof NativeDataFieldContractV2Error || error instanceof UserCandidateContractError) {
|
|
4088
4260
|
fail(error.code, error.pointer);
|
|
4089
4261
|
}
|
|
4090
4262
|
throw error;
|
|
@@ -4307,9 +4479,15 @@ function validateWorkflowReferences(config) {
|
|
|
4307
4479
|
}
|
|
4308
4480
|
definitions.add(key);
|
|
4309
4481
|
validateWorkflowDefinition(definition, `${pointer}/definition`);
|
|
4482
|
+
const preflightErrors = validateWorkflowLaunchPreflight(definition);
|
|
4483
|
+
if (preflightErrors.length)
|
|
4484
|
+
fail(preflightErrors[0], `${pointer}/definition/launchPreflight`);
|
|
4485
|
+
const businessCommandErrors = validateWorkflowCommandHandlers(definition, config.backend.operations);
|
|
4486
|
+
if (businessCommandErrors.length)
|
|
4487
|
+
fail(businessCommandErrors[0], `${pointer}/definition/commandHandlers`);
|
|
4310
4488
|
validateWorkflowSubject(definition.subject, resources, `${pointer}/definition/subject`);
|
|
4311
4489
|
const taskPageErrors = validateWorkflowTaskPages(definition, new Map((resources.get(definition.subject.resourceCode)?.schema.fields || [])
|
|
4312
|
-
.map((field) => [field.code, { ...resources.get(definition.subject.resourceCode)?.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }])));
|
|
4490
|
+
.map((field) => [field.code, { ...resources.get(definition.subject.resourceCode)?.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }])), new Map([...resources].map(([code, resource]) => [code, new Map(resource.schema.fields.map((field) => [field.code, { ...resource.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }]))])));
|
|
4313
4491
|
if (taskPageErrors.length)
|
|
4314
4492
|
fail(taskPageErrors[0], `${pointer}/definition/taskPages`);
|
|
4315
4493
|
const policyErrors = validateWorkflowInstanceCommandPolicies(definition, {
|
|
@@ -4366,6 +4544,9 @@ function validateWorkflowReferences(config) {
|
|
|
4366
4544
|
}
|
|
4367
4545
|
const definition = definitionValues.get(definitionKey);
|
|
4368
4546
|
const binding = bindingValues.get(bindingKey);
|
|
4547
|
+
const preflightErrors = validateWorkflowLaunchPreflight(definition, binding);
|
|
4548
|
+
if (preflightErrors.length)
|
|
4549
|
+
fail(preflightErrors[0], pointer);
|
|
4369
4550
|
const administrationErrors = validateWorkflowAdministration(definition, binding);
|
|
4370
4551
|
if (administrationErrors.length)
|
|
4371
4552
|
fail(administrationErrors[0], pointer);
|
|
@@ -4375,6 +4556,15 @@ function validateWorkflowReferences(config) {
|
|
|
4375
4556
|
fail('NATIVE_WORKFLOW_COMMAND_DEACTIVATION_POLICY_MISMATCH', `${pointer}/acceptedCommandDeactivationPolicy`);
|
|
4376
4557
|
}
|
|
4377
4558
|
validateWorkflowDefinitionBinding(definition, binding, `/config/workflows/activations/${index}`);
|
|
4559
|
+
try {
|
|
4560
|
+
const fields = resources.get(definition.subject.resourceCode)?.schema.fields || [];
|
|
4561
|
+
validateWorkflowUserCandidateBindings(definition, binding, new Map(fields.map((field) => [field.code, field])), `${pointer}/bindings`, new Map([...resources].map(([code, resource]) => [code, new Map(resource.schema.fields.map((field) => [field.code, field]))])));
|
|
4562
|
+
}
|
|
4563
|
+
catch (error) {
|
|
4564
|
+
if (error instanceof UserCandidateContractError)
|
|
4565
|
+
fail(error.code, error.pointer);
|
|
4566
|
+
throw error;
|
|
4567
|
+
}
|
|
4378
4568
|
activeBindings.set(code, binding);
|
|
4379
4569
|
}
|
|
4380
4570
|
const routingErrors = validateWorkflowAssignmentRoutingBindings([...activeBindings.values()]);
|
|
@@ -4533,7 +4723,7 @@ function validateWorkflowDefinition(definition, pointer) {
|
|
|
4533
4723
|
'startAt',
|
|
4534
4724
|
'inputSchema',
|
|
4535
4725
|
'nodes',
|
|
4536
|
-
], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages']);
|
|
4726
|
+
], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages', 'rejectionNotification', 'commandHandlers', 'launchPreflight']);
|
|
4537
4727
|
equal(definition.schemaVersion, 'openxiangda.workflow-definition/v2', `${pointer}/schemaVersion`);
|
|
4538
4728
|
workflowCodeValue(definition.code, `${pointer}/code`);
|
|
4539
4729
|
requiredString(definition.title, `${pointer}/title`, 255);
|
|
@@ -4552,6 +4742,15 @@ function validateWorkflowDefinition(definition, pointer) {
|
|
|
4552
4742
|
const administrationErrors = validateWorkflowAdministration(definition);
|
|
4553
4743
|
if (administrationErrors.length)
|
|
4554
4744
|
fail(administrationErrors[0], `${pointer}/nodes`);
|
|
4745
|
+
const emptyPolicyErrors = validateWorkflowApprovalEmptyPolicy(definition);
|
|
4746
|
+
if (emptyPolicyErrors.length)
|
|
4747
|
+
fail(emptyPolicyErrors[0], `${pointer}/nodes`);
|
|
4748
|
+
const initiatorPolicyErrors = validateWorkflowInitiatorApprovalPolicy(definition);
|
|
4749
|
+
if (initiatorPolicyErrors.length)
|
|
4750
|
+
fail(initiatorPolicyErrors[0], `${pointer}/nodes`);
|
|
4751
|
+
const rejectionNotificationErrors = validateWorkflowRejectionNotification(definition);
|
|
4752
|
+
if (rejectionNotificationErrors.length)
|
|
4753
|
+
fail(rejectionNotificationErrors[0], `${pointer}/rejectionNotification`);
|
|
4555
4754
|
const ccErrors = validateWorkflowAutomaticCc(definition);
|
|
4556
4755
|
if (ccErrors.length)
|
|
4557
4756
|
fail(ccErrors[0], `${pointer}/nodes`);
|
|
@@ -4696,10 +4895,10 @@ function validateWorkflowBinding(binding, pointer) {
|
|
|
4696
4895
|
].includes(provider)) {
|
|
4697
4896
|
fail('NATIVE_WORKFLOW_BINDING_PROVIDER_INVALID', `${pointer}/bindings/${code}/provider`);
|
|
4698
4897
|
}
|
|
4699
|
-
if (provider === 'fixed_users'
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4898
|
+
if (provider === 'fixed_users') {
|
|
4899
|
+
if (!Array.isArray(entry.users))
|
|
4900
|
+
fail('NATIVE_WORKFLOW_BINDING_USERS_REQUIRED', `${pointer}/bindings/${code}/users`);
|
|
4901
|
+
boundedArray(entry.users, `${pointer}/bindings/${code}/users`, 200);
|
|
4703
4902
|
}
|
|
4704
4903
|
if (['input_users', 'form_field_users'].includes(provider)) {
|
|
4705
4904
|
requiredString(entry.inputPath, `${pointer}/bindings/${code}/inputPath`, 255);
|
|
@@ -4723,6 +4922,13 @@ function validateWorkflowBinding(binding, pointer) {
|
|
|
4723
4922
|
function validateWorkflowDefinitionBinding(definition, binding, pointer) {
|
|
4724
4923
|
equal(binding.workflowCode, definition.code, `${pointer}/workflowCode`);
|
|
4725
4924
|
const entries = object(binding.bindings, `${pointer}/bindings`);
|
|
4925
|
+
for (const [code, entry] of Object.entries(entries)) {
|
|
4926
|
+
if (entry.provider === 'fixed_users' && !entry.users.length && !workflowBindingAllowsEmptyUsers(definition, code))
|
|
4927
|
+
fail('NATIVE_WORKFLOW_BINDING_USERS_REQUIRED', `${pointer}/bindings/${code}/users`);
|
|
4928
|
+
}
|
|
4929
|
+
const emptyPolicyErrors = validateWorkflowApprovalEmptyPolicy(definition, binding);
|
|
4930
|
+
if (emptyPolicyErrors.length)
|
|
4931
|
+
fail(emptyPolicyErrors[0], pointer);
|
|
4726
4932
|
const ccErrors = validateWorkflowAutomaticCc(definition, binding);
|
|
4727
4933
|
if (ccErrors.length)
|
|
4728
4934
|
fail(ccErrors[0], pointer);
|
|
@@ -4817,6 +5023,10 @@ function validateResource(raw, pointer, appCode) {
|
|
|
4817
5023
|
'invariants',
|
|
4818
5024
|
'decimalReservationLifecycle',
|
|
4819
5025
|
'uniqueKeys',
|
|
5026
|
+
'workflowHistory',
|
|
5027
|
+
'recordPrint',
|
|
5028
|
+
'recordComments',
|
|
5029
|
+
'recordDeletion',
|
|
4820
5030
|
'capabilities',
|
|
4821
5031
|
'dataPolicyCode',
|
|
4822
5032
|
'fieldPolicies',
|
|
@@ -4839,11 +5049,45 @@ function validateResource(raw, pointer, appCode) {
|
|
|
4839
5049
|
validateNativeDataResourceSurfaceV2(resource.surface, fields, `${pointer}/surface`);
|
|
4840
5050
|
}
|
|
4841
5051
|
catch (error) {
|
|
4842
|
-
if (error instanceof NativeDataFieldContractV2Error || error instanceof NativeDecimalLifecycleContractError || error instanceof NativeUniqueKeyContractError) {
|
|
5052
|
+
if (error instanceof NativeDataFieldContractV2Error || error instanceof UserCandidateContractError || error instanceof NativeDecimalLifecycleContractError || error instanceof NativeUniqueKeyContractError) {
|
|
4843
5053
|
fail(error.code, error.pointer);
|
|
4844
5054
|
}
|
|
4845
5055
|
throw error;
|
|
4846
5056
|
}
|
|
5057
|
+
if (resource.recordComments !== undefined) {
|
|
5058
|
+
const policy = object(resource.recordComments, `${pointer}/recordComments`);
|
|
5059
|
+
exactKeys(policy, ['read', 'create'], `${pointer}/recordComments`);
|
|
5060
|
+
for (const mode of ['read', 'create']) {
|
|
5061
|
+
const list = policy[mode];
|
|
5062
|
+
if (!Array.isArray(list) || list.length > 20 ||
|
|
5063
|
+
list.some(value => typeof value !== 'string' || !value.trim()) || new Set(list).size !== list.length)
|
|
5064
|
+
fail('NATIVE_RECORD_COMMENTS_POLICY_INVALID', `${pointer}/recordComments/${mode}`);
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
if (resource.recordDeletion !== undefined) {
|
|
5068
|
+
const policy = object(resource.recordDeletion, `${pointer}/recordDeletion`);
|
|
5069
|
+
exactKeys(policy, ['delete'], `${pointer}/recordDeletion`);
|
|
5070
|
+
const remove = policy.delete;
|
|
5071
|
+
if (!Array.isArray(remove) || remove.length > 20 ||
|
|
5072
|
+
remove.some(value => typeof value !== 'string' || !value.trim()) || new Set(remove).size !== remove.length)
|
|
5073
|
+
fail('NATIVE_RECORD_DELETION_DELETE_INVALID', `${pointer}/recordDeletion/delete`);
|
|
5074
|
+
}
|
|
5075
|
+
if (resource.recordPrint !== undefined) {
|
|
5076
|
+
const policy = object(resource.recordPrint, `${pointer}/recordPrint`);
|
|
5077
|
+
exactKeys(policy, ['read'], `${pointer}/recordPrint`);
|
|
5078
|
+
const read = policy.read;
|
|
5079
|
+
if (!Array.isArray(read) || read.length > 20 ||
|
|
5080
|
+
read.some(value => typeof value !== 'string' || !value.trim()) || new Set(read).size !== read.length)
|
|
5081
|
+
fail('NATIVE_RECORD_PRINT_READ_INVALID', `${pointer}/recordPrint/read`);
|
|
5082
|
+
}
|
|
5083
|
+
if (resource.workflowHistory !== undefined) {
|
|
5084
|
+
const policy = object(resource.workflowHistory, `${pointer}/workflowHistory`);
|
|
5085
|
+
exactKeys(policy, ['read'], `${pointer}/workflowHistory`);
|
|
5086
|
+
const read = policy.read;
|
|
5087
|
+
if (!Array.isArray(read) || read.length > 20 ||
|
|
5088
|
+
read.some(value => typeof value !== 'string' || !value.trim()) || new Set(read).size !== read.length)
|
|
5089
|
+
fail('NATIVE_WORKFLOW_HISTORY_READ_INVALID', `${pointer}/workflowHistory/read`);
|
|
5090
|
+
}
|
|
4847
5091
|
const fieldCodes = new Set(fields.map(field => field.code));
|
|
4848
5092
|
const capabilities = object(resource.capabilities, `${pointer}/capabilities`);
|
|
4849
5093
|
exactKeys(capabilities, ['read', 'create', 'update', 'delete'], `${pointer}/capabilities`);
|