openxiangda-contracts 2.47.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 +7 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +7 -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 +10 -0
- package/dist/data-field-validation.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -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 +243 -18
- 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-field.d.ts +2 -0
- package/dist/native-compiler/data-field.d.ts.map +1 -1
- package/dist/native-compiler/data-field.js +4 -0
- 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 +3 -3
- package/dist/native-compiler/index.d.ts +9 -0
- package/dist/native-compiler/index.d.ts.map +1 -1
- package/dist/native-compiler/index.js +3 -3
- 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-version.d.ts +16 -1
- package/dist/native-version.d.ts.map +1 -1
- package/dist/native-version.js +16 -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 +3028 -194
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +244 -2
- 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 +35 -2
- 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,7 +310,8 @@ 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
316
|
const hasTaskFile = (item, types, nestedOnly = false) => {
|
|
305
317
|
const fields = resources.find(resource => resource.code === item.definition.subject?.resourceCode)?.schema.fields || [];
|
|
@@ -372,6 +384,12 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
372
384
|
declaration: resources.filter(resource => resource.decimalReservationLifecycle)
|
|
373
385
|
.map(resource => ({ code: resource.code, decimalReservationLifecycle: resource.decimalReservationLifecycle })) }]
|
|
374
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
|
+
: []),
|
|
375
393
|
...(operations.some(operation => operation.platformAccess?.decimalReservation) ||
|
|
376
394
|
config.events.subscriptions.some((subscription) => subscription.platformAccess?.decimalReservation)
|
|
377
395
|
? [{
|
|
@@ -416,6 +434,14 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
416
434
|
},
|
|
417
435
|
]
|
|
418
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 }] : []),
|
|
419
445
|
...(resources.some(hasDataAuditReadPolicy)
|
|
420
446
|
? [{ code: 'data.audit-read-access', declaration: dataUsage }]
|
|
421
447
|
: []),
|
|
@@ -448,6 +474,13 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
448
474
|
]
|
|
449
475
|
: []),
|
|
450
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
|
+
}] : []),
|
|
451
484
|
...(usesManagedFiles
|
|
452
485
|
? [
|
|
453
486
|
{
|
|
@@ -490,6 +523,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
490
523
|
},
|
|
491
524
|
]
|
|
492
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
|
+
: []),
|
|
493
529
|
...(config.workflows.activations.length
|
|
494
530
|
? [
|
|
495
531
|
{
|
|
@@ -508,12 +544,37 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
508
544
|
declaration: config.workflows.definitions.filter((item) => item.definition.instanceCommands !== undefined),
|
|
509
545
|
}]
|
|
510
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)) }] : []),
|
|
511
566
|
...(namedInputSourceDefinitions.length
|
|
512
567
|
? [{ code: 'workflow.named-input-sources', declaration: namedInputSourceDefinitions }]
|
|
513
568
|
: []),
|
|
514
569
|
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.administration !== undefined || node.operationPolicy !== undefined))
|
|
515
570
|
? [{ code: 'workflow.node-administration', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.administration !== undefined || node.operationPolicy !== undefined)) }]
|
|
516
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
|
+
: []),
|
|
517
578
|
...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'cc'))
|
|
518
579
|
? [{ code: 'workflow.automatic-cc', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'cc')) }]
|
|
519
580
|
: []),
|
|
@@ -556,6 +617,8 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
556
617
|
{
|
|
557
618
|
code: 'notification-hub-v2',
|
|
558
619
|
declaration: {
|
|
620
|
+
...(config.workflows.definitions.some((item) => item.definition.rejectionNotification !== undefined)
|
|
621
|
+
? { rejectionNotifications: config.workflows.definitions.filter((item) => item.definition.rejectionNotification !== undefined) } : {}),
|
|
559
622
|
operations: operations
|
|
560
623
|
.filter(operation => operation.platformAccess?.notification)
|
|
561
624
|
.map(operation => ({
|
|
@@ -592,7 +655,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
|
|
|
592
655
|
return usages
|
|
593
656
|
.map(({ code, declaration }) => ({
|
|
594
657
|
code,
|
|
595
|
-
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],
|
|
596
661
|
usageDigest: `sha256:${sha256Canonical(declaration)}`,
|
|
597
662
|
}))
|
|
598
663
|
.sort((left, right) => left.code === right.code ? 0 : left.code < right.code ? -1 : 1);
|
|
@@ -1007,7 +1072,7 @@ function compileExpectedContract(config, configDigest, generatorVersion) {
|
|
|
1007
1072
|
field: trigger.field,
|
|
1008
1073
|
})),
|
|
1009
1074
|
...(config.workflows.activations.length
|
|
1010
|
-
? 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 => ({
|
|
1011
1076
|
code: `workflow:${eventType}`,
|
|
1012
1077
|
source: 'workflow',
|
|
1013
1078
|
eventType,
|
|
@@ -1212,9 +1277,12 @@ function compileCapabilities(config) {
|
|
|
1212
1277
|
};
|
|
1213
1278
|
catalog.set(code, capability);
|
|
1214
1279
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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) {
|
|
1218
1286
|
const operationNames = {
|
|
1219
1287
|
read: `读取${resource.name}`,
|
|
1220
1288
|
create: `新建${resource.name}`,
|
|
@@ -1233,6 +1301,10 @@ function compileCapabilities(config) {
|
|
|
1233
1301
|
source: 'data',
|
|
1234
1302
|
});
|
|
1235
1303
|
}
|
|
1304
|
+
}
|
|
1305
|
+
const definedCapabilities = new Set(catalog.keys());
|
|
1306
|
+
const fieldOwners = new Map();
|
|
1307
|
+
for (const { pointer, resource } of resources) {
|
|
1236
1308
|
for (const [fieldCode, rawPolicy] of Object.entries(resource.fieldPolicies)) {
|
|
1237
1309
|
if (isDataAuditMetadataField(fieldCode))
|
|
1238
1310
|
continue;
|
|
@@ -1242,6 +1314,13 @@ function compileCapabilities(config) {
|
|
|
1242
1314
|
continue;
|
|
1243
1315
|
for (const code of uniqueStrings(policy[operation], `${pointer}/fieldPolicies/${fieldCode}/${operation}`, 2000)) {
|
|
1244
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);
|
|
1245
1324
|
if (catalog.has(code))
|
|
1246
1325
|
continue;
|
|
1247
1326
|
catalog.set(code, {
|
|
@@ -1530,6 +1609,15 @@ function compileOperations(config) {
|
|
|
1530
1609
|
const code = stableCode(operation.code, `${pointer}/code`);
|
|
1531
1610
|
validateBackendOperationAi(operation.ai, pointer, code, method, declaredResourceCodes);
|
|
1532
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
|
+
}
|
|
1533
1621
|
if (platformAccess?.decimalReservation) {
|
|
1534
1622
|
const reservation = platformAccess.decimalReservation;
|
|
1535
1623
|
const resourceCode = String(reservation.resourceCode);
|
|
@@ -1726,11 +1814,33 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
|
|
|
1726
1814
|
if (value === undefined)
|
|
1727
1815
|
return null;
|
|
1728
1816
|
const access = object(value, pointer);
|
|
1729
|
-
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);
|
|
1730
1818
|
if (Object.keys(access).length === 0) {
|
|
1731
1819
|
fail('NATIVE_OPERATION_PLATFORM_ACCESS_EMPTY', pointer);
|
|
1732
1820
|
}
|
|
1733
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
|
+
}
|
|
1734
1844
|
if (access.roleAssertions !== undefined) {
|
|
1735
1845
|
const assertions = object(access.roleAssertions, `${pointer}/roleAssertions`);
|
|
1736
1846
|
exactKeys(assertions, ['roleCodes'], `${pointer}/roleAssertions`);
|
|
@@ -1754,8 +1864,9 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
|
|
|
1754
1864
|
'employeeNumber',
|
|
1755
1865
|
'primaryDepartment',
|
|
1756
1866
|
'departments',
|
|
1867
|
+
...(directory.mode === 'current-initiator' ? ['phone'] : []),
|
|
1757
1868
|
]);
|
|
1758
|
-
const fields = uniqueStrings(directory.fields, `${pointer}/directory/fields`,
|
|
1869
|
+
const fields = uniqueStrings(directory.fields, `${pointer}/directory/fields`, allowed.size);
|
|
1759
1870
|
if (!fields.length || fields.some(field => !allowed.has(field))) {
|
|
1760
1871
|
fail('NATIVE_OPERATION_DIRECTORY_FIELDS_INVALID', `${pointer}/directory/fields`);
|
|
1761
1872
|
}
|
|
@@ -1848,12 +1959,16 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
|
|
|
1848
1959
|
}
|
|
1849
1960
|
if (access.workflow !== undefined) {
|
|
1850
1961
|
const workflow = object(access.workflow, `${pointer}/workflow`);
|
|
1851
|
-
exactKeys(workflow, ['codes'], `${pointer}/workflow
|
|
1962
|
+
exactKeys(workflow, ['codes'], `${pointer}/workflow`, ['businessCommands']);
|
|
1852
1963
|
const codes = uniqueStrings(workflow.codes, `${pointer}/workflow/codes`, 16);
|
|
1853
1964
|
if (!codes.length || codes.some(code => !declaredWorkflowCodes.has(code))) {
|
|
1854
1965
|
fail('NATIVE_OPERATION_WORKFLOW_CODE_INVALID', `${pointer}/workflow/codes`);
|
|
1855
1966
|
}
|
|
1856
|
-
|
|
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) } : {}) };
|
|
1857
1972
|
}
|
|
1858
1973
|
if (access.decimalReservation !== undefined) {
|
|
1859
1974
|
const reservationPointer = `${pointer}/decimalReservation`;
|
|
@@ -3615,6 +3730,40 @@ function validateAuthorizationReferences(config, capabilities) {
|
|
|
3615
3730
|
config.data.resources.forEach((rawResource, index) => {
|
|
3616
3731
|
const resourcePointer = `/config/data/resources/${index}`;
|
|
3617
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
|
+
}
|
|
3618
3767
|
for (const [fieldCode, rawPolicy] of Object.entries(resource.fieldPolicies || {})) {
|
|
3619
3768
|
if (!isDataAuditMetadataField(fieldCode))
|
|
3620
3769
|
continue;
|
|
@@ -3633,6 +3782,9 @@ function validateAuthorizationReferences(config, capabilities) {
|
|
|
3633
3782
|
return;
|
|
3634
3783
|
const resourceCapabilities = object(resource.capabilities, `${resourcePointer}/capabilities`);
|
|
3635
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;
|
|
3636
3788
|
forbiddenNativeMutationCapabilities.set(String(resourceCapabilities[operation]), operation);
|
|
3637
3789
|
}
|
|
3638
3790
|
});
|
|
@@ -4101,9 +4253,10 @@ function validateDataPolicyReferences(config) {
|
|
|
4101
4253
|
function validateDataFieldReferences(config) {
|
|
4102
4254
|
try {
|
|
4103
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`));
|
|
4104
4257
|
}
|
|
4105
4258
|
catch (error) {
|
|
4106
|
-
if (error instanceof NativeDataFieldContractV2Error) {
|
|
4259
|
+
if (error instanceof NativeDataFieldContractV2Error || error instanceof UserCandidateContractError) {
|
|
4107
4260
|
fail(error.code, error.pointer);
|
|
4108
4261
|
}
|
|
4109
4262
|
throw error;
|
|
@@ -4326,6 +4479,12 @@ function validateWorkflowReferences(config) {
|
|
|
4326
4479
|
}
|
|
4327
4480
|
definitions.add(key);
|
|
4328
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`);
|
|
4329
4488
|
validateWorkflowSubject(definition.subject, resources, `${pointer}/definition/subject`);
|
|
4330
4489
|
const taskPageErrors = validateWorkflowTaskPages(definition, new Map((resources.get(definition.subject.resourceCode)?.schema.fields || [])
|
|
4331
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) }]))])));
|
|
@@ -4385,6 +4544,9 @@ function validateWorkflowReferences(config) {
|
|
|
4385
4544
|
}
|
|
4386
4545
|
const definition = definitionValues.get(definitionKey);
|
|
4387
4546
|
const binding = bindingValues.get(bindingKey);
|
|
4547
|
+
const preflightErrors = validateWorkflowLaunchPreflight(definition, binding);
|
|
4548
|
+
if (preflightErrors.length)
|
|
4549
|
+
fail(preflightErrors[0], pointer);
|
|
4388
4550
|
const administrationErrors = validateWorkflowAdministration(definition, binding);
|
|
4389
4551
|
if (administrationErrors.length)
|
|
4390
4552
|
fail(administrationErrors[0], pointer);
|
|
@@ -4394,6 +4556,15 @@ function validateWorkflowReferences(config) {
|
|
|
4394
4556
|
fail('NATIVE_WORKFLOW_COMMAND_DEACTIVATION_POLICY_MISMATCH', `${pointer}/acceptedCommandDeactivationPolicy`);
|
|
4395
4557
|
}
|
|
4396
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
|
+
}
|
|
4397
4568
|
activeBindings.set(code, binding);
|
|
4398
4569
|
}
|
|
4399
4570
|
const routingErrors = validateWorkflowAssignmentRoutingBindings([...activeBindings.values()]);
|
|
@@ -4552,7 +4723,7 @@ function validateWorkflowDefinition(definition, pointer) {
|
|
|
4552
4723
|
'startAt',
|
|
4553
4724
|
'inputSchema',
|
|
4554
4725
|
'nodes',
|
|
4555
|
-
], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages']);
|
|
4726
|
+
], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages', 'rejectionNotification', 'commandHandlers', 'launchPreflight']);
|
|
4556
4727
|
equal(definition.schemaVersion, 'openxiangda.workflow-definition/v2', `${pointer}/schemaVersion`);
|
|
4557
4728
|
workflowCodeValue(definition.code, `${pointer}/code`);
|
|
4558
4729
|
requiredString(definition.title, `${pointer}/title`, 255);
|
|
@@ -4571,6 +4742,15 @@ function validateWorkflowDefinition(definition, pointer) {
|
|
|
4571
4742
|
const administrationErrors = validateWorkflowAdministration(definition);
|
|
4572
4743
|
if (administrationErrors.length)
|
|
4573
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`);
|
|
4574
4754
|
const ccErrors = validateWorkflowAutomaticCc(definition);
|
|
4575
4755
|
if (ccErrors.length)
|
|
4576
4756
|
fail(ccErrors[0], `${pointer}/nodes`);
|
|
@@ -4715,10 +4895,10 @@ function validateWorkflowBinding(binding, pointer) {
|
|
|
4715
4895
|
].includes(provider)) {
|
|
4716
4896
|
fail('NATIVE_WORKFLOW_BINDING_PROVIDER_INVALID', `${pointer}/bindings/${code}/provider`);
|
|
4717
4897
|
}
|
|
4718
|
-
if (provider === 'fixed_users'
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
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);
|
|
4722
4902
|
}
|
|
4723
4903
|
if (['input_users', 'form_field_users'].includes(provider)) {
|
|
4724
4904
|
requiredString(entry.inputPath, `${pointer}/bindings/${code}/inputPath`, 255);
|
|
@@ -4742,6 +4922,13 @@ function validateWorkflowBinding(binding, pointer) {
|
|
|
4742
4922
|
function validateWorkflowDefinitionBinding(definition, binding, pointer) {
|
|
4743
4923
|
equal(binding.workflowCode, definition.code, `${pointer}/workflowCode`);
|
|
4744
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);
|
|
4745
4932
|
const ccErrors = validateWorkflowAutomaticCc(definition, binding);
|
|
4746
4933
|
if (ccErrors.length)
|
|
4747
4934
|
fail(ccErrors[0], pointer);
|
|
@@ -4836,6 +5023,10 @@ function validateResource(raw, pointer, appCode) {
|
|
|
4836
5023
|
'invariants',
|
|
4837
5024
|
'decimalReservationLifecycle',
|
|
4838
5025
|
'uniqueKeys',
|
|
5026
|
+
'workflowHistory',
|
|
5027
|
+
'recordPrint',
|
|
5028
|
+
'recordComments',
|
|
5029
|
+
'recordDeletion',
|
|
4839
5030
|
'capabilities',
|
|
4840
5031
|
'dataPolicyCode',
|
|
4841
5032
|
'fieldPolicies',
|
|
@@ -4858,11 +5049,45 @@ function validateResource(raw, pointer, appCode) {
|
|
|
4858
5049
|
validateNativeDataResourceSurfaceV2(resource.surface, fields, `${pointer}/surface`);
|
|
4859
5050
|
}
|
|
4860
5051
|
catch (error) {
|
|
4861
|
-
if (error instanceof NativeDataFieldContractV2Error || error instanceof NativeDecimalLifecycleContractError || error instanceof NativeUniqueKeyContractError) {
|
|
5052
|
+
if (error instanceof NativeDataFieldContractV2Error || error instanceof UserCandidateContractError || error instanceof NativeDecimalLifecycleContractError || error instanceof NativeUniqueKeyContractError) {
|
|
4862
5053
|
fail(error.code, error.pointer);
|
|
4863
5054
|
}
|
|
4864
5055
|
throw error;
|
|
4865
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
|
+
}
|
|
4866
5091
|
const fieldCodes = new Set(fields.map(field => field.code));
|
|
4867
5092
|
const capabilities = object(resource.capabilities, `${pointer}/capabilities`);
|
|
4868
5093
|
exactKeys(capabilities, ['read', 'create', 'update', 'delete'], `${pointer}/capabilities`);
|