@servicenow/sdk-build-plugins 4.6.1 → 4.7.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/acl-plugin.js +0 -3
- package/dist/acl-plugin.js.map +1 -1
- package/dist/applicability-plugin.js +0 -2
- package/dist/applicability-plugin.js.map +1 -1
- package/dist/application-menu-plugin.js +0 -2
- package/dist/application-menu-plugin.js.map +1 -1
- package/dist/arrow-function-plugin.js +0 -1
- package/dist/arrow-function-plugin.js.map +1 -1
- package/dist/atf/test-plugin.js +0 -2
- package/dist/atf/test-plugin.js.map +1 -1
- package/dist/basic-syntax-plugin.js +0 -1
- package/dist/basic-syntax-plugin.js.map +1 -1
- package/dist/business-rule-plugin.js +0 -1
- package/dist/business-rule-plugin.js.map +1 -1
- package/dist/call-expression-plugin.js +0 -1
- package/dist/call-expression-plugin.js.map +1 -1
- package/dist/claims-plugin.js +0 -1
- package/dist/claims-plugin.js.map +1 -1
- package/dist/client-script-plugin.js +0 -1
- package/dist/client-script-plugin.js.map +1 -1
- package/dist/column-plugin.js +24 -7
- package/dist/column-plugin.js.map +1 -1
- package/dist/cross-scope-privilege-plugin.js +0 -1
- package/dist/cross-scope-privilege-plugin.js.map +1 -1
- package/dist/dashboard/dashboard-plugin.js +0 -2
- package/dist/dashboard/dashboard-plugin.js.map +1 -1
- package/dist/data-plugin.js +0 -1
- package/dist/data-plugin.js.map +1 -1
- package/dist/data-policy-plugin.d.ts +2 -0
- package/dist/data-policy-plugin.js +276 -0
- package/dist/data-policy-plugin.js.map +1 -0
- package/dist/email-notification-plugin.js +2 -3
- package/dist/email-notification-plugin.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-constants.d.ts +2 -0
- package/dist/flow/flow-logic/flow-logic-constants.js +6 -1
- package/dist/flow/flow-logic/flow-logic-constants.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-diagnostics.js +192 -56
- package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +2 -1
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +44 -5
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin.js +279 -29
- package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-shapes.d.ts +15 -0
- package/dist/flow/flow-logic/flow-logic-shapes.js +25 -1
- package/dist/flow/flow-logic/flow-logic-shapes.js.map +1 -1
- package/dist/flow/plugins/approval-rules-plugin.js +0 -1
- package/dist/flow/plugins/approval-rules-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +804 -205
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-data-pill-plugin.js +3 -5
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +84 -17
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-diagnostics-plugin.js +65 -3
- package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-instance-plugin.js +13 -5
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-trigger-instance-plugin.js +0 -1
- package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/inline-script-plugin.js +0 -1
- package/dist/flow/plugins/inline-script-plugin.js.map +1 -1
- package/dist/flow/plugins/step-definition-plugin.js +0 -2
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.js +216 -77
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/trigger-plugin.js +0 -2
- package/dist/flow/plugins/trigger-plugin.js.map +1 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js +0 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
- package/dist/flow/utils/datapill-transformer.js +9 -5
- package/dist/flow/utils/datapill-transformer.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +12 -0
- package/dist/flow/utils/flow-constants.js +17 -3
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
- package/dist/flow/utils/flow-io-to-record.js +21 -13
- package/dist/flow/utils/flow-io-to-record.js.map +1 -1
- package/dist/flow/utils/flow-pill-utils.d.ts +26 -0
- package/dist/flow/utils/flow-pill-utils.js +50 -0
- package/dist/flow/utils/flow-pill-utils.js.map +1 -0
- package/dist/flow/utils/flow-stage-processor.d.ts +138 -0
- package/dist/flow/utils/flow-stage-processor.js +665 -0
- package/dist/flow/utils/flow-stage-processor.js.map +1 -0
- package/dist/flow/utils/pill-string-parser.js +28 -43
- package/dist/flow/utils/pill-string-parser.js.map +1 -1
- package/dist/flow/utils/utils.d.ts +11 -6
- package/dist/flow/utils/utils.js +37 -28
- package/dist/flow/utils/utils.js.map +1 -1
- package/dist/form-plugin.js +4 -14
- package/dist/form-plugin.js.map +1 -1
- package/dist/html-import-plugin.js +0 -1
- package/dist/html-import-plugin.js.map +1 -1
- package/dist/import-sets-plugin.js +0 -2
- package/dist/import-sets-plugin.js.map +1 -1
- package/dist/inbound-email-action-plugin.js +0 -1
- package/dist/inbound-email-action-plugin.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/instance-scan-plugin.js +0 -7
- package/dist/instance-scan-plugin.js.map +1 -1
- package/dist/json-plugin.js +0 -1
- package/dist/json-plugin.js.map +1 -1
- package/dist/list-plugin.js +4 -1
- package/dist/list-plugin.js.map +1 -1
- package/dist/now-attach-plugin.js +0 -1
- package/dist/now-attach-plugin.js.map +1 -1
- package/dist/now-config-plugin.js +0 -1
- package/dist/now-config-plugin.js.map +1 -1
- package/dist/now-id-plugin.js +0 -1
- package/dist/now-id-plugin.js.map +1 -1
- package/dist/now-include-plugin.js +0 -1
- package/dist/now-include-plugin.js.map +1 -1
- package/dist/now-ref-plugin.js +0 -1
- package/dist/now-ref-plugin.js.map +1 -1
- package/dist/now-unresolved-plugin.js +0 -1
- package/dist/now-unresolved-plugin.js.map +1 -1
- package/dist/package-json-plugin.js +3 -2
- package/dist/package-json-plugin.js.map +1 -1
- package/dist/property-plugin.js +0 -2
- package/dist/property-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +2 -0
- package/dist/record-plugin.js +2 -2
- package/dist/record-plugin.js.map +1 -1
- package/dist/repack/lint/Rules.d.ts +1 -2
- package/dist/rest-api-plugin.js +6 -5
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/role-plugin.js +0 -1
- package/dist/role-plugin.js.map +1 -1
- package/dist/schedule-script/scheduled-script-plugin.js +5 -4
- package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
- package/dist/script-action-plugin.js +0 -2
- package/dist/script-action-plugin.js.map +1 -1
- package/dist/script-include-plugin.js +0 -4
- package/dist/script-include-plugin.js.map +1 -1
- package/dist/server-module-plugin/index.js +2 -3
- package/dist/server-module-plugin/index.js.map +1 -1
- package/dist/service-catalog/catalog-clientscript-plugin.js +0 -2
- package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
- package/dist/service-catalog/catalog-item-plugin.js +0 -2
- package/dist/service-catalog/catalog-item-plugin.js.map +1 -1
- package/dist/service-catalog/catalog-ui-policy-plugin.js +0 -2
- package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
- package/dist/service-catalog/sc-record-producer-plugin.js +0 -2
- package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
- package/dist/service-catalog/variable-set-plugin.js +0 -2
- package/dist/service-catalog/variable-set-plugin.js.map +1 -1
- package/dist/service-portal/angular-provider-plugin.js +0 -2
- package/dist/service-portal/angular-provider-plugin.js.map +1 -1
- package/dist/service-portal/dependency-plugin.js +3 -5
- package/dist/service-portal/dependency-plugin.js.map +1 -1
- package/dist/service-portal/header-footer-plugin.js +3 -5
- package/dist/service-portal/header-footer-plugin.js.map +1 -1
- package/dist/service-portal/menu-plugin.js +0 -1
- package/dist/service-portal/menu-plugin.js.map +1 -1
- package/dist/service-portal/page-plugin.js +0 -1
- package/dist/service-portal/page-plugin.js.map +1 -1
- package/dist/service-portal/page-route-map-plugin.js +0 -1
- package/dist/service-portal/page-route-map-plugin.js.map +1 -1
- package/dist/service-portal/portal-plugin.js +0 -2
- package/dist/service-portal/portal-plugin.js.map +1 -1
- package/dist/service-portal/theme-plugin.js +0 -2
- package/dist/service-portal/theme-plugin.js.map +1 -1
- package/dist/service-portal/widget-plugin.js +3 -5
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/sla-plugin.js +0 -2
- package/dist/sla-plugin.js.map +1 -1
- package/dist/static-content-plugin.js +32 -3
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +102 -11
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-action-plugin.js +26 -17
- package/dist/ui-action-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +159 -17
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/ui-policy-plugin.js +0 -1
- package/dist/ui-policy-plugin.js.map +1 -1
- package/dist/user-preference-plugin.js +0 -2
- package/dist/user-preference-plugin.js.map +1 -1
- package/dist/utils.d.ts +1 -9
- package/dist/utils.js +0 -14
- package/dist/utils.js.map +1 -1
- package/dist/ux-list-menu-config-plugin.js +0 -2
- package/dist/ux-list-menu-config-plugin.js.map +1 -1
- package/dist/view-plugin.js +0 -1
- package/dist/view-plugin.js.map +1 -1
- package/dist/workspace-plugin.js +0 -2
- package/dist/workspace-plugin.js.map +1 -1
- package/package.json +6 -6
- package/src/acl-plugin.ts +1 -4
- package/src/applicability-plugin.ts +0 -2
- package/src/application-menu-plugin.ts +0 -2
- package/src/arrow-function-plugin.ts +0 -1
- package/src/atf/test-plugin.ts +0 -2
- package/src/basic-syntax-plugin.ts +0 -1
- package/src/business-rule-plugin.ts +1 -2
- package/src/call-expression-plugin.ts +0 -1
- package/src/claims-plugin.ts +0 -1
- package/src/client-script-plugin.ts +1 -2
- package/src/column-plugin.ts +29 -9
- package/src/cross-scope-privilege-plugin.ts +1 -2
- package/src/dashboard/dashboard-plugin.ts +0 -2
- package/src/data-plugin.ts +0 -1
- package/src/data-policy-plugin.ts +333 -0
- package/src/email-notification-plugin.ts +8 -4
- package/src/flow/flow-logic/flow-logic-constants.ts +6 -0
- package/src/flow/flow-logic/flow-logic-diagnostics.ts +236 -58
- package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +59 -6
- package/src/flow/flow-logic/flow-logic-plugin.ts +368 -38
- package/src/flow/flow-logic/flow-logic-shapes.ts +25 -0
- package/src/flow/plugins/approval-rules-plugin.ts +0 -1
- package/src/flow/plugins/flow-action-definition-plugin.ts +940 -208
- package/src/flow/plugins/flow-data-pill-plugin.ts +3 -5
- package/src/flow/plugins/flow-definition-plugin.ts +159 -26
- package/src/flow/plugins/flow-diagnostics-plugin.ts +89 -3
- package/src/flow/plugins/flow-instance-plugin.ts +26 -12
- package/src/flow/plugins/flow-trigger-instance-plugin.ts +0 -1
- package/src/flow/plugins/inline-script-plugin.ts +0 -1
- package/src/flow/plugins/step-definition-plugin.ts +0 -2
- package/src/flow/plugins/step-instance-plugin.ts +259 -65
- package/src/flow/plugins/trigger-plugin.ts +0 -2
- package/src/flow/plugins/wfa-datapill-plugin.ts +0 -1
- package/src/flow/utils/datapill-transformer.ts +13 -5
- package/src/flow/utils/flow-constants.ts +19 -1
- package/src/flow/utils/flow-io-to-record.ts +29 -19
- package/src/flow/utils/flow-pill-utils.ts +48 -0
- package/src/flow/utils/flow-stage-processor.ts +831 -0
- package/src/flow/utils/pill-string-parser.ts +29 -47
- package/src/flow/utils/utils.ts +39 -35
- package/src/form-plugin.ts +5 -15
- package/src/html-import-plugin.ts +0 -1
- package/src/import-sets-plugin.ts +0 -2
- package/src/inbound-email-action-plugin.ts +1 -2
- package/src/index.ts +7 -1
- package/src/instance-scan-plugin.ts +0 -7
- package/src/json-plugin.ts +0 -1
- package/src/list-plugin.ts +6 -2
- package/src/now-attach-plugin.ts +0 -1
- package/src/now-config-plugin.ts +0 -1
- package/src/now-id-plugin.ts +0 -1
- package/src/now-include-plugin.ts +0 -1
- package/src/now-ref-plugin.ts +0 -1
- package/src/now-unresolved-plugin.ts +0 -1
- package/src/package-json-plugin.ts +8 -3
- package/src/property-plugin.ts +0 -2
- package/src/record-plugin.ts +3 -3
- package/src/repack/lint/Rules.ts +1 -1
- package/src/rest-api-plugin.ts +7 -6
- package/src/role-plugin.ts +1 -2
- package/src/schedule-script/scheduled-script-plugin.ts +11 -5
- package/src/script-action-plugin.ts +0 -2
- package/src/script-include-plugin.ts +0 -4
- package/src/server-module-plugin/index.ts +2 -3
- package/src/service-catalog/catalog-clientscript-plugin.ts +0 -2
- package/src/service-catalog/catalog-item-plugin.ts +0 -2
- package/src/service-catalog/catalog-ui-policy-plugin.ts +0 -2
- package/src/service-catalog/sc-record-producer-plugin.ts +0 -2
- package/src/service-catalog/variable-set-plugin.ts +0 -2
- package/src/service-portal/angular-provider-plugin.ts +0 -2
- package/src/service-portal/dependency-plugin.ts +0 -2
- package/src/service-portal/header-footer-plugin.ts +0 -2
- package/src/service-portal/menu-plugin.ts +1 -2
- package/src/service-portal/page-plugin.ts +1 -2
- package/src/service-portal/page-route-map-plugin.ts +1 -2
- package/src/service-portal/portal-plugin.ts +0 -2
- package/src/service-portal/theme-plugin.ts +0 -2
- package/src/service-portal/widget-plugin.ts +0 -2
- package/src/sla-plugin.ts +0 -2
- package/src/static-content-plugin.ts +37 -4
- package/src/table-plugin.ts +118 -16
- package/src/ui-action-plugin.ts +30 -17
- package/src/ui-page-plugin.ts +188 -20
- package/src/ui-policy-plugin.ts +1 -2
- package/src/user-preference-plugin.ts +0 -2
- package/src/utils.ts +0 -15
- package/src/ux-list-menu-config-plugin.ts +0 -2
- package/src/view-plugin.ts +0 -1
- package/src/workspace-plugin.ts +0 -2
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { CallExpressionShape, Plugin, isSNScope, type Record as SdkRecord, Shape } from '@servicenow/sdk-build-core'
|
|
2
|
+
import { NowIdShape } from './now-id-plugin'
|
|
3
|
+
|
|
4
|
+
// Define table names as constants
|
|
5
|
+
const SYS_DATA_POLICY = 'sys_data_policy2'
|
|
6
|
+
const SYS_DATA_POLICY_RULE = 'sys_data_policy_rule'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Type for data policy rule configuration (internal plugin representation)
|
|
10
|
+
*/
|
|
11
|
+
type DataPolicyRuleInternal = {
|
|
12
|
+
$id?: NowIdShape
|
|
13
|
+
mandatory?: boolean | 'ignore'
|
|
14
|
+
readOnly?: boolean | 'ignore'
|
|
15
|
+
table?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Most-restrictive-wins merge for a single boolean|'ignore' property.
|
|
20
|
+
* Priority: true > false > 'ignore' / undefined
|
|
21
|
+
*/
|
|
22
|
+
const mergeRestrictive = (
|
|
23
|
+
existing: boolean | 'ignore' | undefined,
|
|
24
|
+
incoming: boolean | 'ignore' | undefined
|
|
25
|
+
): boolean | 'ignore' | undefined => {
|
|
26
|
+
if (incoming === true || existing === true) {
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
if (incoming === false || existing === false) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
return incoming ?? existing
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Converts ServiceNow string format ('true'/'false'/'leave alone') to boolean | 'ignore'
|
|
37
|
+
* Used when reading from database (toShape)
|
|
38
|
+
*/
|
|
39
|
+
const stringToBoolean = (value?: string): boolean | 'ignore' | undefined => {
|
|
40
|
+
switch (value) {
|
|
41
|
+
case 'true':
|
|
42
|
+
return true
|
|
43
|
+
case 'false':
|
|
44
|
+
return false
|
|
45
|
+
case 'leave alone':
|
|
46
|
+
return 'ignore'
|
|
47
|
+
default:
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Converts boolean | 'ignore' to ServiceNow string format ('true'/'false'/'leave alone')
|
|
54
|
+
* Used when writing to database (toRecord)
|
|
55
|
+
* Default: 'leave alone' - matches ServiceNow platform default for null/undefined values
|
|
56
|
+
*/
|
|
57
|
+
const booleanToString = (value: Shape): string => {
|
|
58
|
+
if (value.isBoolean()) {
|
|
59
|
+
return value.getValue() ? 'true' : 'false'
|
|
60
|
+
}
|
|
61
|
+
// Default to 'leave alone' when value is 'ignore' or undefined
|
|
62
|
+
return 'leave alone'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Checks if a value is a valid rule property (boolean or 'ignore')
|
|
67
|
+
* Used for validation in toRecord
|
|
68
|
+
*/
|
|
69
|
+
const isValidRuleValue = (value: Shape): boolean => {
|
|
70
|
+
return value.isBoolean() || (value.isString() && value.getValue() === 'ignore')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const DataPolicyPlugin = Plugin.create({
|
|
74
|
+
name: 'DataPolicyPlugin',
|
|
75
|
+
records: {
|
|
76
|
+
[SYS_DATA_POLICY]: {
|
|
77
|
+
relationships: {
|
|
78
|
+
[SYS_DATA_POLICY_RULE]: {
|
|
79
|
+
via: 'sys_data_policy',
|
|
80
|
+
descendant: true,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
toShape(record, { descendants, logger }) {
|
|
84
|
+
const rulesRecord: Record<string, DataPolicyRuleInternal> = {}
|
|
85
|
+
const parentTable = record.get('model_table')?.ifString()?.getValue()
|
|
86
|
+
|
|
87
|
+
descendants.query(SYS_DATA_POLICY_RULE).forEach((rule) => {
|
|
88
|
+
const fieldNameValue = rule.get('field')?.ifString()?.getValue()
|
|
89
|
+
if (!fieldNameValue) {
|
|
90
|
+
return // Skip rules without a field name
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Convert ServiceNow string format to boolean | 'ignore'
|
|
94
|
+
const mandatoryValue = rule.get('mandatory')?.ifString()?.getValue()
|
|
95
|
+
const readOnlyValue = rule.get('disabled')?.ifString()?.getValue()
|
|
96
|
+
|
|
97
|
+
const mandatory = stringToBoolean(mandatoryValue)
|
|
98
|
+
const readOnly = stringToBoolean(readOnlyValue)
|
|
99
|
+
|
|
100
|
+
if (rulesRecord[fieldNameValue]) {
|
|
101
|
+
// Multiple rule records for the same field — most restrictive wins.
|
|
102
|
+
// Priority: true > false > 'ignore' / undefined
|
|
103
|
+
// WARNING: Only the first rule's $id is kept in Fluent. The duplicate rule
|
|
104
|
+
// record (this one) will NOT be deleted from the instance on next deploy.
|
|
105
|
+
// Clean up orphan rule records manually on the instance.
|
|
106
|
+
logger.warn(
|
|
107
|
+
`Data Policy '${record.getId().getValue()}': field '${fieldNameValue}' has multiple rule records on the instance. ` +
|
|
108
|
+
`Merging values using most-restrictive-wins. The duplicate rule '${rule.getId().getValue()}' will NOT be deleted from the instance — remove it manually.`
|
|
109
|
+
)
|
|
110
|
+
const existing = rulesRecord[fieldNameValue]
|
|
111
|
+
const mergedMandatory = mergeRestrictive(existing.mandatory, mandatory)
|
|
112
|
+
const mergedReadOnly = mergeRestrictive(existing.readOnly, readOnly)
|
|
113
|
+
|
|
114
|
+
// Handle mutual exclusivity with priority rules:
|
|
115
|
+
// 1. mandatory=true beats readOnly=true → set mandatory=true, readOnly=false
|
|
116
|
+
// 2. readOnly=true beats mandatory=false → set readOnly=true, remove mandatory
|
|
117
|
+
// 3. mandatory=false beats readOnly=false → set mandatory=false, remove readOnly
|
|
118
|
+
let finalMandatory = mergedMandatory
|
|
119
|
+
let finalReadOnly = mergedReadOnly
|
|
120
|
+
|
|
121
|
+
if (mergedMandatory === true && mergedReadOnly === true) {
|
|
122
|
+
// Case 1: mandatory=true wins over readOnly=true
|
|
123
|
+
finalReadOnly = false
|
|
124
|
+
} else if (mergedReadOnly === true && mergedMandatory === false) {
|
|
125
|
+
// Case 2: readOnly=true wins over mandatory=false
|
|
126
|
+
finalMandatory = 'ignore'
|
|
127
|
+
} else if (mergedMandatory === false && mergedReadOnly === false) {
|
|
128
|
+
// Case 3: mandatory=false wins over readOnly=false
|
|
129
|
+
finalReadOnly = 'ignore'
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (finalMandatory !== undefined && finalMandatory !== 'ignore') {
|
|
133
|
+
existing.mandatory = finalMandatory
|
|
134
|
+
} else {
|
|
135
|
+
delete existing.mandatory
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (finalReadOnly !== undefined && finalReadOnly !== 'ignore') {
|
|
139
|
+
existing.readOnly = finalReadOnly
|
|
140
|
+
} else {
|
|
141
|
+
delete existing.readOnly
|
|
142
|
+
}
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ruleConfig: DataPolicyRuleInternal = {
|
|
147
|
+
$id: NowIdShape.from(rule),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Only include properties with actual boolean values (not 'ignore')
|
|
151
|
+
// Omitting a property is equivalent to 'ignore' - cleaner generated code
|
|
152
|
+
if (mandatory !== undefined && mandatory !== 'ignore') {
|
|
153
|
+
ruleConfig.mandatory = mandatory
|
|
154
|
+
}
|
|
155
|
+
if (readOnly !== undefined && readOnly !== 'ignore') {
|
|
156
|
+
ruleConfig.readOnly = readOnly
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Add table field if it's different from parent policy's table
|
|
160
|
+
const table = rule.get('table')?.ifString()?.getValue()
|
|
161
|
+
if (table && table !== parentTable) {
|
|
162
|
+
ruleConfig.table = table
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
rulesRecord[fieldNameValue] = ruleConfig
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
value: new CallExpressionShape({
|
|
171
|
+
source: record,
|
|
172
|
+
callee: 'DataPolicy',
|
|
173
|
+
args: [
|
|
174
|
+
record.transform(({ $ }) => ({
|
|
175
|
+
$id: $.val(NowIdShape.from(record)),
|
|
176
|
+
table: $.from('model_table').def(''),
|
|
177
|
+
shortDescription: $.from('short_description').def(''),
|
|
178
|
+
active: $.toBoolean().def(true),
|
|
179
|
+
applyToImportSets: $.from('apply_import_set').toBoolean().def(true),
|
|
180
|
+
applyToSOAP: $.from('apply_soap').toBoolean().def(true),
|
|
181
|
+
conditions: $.def(''),
|
|
182
|
+
description: $.def(''),
|
|
183
|
+
useAsUiPolicyOnClient: $.from('enforce_ui').toBoolean().def(true),
|
|
184
|
+
inherit: $.toBoolean().def(false),
|
|
185
|
+
modelId: $.from('model_id').def(''),
|
|
186
|
+
reverseIfFalse: $.from('reverse_if_false').toBoolean().def(true),
|
|
187
|
+
rules: $.val(Object.keys(rulesRecord).length > 0 ? rulesRecord : undefined),
|
|
188
|
+
})),
|
|
189
|
+
],
|
|
190
|
+
}),
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
[SYS_DATA_POLICY_RULE]: {},
|
|
195
|
+
},
|
|
196
|
+
shapes: [
|
|
197
|
+
{
|
|
198
|
+
shape: CallExpressionShape,
|
|
199
|
+
fileTypes: ['fluent'],
|
|
200
|
+
async toRecord(callExpression, { config, factory, diagnostics }) {
|
|
201
|
+
if (callExpression.getCallee() !== 'DataPolicy') {
|
|
202
|
+
return { success: false }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const arg = callExpression.getArgument(0).asObject()
|
|
206
|
+
|
|
207
|
+
// Validate table scope prefix for scoped apps
|
|
208
|
+
const tableValue = arg.get('table')
|
|
209
|
+
if (tableValue?.isString() && config?.scope) {
|
|
210
|
+
const tableName = tableValue.getValue()
|
|
211
|
+
const scopeName = config.scope
|
|
212
|
+
//Note: If scope is sn_ or global, this validation will not work as expected
|
|
213
|
+
if (!tableName.startsWith(`${scopeName}_`) && !isSNScope(scopeName) && scopeName !== 'global') {
|
|
214
|
+
diagnostics.error(
|
|
215
|
+
arg.get('table'),
|
|
216
|
+
`'table' property should start with scope prefix '${scopeName}_'. Data Policies in scoped apps can only be created for tables within the same scope.`
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Create the Data Policy record
|
|
222
|
+
const policyRecord = await factory.createRecord({
|
|
223
|
+
source: callExpression,
|
|
224
|
+
table: SYS_DATA_POLICY,
|
|
225
|
+
explicitId: arg.get('$id'),
|
|
226
|
+
properties: arg.transform(({ $ }) => ({
|
|
227
|
+
model_table: $.from('table').def(''),
|
|
228
|
+
short_description: $.from('shortDescription').def(''),
|
|
229
|
+
active: $.from('active').def(true),
|
|
230
|
+
apply_import_set: $.from('applyToImportSets').def(true),
|
|
231
|
+
apply_soap: $.from('applyToSOAP').def(true),
|
|
232
|
+
conditions: $.from('conditions').def(''),
|
|
233
|
+
description: $.from('description').def(''),
|
|
234
|
+
enforce_ui: $.from('useAsUiPolicyOnClient').def(true),
|
|
235
|
+
inherit: $.from('inherit').def(false),
|
|
236
|
+
model_id: $.from('modelId').def(''),
|
|
237
|
+
reverse_if_false: $.from('reverseIfFalse').def(true),
|
|
238
|
+
})),
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// Process rules if they exist (Record structure)
|
|
242
|
+
const rules = arg.get('rules')
|
|
243
|
+
const ruleRecords: SdkRecord[] = []
|
|
244
|
+
|
|
245
|
+
if (!rules?.isObject()) {
|
|
246
|
+
return { success: true, value: policyRecord }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const ruleKeys = rules.keys()
|
|
250
|
+
|
|
251
|
+
// Process each field name as a key in the rules record
|
|
252
|
+
for (const fieldName of ruleKeys) {
|
|
253
|
+
const ruleConfig = rules.get(fieldName)
|
|
254
|
+
if (!ruleConfig?.isObject()) {
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Get rule property values
|
|
259
|
+
let mandatoryValue = ruleConfig.get('mandatory')
|
|
260
|
+
let readOnlyValue = ruleConfig.get('readOnly')
|
|
261
|
+
|
|
262
|
+
const hasMandatoryProp = isValidRuleValue(mandatoryValue)
|
|
263
|
+
const hasReadOnlyProp = isValidRuleValue(readOnlyValue)
|
|
264
|
+
|
|
265
|
+
// If no valid properties, default both to 'ignore' instead of throwing error
|
|
266
|
+
// This creates a rule record with both fields set to 'leave alone' in the database
|
|
267
|
+
if (!hasMandatoryProp && !hasReadOnlyProp) {
|
|
268
|
+
mandatoryValue = Shape.from(ruleConfig, 'ignore')
|
|
269
|
+
readOnlyValue = Shape.from(ruleConfig, 'ignore')
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Validate mutual exclusivity: mandatory and readOnly cannot both be true
|
|
273
|
+
if (
|
|
274
|
+
mandatoryValue?.isBoolean() &&
|
|
275
|
+
mandatoryValue.getValue() === true &&
|
|
276
|
+
readOnlyValue?.isBoolean() &&
|
|
277
|
+
readOnlyValue.getValue() === true
|
|
278
|
+
) {
|
|
279
|
+
diagnostics.error(
|
|
280
|
+
ruleConfig,
|
|
281
|
+
`Rule for field '${fieldName}': mandatory and readOnly cannot both be true - these are mutually exclusive`
|
|
282
|
+
)
|
|
283
|
+
continue
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Validate mandatory field is not set for tables outside the current scope
|
|
287
|
+
const ruleTableValue = ruleConfig.get('table')
|
|
288
|
+
if (
|
|
289
|
+
mandatoryValue?.isBoolean() &&
|
|
290
|
+
mandatoryValue.getValue() === true &&
|
|
291
|
+
ruleTableValue?.isString() &&
|
|
292
|
+
config?.scope
|
|
293
|
+
) {
|
|
294
|
+
const ruleTableName = ruleTableValue.getValue()
|
|
295
|
+
const scopeName = config.scope
|
|
296
|
+
const policyTableName = arg.get('table')?.ifString()?.getValue() ?? ''
|
|
297
|
+
|
|
298
|
+
// Check if rule table is different from policy table and outside scope
|
|
299
|
+
if (
|
|
300
|
+
ruleTableName !== policyTableName &&
|
|
301
|
+
!ruleTableName.startsWith(`${scopeName}_`) &&
|
|
302
|
+
!isSNScope(scopeName) &&
|
|
303
|
+
scopeName !== 'global'
|
|
304
|
+
) {
|
|
305
|
+
diagnostics.error(
|
|
306
|
+
ruleConfig.get('table') || ruleConfig,
|
|
307
|
+
`Rule for field '${fieldName}': cannot set mandatory=true for table '${ruleTableName}' which is outside the current scope '${scopeName}'. For tables in a different scope than the data policy record, you cannot make a field mandatory.`
|
|
308
|
+
)
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const ruleRecord = await factory.createRecord({
|
|
314
|
+
source: ruleConfig,
|
|
315
|
+
table: SYS_DATA_POLICY_RULE,
|
|
316
|
+
explicitId: ruleConfig.get('$id'),
|
|
317
|
+
properties: ruleConfig.transform(({ $ }) => ({
|
|
318
|
+
sys_data_policy: $.val(policyRecord.getId()),
|
|
319
|
+
table: $.from('table').def(arg.get('table')?.ifString()?.getValue() ?? ''),
|
|
320
|
+
field: $.val(fieldName),
|
|
321
|
+
mandatory: $.val(booleanToString(mandatoryValue)),
|
|
322
|
+
disabled: $.val(booleanToString(readOnlyValue)),
|
|
323
|
+
})),
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
ruleRecords.push(ruleRecord)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return { success: true, value: policyRecord.with(...ruleRecords) }
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CallExpressionShape, Plugin, isGUID, Shape, type ObjectShape } from '@servicenow/sdk-build-core'
|
|
2
2
|
import { NowIdShape } from './now-id-plugin'
|
|
3
|
-
import { showGuidFieldDiagnostic
|
|
3
|
+
import { showGuidFieldDiagnostic } from './utils'
|
|
4
4
|
import type { EmailNotification } from '@servicenow/sdk-core/runtime/notification'
|
|
5
5
|
|
|
6
6
|
// Use index access types instead of exported utility types
|
|
@@ -55,7 +55,6 @@ const isValidEmail = (email: string): boolean => {
|
|
|
55
55
|
|
|
56
56
|
export const EmailNotificationPlugin = Plugin.create({
|
|
57
57
|
name: 'EmailNotificationPlugin',
|
|
58
|
-
docs: [createSdkDocEntry('EmailNotification', ['sysevent_email_action'])],
|
|
59
58
|
records: {
|
|
60
59
|
sysevent_email_action: {
|
|
61
60
|
toShape(record) {
|
|
@@ -555,7 +554,7 @@ export const EmailNotificationPlugin = Plugin.create({
|
|
|
555
554
|
}
|
|
556
555
|
}
|
|
557
556
|
|
|
558
|
-
// Handle style field - can be string (GUID) or Record<'
|
|
557
|
+
// Handle style field - can be string (GUID) or Record<'sysevent_email_style'>
|
|
559
558
|
let styleValue: string | undefined
|
|
560
559
|
if (emailContent) {
|
|
561
560
|
const styleArg = emailContent.get('style')
|
|
@@ -569,7 +568,12 @@ export const EmailNotificationPlugin = Plugin.create({
|
|
|
569
568
|
if (isGUID(styleStr)) {
|
|
570
569
|
styleValue = styleStr
|
|
571
570
|
} else {
|
|
572
|
-
showGuidFieldDiagnostic(
|
|
571
|
+
showGuidFieldDiagnostic(
|
|
572
|
+
styleArg,
|
|
573
|
+
'emailContent.style',
|
|
574
|
+
'sysevent_email_style',
|
|
575
|
+
diagnostics
|
|
576
|
+
)
|
|
573
577
|
}
|
|
574
578
|
}
|
|
575
579
|
}
|
|
@@ -13,6 +13,7 @@ export enum FLOW_LOGIC {
|
|
|
13
13
|
TRY_CATCH = 'wfa.flowLogic.tryCatch',
|
|
14
14
|
DO_IN_PARALLEL = 'wfa.flowLogic.doInParallel',
|
|
15
15
|
SET_FLOW_VARIABLES = 'wfa.flowLogic.setFlowVariables',
|
|
16
|
+
APPEND_TO_FLOW_VARIABLES = 'wfa.flowLogic.appendToFlowVariables',
|
|
16
17
|
ASSIGN_SUBFLOW_OUTPUTS = 'wfa.flowLogic.assignSubflowOutputs',
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -101,7 +102,10 @@ export const FLOW_LOGIC_SYS_ID_MAP: { [K in FLOW_LOGIC]?: string } = {
|
|
|
101
102
|
[FLOW_LOGIC.ENDFLOW]: 'd176605ea76103004f27b0d2187901c7',
|
|
102
103
|
[FLOW_LOGIC.SKIP_ITERATION]: '48de1b5837063110ac85cdd734924b15',
|
|
103
104
|
[FLOW_LOGIC.FOR_EACH]: '098e1dc5c3e232002841b63b12d3ae33',
|
|
105
|
+
[FLOW_LOGIC.TRY_CATCH]: '07223332de022010ec58424b73829721',
|
|
106
|
+
[FLOW_LOGIC.DO_IN_PARALLEL]: '5d31e5dedfe50300b61ecae83df26326',
|
|
104
107
|
[FLOW_LOGIC.SET_FLOW_VARIABLES]: '4f787d1e0f9b0010ecf0cc52ff767ea0',
|
|
108
|
+
[FLOW_LOGIC.APPEND_TO_FLOW_VARIABLES]: '4784e82683031210d266f953d22bc0fd',
|
|
105
109
|
[FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS]: 'df4e1945c3e232002841b63b12d3ae3e',
|
|
106
110
|
}
|
|
107
111
|
|
|
@@ -118,3 +122,5 @@ export namespace FlowLogicSysId {
|
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
export const DO_IN_PARALLEL_BLOCK_SYS_ID = 'a1168de173311300b61e66476bf6a7d0'
|
|
125
|
+
|
|
126
|
+
export const TRY_CATCH_CATCH_SYS_ID = '35d60003e6022010a5e40cdd1254dd21'
|