@servicenow/sdk-build-plugins 4.4.1 → 4.5.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 +54 -4
- package/dist/acl-plugin.js.map +1 -1
- package/dist/applicability-plugin.js +2 -0
- package/dist/applicability-plugin.js.map +1 -1
- package/dist/application-menu-plugin.js +2 -0
- package/dist/application-menu-plugin.js.map +1 -1
- package/dist/arrow-function-plugin.d.ts +6 -1
- package/dist/arrow-function-plugin.js +105 -12
- package/dist/arrow-function-plugin.js.map +1 -1
- package/dist/atf/test-plugin.js +2 -0
- package/dist/atf/test-plugin.js.map +1 -1
- package/dist/basic-syntax-plugin.js +20 -0
- package/dist/basic-syntax-plugin.js.map +1 -1
- package/dist/call-expression-plugin.js +1 -0
- package/dist/call-expression-plugin.js.map +1 -1
- package/dist/claims-plugin.js +1 -0
- package/dist/claims-plugin.js.map +1 -1
- package/dist/client-script-plugin.js +1 -0
- package/dist/client-script-plugin.js.map +1 -1
- package/dist/column-plugin.js +1 -0
- package/dist/column-plugin.js.map +1 -1
- package/dist/cross-scope-privilege-plugin.js +1 -0
- package/dist/cross-scope-privilege-plugin.js.map +1 -1
- package/dist/dashboard/dashboard-plugin.js +2 -0
- package/dist/dashboard/dashboard-plugin.js.map +1 -1
- package/dist/data-plugin.js +1 -0
- package/dist/data-plugin.js.map +1 -1
- package/dist/email-notification-plugin.js +9 -13
- package/dist/email-notification-plugin.js.map +1 -1
- package/dist/flow/constants/flow-plugin-constants.d.ts +1 -1
- package/dist/flow/constants/flow-plugin-constants.js +1 -1
- package/dist/flow/constants/flow-plugin-constants.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +82 -2
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +48 -40
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin.js +1 -0
- package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
- package/dist/flow/plugins/approval-rules-plugin.js +1 -0
- package/dist/flow/plugins/approval-rules-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +4 -2
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-data-pill-plugin.js +1 -0
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +8 -3
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-diagnostics-plugin.js +1 -0
- package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-instance-plugin.js +68 -12
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-trigger-instance-plugin.js +1 -0
- package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/inline-script-plugin.js +1 -0
- package/dist/flow/plugins/inline-script-plugin.js.map +1 -1
- package/dist/flow/plugins/step-definition-plugin.js +3 -2
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.js +1 -0
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/trigger-plugin.js +2 -0
- package/dist/flow/plugins/trigger-plugin.js.map +1 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js +1 -0
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
- package/dist/flow/post-install.d.ts +2 -0
- package/dist/flow/post-install.js +58 -0
- package/dist/flow/post-install.js.map +1 -0
- package/dist/flow/utils/complex-objects.js +4 -2
- package/dist/flow/utils/complex-objects.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +24 -0
- package/dist/flow/utils/flow-constants.js +29 -2
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/flow-to-xml.d.ts +3 -2
- package/dist/flow/utils/flow-to-xml.js +3 -4
- package/dist/flow/utils/flow-to-xml.js.map +1 -1
- package/dist/flow/utils/label-cache-processor.d.ts +5 -0
- package/dist/flow/utils/label-cache-processor.js +14 -2
- package/dist/flow/utils/label-cache-processor.js.map +1 -1
- package/dist/flow/utils/service-catalog.js +5 -1
- package/dist/flow/utils/service-catalog.js.map +1 -1
- package/dist/form-plugin.d.ts +2 -0
- package/dist/form-plugin.js +1134 -0
- package/dist/form-plugin.js.map +1 -0
- package/dist/html-import-plugin.js +1 -0
- package/dist/html-import-plugin.js.map +1 -1
- package/dist/import-sets-plugin.js +2 -0
- package/dist/import-sets-plugin.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/instance-scan-plugin.d.ts +2 -0
- package/dist/instance-scan-plugin.js +298 -0
- package/dist/instance-scan-plugin.js.map +1 -0
- package/dist/json-plugin.js +1 -0
- package/dist/json-plugin.js.map +1 -1
- package/dist/list-plugin.js +1 -0
- package/dist/list-plugin.js.map +1 -1
- package/dist/now-attach-plugin.js +1 -0
- package/dist/now-attach-plugin.js.map +1 -1
- package/dist/now-config-plugin.js +659 -51
- package/dist/now-config-plugin.js.map +1 -1
- package/dist/now-id-plugin.js +1 -0
- package/dist/now-id-plugin.js.map +1 -1
- package/dist/now-include-plugin.js +1 -0
- package/dist/now-include-plugin.js.map +1 -1
- package/dist/now-ref-plugin.js +1 -0
- package/dist/now-ref-plugin.js.map +1 -1
- package/dist/now-unresolved-plugin.js +1 -0
- package/dist/now-unresolved-plugin.js.map +1 -1
- package/dist/package-json-plugin.js +1 -0
- package/dist/package-json-plugin.js.map +1 -1
- package/dist/property-plugin.js +3 -1
- package/dist/property-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +30 -0
- package/dist/record-plugin.js +37 -1
- package/dist/record-plugin.js.map +1 -1
- package/dist/repack/lint/Rules.d.ts +11 -2
- package/dist/repack/lint/Rules.js +160 -16
- package/dist/repack/lint/Rules.js.map +1 -1
- package/dist/repack/lint/index.d.ts +10 -5
- package/dist/repack/lint/index.js +76 -50
- package/dist/repack/lint/index.js.map +1 -1
- package/dist/rest-api-plugin.js +14 -0
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/role-plugin.js +1 -0
- package/dist/role-plugin.js.map +1 -1
- package/dist/schedule-script/index.d.ts +1 -0
- package/dist/schedule-script/index.js +18 -0
- package/dist/schedule-script/index.js.map +1 -0
- package/dist/schedule-script/scheduled-script-plugin.d.ts +2 -0
- package/dist/schedule-script/scheduled-script-plugin.js +551 -0
- package/dist/schedule-script/scheduled-script-plugin.js.map +1 -0
- package/dist/schedule-script/timeZoneConverter.d.ts +61 -0
- package/dist/schedule-script/timeZoneConverter.js +170 -0
- package/dist/schedule-script/timeZoneConverter.js.map +1 -0
- package/dist/script-action-plugin.js +2 -0
- package/dist/script-action-plugin.js.map +1 -1
- package/dist/script-include-plugin.js +2 -0
- package/dist/script-include-plugin.js.map +1 -1
- package/dist/server-module-plugin/index.js +13 -2
- package/dist/server-module-plugin/index.js.map +1 -1
- package/dist/service-catalog/catalog-clientscript-plugin.js +2 -0
- package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
- package/dist/service-catalog/catalog-item-plugin.js +2 -0
- package/dist/service-catalog/catalog-item-plugin.js.map +1 -1
- package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -0
- package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
- package/dist/service-catalog/sc-record-producer-plugin.js +2 -0
- package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
- package/dist/service-catalog/service-catalog-diagnostics.d.ts +6 -0
- package/dist/service-catalog/service-catalog-diagnostics.js +20 -0
- package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -1
- package/dist/service-catalog/shape-to-record.js +7 -2
- package/dist/service-catalog/shape-to-record.js.map +1 -1
- package/dist/service-catalog/variable-set-plugin.js +2 -0
- package/dist/service-catalog/variable-set-plugin.js.map +1 -1
- package/dist/service-portal/angular-provider-plugin.js +2 -0
- package/dist/service-portal/angular-provider-plugin.js.map +1 -1
- package/dist/service-portal/dependency-plugin.js +5 -31
- package/dist/service-portal/dependency-plugin.js.map +1 -1
- package/dist/service-portal/menu-plugin.d.ts +2 -0
- package/dist/service-portal/menu-plugin.js +353 -0
- package/dist/service-portal/menu-plugin.js.map +1 -0
- package/dist/service-portal/page-plugin.d.ts +2 -0
- package/dist/service-portal/page-plugin.js +702 -0
- package/dist/service-portal/page-plugin.js.map +1 -0
- package/dist/service-portal/portal-plugin.d.ts +2 -0
- package/dist/service-portal/portal-plugin.js +296 -0
- package/dist/service-portal/portal-plugin.js.map +1 -0
- package/dist/service-portal/theme-plugin.d.ts +2 -0
- package/dist/service-portal/theme-plugin.js +112 -0
- package/dist/service-portal/theme-plugin.js.map +1 -0
- package/dist/service-portal/utils.d.ts +8 -0
- package/dist/service-portal/utils.js +50 -0
- package/dist/service-portal/utils.js.map +1 -0
- package/dist/service-portal/widget-plugin.js +45 -8
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/sla-plugin.js +2 -0
- package/dist/sla-plugin.js.map +1 -1
- package/dist/static-content-plugin.js +1 -0
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +1 -0
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-action-plugin.js +2 -0
- package/dist/ui-action-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +33 -8
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/ui-policy-plugin.js +1 -0
- package/dist/ui-policy-plugin.js.map +1 -1
- package/dist/user-preference-plugin.js +2 -0
- package/dist/user-preference-plugin.js.map +1 -1
- package/dist/utils.d.ts +20 -2
- package/dist/utils.js +34 -3
- package/dist/utils.js.map +1 -1
- package/dist/ux-list-menu-config-plugin.js +2 -0
- package/dist/ux-list-menu-config-plugin.js.map +1 -1
- package/dist/view-plugin.js +1 -0
- package/dist/view-plugin.js.map +1 -1
- package/dist/workspace-plugin.js +2 -0
- package/dist/workspace-plugin.js.map +1 -1
- package/package.json +10 -11
- package/src/_types/eslint-community-eslint-utils.d.ts +15 -0
- package/src/acl-plugin.ts +97 -8
- package/src/applicability-plugin.ts +2 -0
- package/src/application-menu-plugin.ts +2 -0
- package/src/arrow-function-plugin.ts +128 -13
- package/src/atf/test-plugin.ts +2 -0
- package/src/basic-syntax-plugin.ts +21 -0
- package/src/call-expression-plugin.ts +1 -0
- package/src/claims-plugin.ts +1 -0
- package/src/client-script-plugin.ts +2 -1
- package/src/column-plugin.ts +1 -0
- package/src/cross-scope-privilege-plugin.ts +2 -1
- package/src/dashboard/dashboard-plugin.ts +2 -0
- package/src/data-plugin.ts +1 -0
- package/src/email-notification-plugin.ts +3 -23
- package/src/flow/constants/flow-plugin-constants.ts +1 -1
- package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +47 -45
- package/src/flow/flow-logic/flow-logic-plugin.ts +1 -0
- package/src/flow/plugins/approval-rules-plugin.ts +1 -0
- package/src/flow/plugins/flow-action-definition-plugin.ts +4 -2
- package/src/flow/plugins/flow-data-pill-plugin.ts +1 -0
- package/src/flow/plugins/flow-definition-plugin.ts +10 -4
- package/src/flow/plugins/flow-diagnostics-plugin.ts +1 -0
- package/src/flow/plugins/flow-instance-plugin.ts +103 -14
- package/src/flow/plugins/flow-trigger-instance-plugin.ts +1 -0
- package/src/flow/plugins/inline-script-plugin.ts +1 -0
- package/src/flow/plugins/step-definition-plugin.ts +3 -2
- package/src/flow/plugins/step-instance-plugin.ts +1 -0
- package/src/flow/plugins/trigger-plugin.ts +2 -0
- package/src/flow/plugins/wfa-datapill-plugin.ts +1 -0
- package/src/flow/post-install.ts +92 -0
- package/src/flow/utils/complex-objects.ts +10 -2
- package/src/flow/utils/flow-constants.ts +30 -1
- package/src/flow/utils/flow-to-xml.ts +4 -4
- package/src/flow/utils/label-cache-processor.ts +14 -2
- package/src/flow/utils/service-catalog.ts +5 -2
- package/src/form-plugin.ts +1411 -0
- package/src/html-import-plugin.ts +1 -0
- package/src/import-sets-plugin.ts +2 -0
- package/src/index.ts +9 -0
- package/src/instance-scan-plugin.ts +318 -0
- package/src/json-plugin.ts +1 -0
- package/src/list-plugin.ts +2 -1
- package/src/now-attach-plugin.ts +1 -0
- package/src/now-config-plugin.ts +833 -53
- package/src/now-id-plugin.ts +1 -0
- package/src/now-include-plugin.ts +1 -0
- package/src/now-ref-plugin.ts +1 -0
- package/src/now-unresolved-plugin.ts +1 -0
- package/src/package-json-plugin.ts +1 -0
- package/src/property-plugin.ts +3 -1
- package/src/record-plugin.ts +42 -2
- package/src/repack/lint/Rules.ts +171 -22
- package/src/repack/lint/index.ts +80 -56
- package/src/rest-api-plugin.ts +21 -1
- package/src/role-plugin.ts +2 -1
- package/src/schedule-script/index.ts +1 -0
- package/src/schedule-script/scheduled-script-plugin.ts +679 -0
- package/src/schedule-script/timeZoneConverter.ts +188 -0
- package/src/script-action-plugin.ts +2 -0
- package/src/script-include-plugin.ts +2 -0
- package/src/server-module-plugin/index.ts +14 -2
- package/src/service-catalog/catalog-clientscript-plugin.ts +2 -0
- package/src/service-catalog/catalog-item-plugin.ts +2 -0
- package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -0
- package/src/service-catalog/sc-record-producer-plugin.ts +2 -0
- package/src/service-catalog/service-catalog-diagnostics.ts +30 -0
- package/src/service-catalog/shape-to-record.ts +8 -2
- package/src/service-catalog/variable-set-plugin.ts +2 -0
- package/src/service-portal/angular-provider-plugin.ts +2 -0
- package/src/service-portal/dependency-plugin.ts +6 -53
- package/src/service-portal/menu-plugin.ts +435 -0
- package/src/service-portal/page-plugin.ts +830 -0
- package/src/service-portal/portal-plugin.ts +319 -0
- package/src/service-portal/theme-plugin.ts +135 -0
- package/src/service-portal/utils.ts +69 -0
- package/src/service-portal/widget-plugin.ts +79 -9
- package/src/sla-plugin.ts +2 -0
- package/src/static-content-plugin.ts +1 -0
- package/src/table-plugin.ts +2 -1
- package/src/ui-action-plugin.ts +2 -0
- package/src/ui-page-plugin.ts +34 -8
- package/src/ui-policy-plugin.ts +2 -1
- package/src/user-preference-plugin.ts +2 -0
- package/src/utils.ts +42 -2
- package/src/ux-list-menu-config-plugin.ts +2 -0
- package/src/view-plugin.ts +1 -0
- package/src/workspace-plugin.ts +2 -0
- package/src/_types/eslint-plugin-es-x.d.ts +0 -17
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CallExpressionShape,
|
|
3
|
+
Plugin,
|
|
4
|
+
type Factory,
|
|
5
|
+
//type Diagnostics,
|
|
6
|
+
type Record,
|
|
7
|
+
type RecordId,
|
|
8
|
+
type Shape,
|
|
9
|
+
type ArrayShape,
|
|
10
|
+
type ObjectShape,
|
|
11
|
+
} from '@servicenow/sdk-build-core'
|
|
12
|
+
import isEmpty from 'lodash/isEmpty'
|
|
13
|
+
import { toReference, getFieldAsNumber, createSdkDocEntry } from '../utils'
|
|
14
|
+
|
|
15
|
+
type Dict = { [key: string]: unknown }
|
|
16
|
+
|
|
17
|
+
const default_placeholder_dimensions = `{
|
|
18
|
+
"mobile": {
|
|
19
|
+
"height": "250px",
|
|
20
|
+
"width": "100%"
|
|
21
|
+
},
|
|
22
|
+
"desktop": {
|
|
23
|
+
"height": "250px",
|
|
24
|
+
"width": "100%"
|
|
25
|
+
},
|
|
26
|
+
"tablet": {
|
|
27
|
+
"height": "250px",
|
|
28
|
+
"width": "100%"
|
|
29
|
+
}
|
|
30
|
+
}`
|
|
31
|
+
|
|
32
|
+
const default_placeholder_template = `<!--
|
|
33
|
+
AngularJS template with configurable options.
|
|
34
|
+
Use the "options" object to control dynamic behavior.
|
|
35
|
+
Example: Display an element when max row count is 10:
|
|
36
|
+
<div ng-if="options.maxRowCount === 10"></div>
|
|
37
|
+
The "skeleton-container" class is used for loading placeholders.
|
|
38
|
+
-->
|
|
39
|
+
<div class="skeleton-container">
|
|
40
|
+
<!-- Header Skeleton -->
|
|
41
|
+
<div class="skeleton-box skeleton-header"></div>
|
|
42
|
+
<!-- Body Skeleton -->
|
|
43
|
+
<div class="skeleton-box skeleton-line"></div>
|
|
44
|
+
<div class="skeleton-box skeleton-line small"></div>
|
|
45
|
+
<div class="skeleton-box skeleton-line medium"></div>
|
|
46
|
+
</div>`
|
|
47
|
+
|
|
48
|
+
const default_placeholder_script = `function evaluateConfig(options) { return {
|
|
49
|
+
"mobile": {
|
|
50
|
+
"height": "250px",
|
|
51
|
+
"width": "100%"
|
|
52
|
+
},
|
|
53
|
+
"desktop": {
|
|
54
|
+
"height": "250px",
|
|
55
|
+
"width": "100%"
|
|
56
|
+
},
|
|
57
|
+
"tablet": {
|
|
58
|
+
"height": "250px",
|
|
59
|
+
"width": "100%"
|
|
60
|
+
}
|
|
61
|
+
}; }`
|
|
62
|
+
|
|
63
|
+
const defaultValues = {
|
|
64
|
+
container: {
|
|
65
|
+
width: 'container',
|
|
66
|
+
backgroundStyle: 'default',
|
|
67
|
+
subheader: false,
|
|
68
|
+
bootstrapAlt: false,
|
|
69
|
+
},
|
|
70
|
+
instance: {
|
|
71
|
+
active: true,
|
|
72
|
+
color: 'default',
|
|
73
|
+
size: 'md',
|
|
74
|
+
asyncLoadDeviceType: 'desktop,tablet,mobile',
|
|
75
|
+
},
|
|
76
|
+
column: {
|
|
77
|
+
size: 12,
|
|
78
|
+
},
|
|
79
|
+
page: {
|
|
80
|
+
category: 'custom',
|
|
81
|
+
useSeoScript: false,
|
|
82
|
+
shortDescription: '',
|
|
83
|
+
public: false,
|
|
84
|
+
draft: false,
|
|
85
|
+
omitWatcher: false,
|
|
86
|
+
internal: false,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Safely parse a size value from a Shape, handling non-numeric strings
|
|
92
|
+
* @param shape - The shape to extract the size from
|
|
93
|
+
* @param fieldName - The field name to extract (e.g., "size", "size_sm")
|
|
94
|
+
* @param defaultValue - Default value if parsing fails
|
|
95
|
+
* @returns Numeric size value or undefined
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Sort an array of shapes by their order field
|
|
100
|
+
* @param shapes - Array of shapes to sort
|
|
101
|
+
* @returns Sorted array
|
|
102
|
+
*/
|
|
103
|
+
function sortByOrder<T extends Record>(shapes: T[]): T[] {
|
|
104
|
+
return shapes.sort((a, b) => {
|
|
105
|
+
const aOrder = getFieldAsNumber(a, 'order', 1)
|
|
106
|
+
const bOrder = getFieldAsNumber(b, 'order', 1)
|
|
107
|
+
return (aOrder ?? 1) - (bOrder ?? 1)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Conditionally adds a property to an object if the value is not empty or undefined.
|
|
113
|
+
* Filters out undefined values, empty arrays, and empty strings to keep objects clean.
|
|
114
|
+
* @param obj - The target object to add the property to
|
|
115
|
+
* @param key - The property key to add
|
|
116
|
+
* @param value - The value to add (will be filtered if empty/undefined)
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
const addProperty = (obj: Dict, key: string, value: unknown, objectType?: keyof typeof defaultValues) => {
|
|
120
|
+
if (
|
|
121
|
+
value === undefined ||
|
|
122
|
+
(Array.isArray(value) && value.length === 0) ||
|
|
123
|
+
(typeof value === 'string' && value === '')
|
|
124
|
+
) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check if value matches default for this object type
|
|
129
|
+
if (objectType && defaultValues[objectType] && (defaultValues[objectType] as Dict)[key] === value) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
obj[key] = value
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates a container object from a ServiceNow sp_container record Shape.
|
|
137
|
+
* Transforms database fields to Fluent API format and includes nested rows.
|
|
138
|
+
* @param container - The sp_container record Shape
|
|
139
|
+
* @param rows - Array of row objects that belong to this container
|
|
140
|
+
* @returns Formatted container object or undefined if container is empty
|
|
141
|
+
*/
|
|
142
|
+
const getContainerObject = (container: Record, rows: object[]): Dict | undefined => {
|
|
143
|
+
if (!container || isEmpty(container)) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const name = container.get('name').ifString()?.getValue()
|
|
148
|
+
const width = container.get('width').ifString()?.getValue()
|
|
149
|
+
const backgroundStyle = container.get('background_style').ifString()?.getValue()
|
|
150
|
+
const backgroundColor = container.get('background_color').ifString()?.getValue()
|
|
151
|
+
const backgroundImage = container.get('background_image').ifString()?.getValue()
|
|
152
|
+
const cssClass = container.get('class_name').ifString()?.getValue()
|
|
153
|
+
const parentClass = container.get('container_class_name').ifString()?.getValue()
|
|
154
|
+
const subheader = container.get('subheader').toBoolean()?.getValue()
|
|
155
|
+
const bootstrapAlt = container.get('bootstrap_alt').toBoolean()?.getValue()
|
|
156
|
+
const semanticTag = container.get('semantic_tag').ifString()?.getValue()
|
|
157
|
+
const title = container.get('title').ifString()?.getValue()
|
|
158
|
+
|
|
159
|
+
const containerObject: Dict = {}
|
|
160
|
+
|
|
161
|
+
addProperty(containerObject, '$id', container.getId())
|
|
162
|
+
addProperty(containerObject, 'order', getFieldAsNumber(container, 'order', 1))
|
|
163
|
+
addProperty(containerObject, 'name', name)
|
|
164
|
+
addProperty(containerObject, 'width', width, 'container')
|
|
165
|
+
addProperty(containerObject, 'backgroundStyle', backgroundStyle, 'container')
|
|
166
|
+
addProperty(containerObject, 'backgroundColor', backgroundColor, 'container')
|
|
167
|
+
addProperty(containerObject, 'backgroundImage', backgroundImage)
|
|
168
|
+
addProperty(containerObject, 'cssClass', cssClass)
|
|
169
|
+
addProperty(containerObject, 'parentClass', parentClass)
|
|
170
|
+
addProperty(containerObject, 'subheader', subheader, 'container')
|
|
171
|
+
addProperty(containerObject, 'bootstrapAlt', bootstrapAlt, 'container')
|
|
172
|
+
addProperty(containerObject, 'semanticTag', semanticTag)
|
|
173
|
+
addProperty(containerObject, 'title', title)
|
|
174
|
+
addProperty(containerObject, 'rows', rows)
|
|
175
|
+
|
|
176
|
+
return containerObject
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Creates a row object from a ServiceNow sp_row record Shape.
|
|
181
|
+
* Transforms database fields to Fluent API format and includes nested columns.
|
|
182
|
+
* @param row - The sp_row record Shape
|
|
183
|
+
* @param columns - Array of column objects that belong to this row
|
|
184
|
+
* @returns Formatted row object or undefined if row is empty
|
|
185
|
+
*/
|
|
186
|
+
const getRowObject = (row: Record, columns: object[]): Dict | undefined => {
|
|
187
|
+
if (!row || isEmpty(row)) {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
const cssClass = row.get('class_name').ifString()?.getValue()
|
|
191
|
+
const semanticTag = row.get('semantic_tag').ifString()?.getValue()
|
|
192
|
+
|
|
193
|
+
const rowObject: Dict = {}
|
|
194
|
+
addProperty(rowObject, '$id', row.getId())
|
|
195
|
+
addProperty(rowObject, 'cssClass', cssClass)
|
|
196
|
+
addProperty(rowObject, 'semanticTag', semanticTag)
|
|
197
|
+
addProperty(rowObject, 'order', getFieldAsNumber(row, 'order', 1))
|
|
198
|
+
addProperty(rowObject, 'columns', columns)
|
|
199
|
+
return rowObject
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Creates a column object from a ServiceNow sp_column record Shape.
|
|
204
|
+
* @param column - The sp_column record Shape
|
|
205
|
+
* @param instances - Array of instance objects that belong to this column
|
|
206
|
+
* @param nestedRows - Array of nested row objects within this column
|
|
207
|
+
* @returns Formatted column object or undefined if column is empty
|
|
208
|
+
*/
|
|
209
|
+
const getColumnObject = (column: Record, instances: object[], nestedRows: object[]): Dict | undefined => {
|
|
210
|
+
if (!column || isEmpty(column)) {
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const cssClass = column.get('class_name').ifString()?.getValue()
|
|
215
|
+
const semanticTag = column.get('semantic_tag').ifString()?.getValue()
|
|
216
|
+
|
|
217
|
+
const columnObject: Dict = {}
|
|
218
|
+
|
|
219
|
+
addProperty(columnObject, '$id', column.getId())
|
|
220
|
+
addProperty(columnObject, 'size', getFieldAsNumber(column, 'size', 12), 'column')
|
|
221
|
+
addProperty(columnObject, 'sizeSm', getFieldAsNumber(column, 'size_sm'))
|
|
222
|
+
addProperty(columnObject, 'sizeLg', getFieldAsNumber(column, 'size_lg'))
|
|
223
|
+
addProperty(columnObject, 'sizeXs', getFieldAsNumber(column, 'size_xs'))
|
|
224
|
+
addProperty(columnObject, 'cssClass', cssClass)
|
|
225
|
+
addProperty(columnObject, 'semanticTag', semanticTag)
|
|
226
|
+
addProperty(columnObject, 'order', getFieldAsNumber(column, 'order', 1))
|
|
227
|
+
addProperty(columnObject, 'instances', instances)
|
|
228
|
+
addProperty(columnObject, 'nestedRows', nestedRows)
|
|
229
|
+
|
|
230
|
+
return columnObject
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Extracts and parses roles from a ServiceNow instance record.
|
|
234
|
+
* Converts comma-separated role string to an array of role names.
|
|
235
|
+
* @param instance - The sp_instance record Shape
|
|
236
|
+
* @returns Array of role names or undefined if no roles exist
|
|
237
|
+
*/
|
|
238
|
+
const getRolesArray = (instance: Record): string[] | undefined => {
|
|
239
|
+
const rolesStr = instance.get('roles').ifString()?.getValue()
|
|
240
|
+
if (!rolesStr || rolesStr === '') {
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
const rolesArray = rolesStr
|
|
244
|
+
.split(',')
|
|
245
|
+
.map((role) => role.trim())
|
|
246
|
+
.filter((role) => role !== '')
|
|
247
|
+
return rolesArray.length > 0 ? rolesArray : undefined
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Creates an instance object from a ServiceNow sp_instance record Shape.
|
|
252
|
+
* Transforms all instance properties from database format to Fluent API format,
|
|
253
|
+
* including widget references, styling, roles, and configuration.
|
|
254
|
+
* @param instance - The sp_instance record Shape
|
|
255
|
+
* @returns Formatted instance object or undefined if instance is empty
|
|
256
|
+
*/
|
|
257
|
+
function getInstanceObject(instance: Record): object | undefined {
|
|
258
|
+
if (!instance || isEmpty(instance)) {
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const title = instance.get('title').ifString()?.getValue()
|
|
263
|
+
const id = instance.get('id').ifString()?.getValue()
|
|
264
|
+
const widget = instance.get('sp_widget').ifString()?.getValue()
|
|
265
|
+
const widgetParameters = instance.get('widget_parameters').ifString()?.getValue()
|
|
266
|
+
const css = instance.get('css').ifString()?.getValue()
|
|
267
|
+
const url = instance.get('url').ifString()?.getValue()
|
|
268
|
+
const glyph = instance.get('glyph').ifString()?.getValue()
|
|
269
|
+
const size = instance.get('size').ifString()?.getValue()
|
|
270
|
+
const color = instance.get('color').ifString()?.getValue()
|
|
271
|
+
const cssClass = instance.get('class_name').ifString()?.getValue()
|
|
272
|
+
const shortDescription = instance.get('short_description').ifString()?.getValue()
|
|
273
|
+
const active = instance.get('active').toBoolean()?.getValue()
|
|
274
|
+
|
|
275
|
+
const instanceObject: Dict = {}
|
|
276
|
+
addProperty(instanceObject, '$id', instance.getId())
|
|
277
|
+
addProperty(instanceObject, 'title', title)
|
|
278
|
+
addProperty(instanceObject, 'id', id)
|
|
279
|
+
addProperty(instanceObject, 'widget', widget)
|
|
280
|
+
addProperty(instanceObject, 'widgetParameters', widgetParameters)
|
|
281
|
+
addProperty(instanceObject, 'css', css)
|
|
282
|
+
addProperty(instanceObject, 'url', url)
|
|
283
|
+
addProperty(instanceObject, 'glyph', glyph)
|
|
284
|
+
addProperty(instanceObject, 'size', size, 'instance')
|
|
285
|
+
addProperty(instanceObject, 'color', color, 'instance')
|
|
286
|
+
addProperty(instanceObject, 'cssClass', cssClass)
|
|
287
|
+
addProperty(instanceObject, 'active', active, 'instance')
|
|
288
|
+
addProperty(instanceObject, 'order', getFieldAsNumber(instance, 'order', 1))
|
|
289
|
+
addProperty(instanceObject, 'roles', getRolesArray(instance))
|
|
290
|
+
addProperty(instanceObject, 'shortDescription', shortDescription)
|
|
291
|
+
|
|
292
|
+
return instanceObject
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Converts an ArrayShape of roles to a comma-separated string.
|
|
297
|
+
* Handles both role record objects and string values, removes duplicates and empty values.
|
|
298
|
+
* @param roles - ArrayShape containing role records or strings
|
|
299
|
+
* @returns Comma-separated string of unique role names or undefined if no roles
|
|
300
|
+
*/
|
|
301
|
+
function getRolesString(roles: ArrayShape | undefined): string | undefined {
|
|
302
|
+
if (!roles) {
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
const uniqueRoles = [
|
|
306
|
+
...new Set(
|
|
307
|
+
roles.getElements().map((role) => {
|
|
308
|
+
const roleName = role.isRecord()
|
|
309
|
+
? role.get('name')?.ifString()?.getValue()
|
|
310
|
+
: role.ifString()?.getValue()
|
|
311
|
+
return (roleName ?? '').trim()
|
|
312
|
+
})
|
|
313
|
+
),
|
|
314
|
+
]
|
|
315
|
+
return uniqueRoles.filter((role) => role !== '').join(',')
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Recursively processes nested rows within a column
|
|
320
|
+
* @param columnRows - Rows that belong to the column
|
|
321
|
+
* @param allColumns - All available columns
|
|
322
|
+
* @param allInstances - All available instances
|
|
323
|
+
* @returns Array of processed SPRow objects with nested structure
|
|
324
|
+
*/
|
|
325
|
+
function getNestedRows(columnRows: Record[], allRows: Record[], allColumns: Record[], allInstances: Record[]): Dict[] {
|
|
326
|
+
if (!columnRows || columnRows.length === 0) {
|
|
327
|
+
return []
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Sort rows by order
|
|
331
|
+
const sortedRows = sortByOrder(columnRows)
|
|
332
|
+
|
|
333
|
+
return sortedRows
|
|
334
|
+
.map((row) => {
|
|
335
|
+
const rowId = row.getId()
|
|
336
|
+
|
|
337
|
+
// Get columns for this nested row
|
|
338
|
+
const rowColumns = sortByOrder(allColumns.filter((column) => column.get('sp_row').equals(rowId)))
|
|
339
|
+
|
|
340
|
+
const columns = rowColumns
|
|
341
|
+
.map((column) => {
|
|
342
|
+
const columnId = column.getId()
|
|
343
|
+
|
|
344
|
+
// Get instances for this column
|
|
345
|
+
const columnInstances = sortByOrder(
|
|
346
|
+
allInstances.filter((instance) => instance.get('sp_column').equals(columnId))
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
// Recursively get nested rows for this column
|
|
350
|
+
const nestedColumnRows = allRows.filter((row) => row.get('sp_column').equals(columnId))
|
|
351
|
+
const nestedRows = getNestedRows(nestedColumnRows, allRows, allColumns, allInstances)
|
|
352
|
+
const instances = columnInstances
|
|
353
|
+
.map((instance) => {
|
|
354
|
+
return getInstanceObject(instance)
|
|
355
|
+
})
|
|
356
|
+
.filter((instanceObject): instanceObject is Dict => Boolean(instanceObject))
|
|
357
|
+
|
|
358
|
+
return getColumnObject(column, instances, nestedRows)
|
|
359
|
+
})
|
|
360
|
+
.filter((col): col is Dict => Boolean(col))
|
|
361
|
+
|
|
362
|
+
const rowObj = getRowObject(row, columns)
|
|
363
|
+
return rowObj as Dict
|
|
364
|
+
})
|
|
365
|
+
.filter((row): row is Dict => Boolean(row))
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Generates a container name from the container shape or creates a default name.
|
|
370
|
+
* Uses the explicit name if provided, otherwise generates a name based on page title and order.
|
|
371
|
+
* @param $ - The container shape object
|
|
372
|
+
* @param pageTitle - The title of the parent page
|
|
373
|
+
* @param index - The zero-based index of the container in the page
|
|
374
|
+
* @returns The container name string
|
|
375
|
+
*/
|
|
376
|
+
const getContainerName = ($: ObjectShape, pageTitle: string, index: number): string => {
|
|
377
|
+
let name = $.get('name').ifString()?.getValue()
|
|
378
|
+
if (!name) {
|
|
379
|
+
const order = $.get('order').ifNumber()?.getValue() || index + 1
|
|
380
|
+
name = `${pageTitle} - Container ${order}`
|
|
381
|
+
}
|
|
382
|
+
return name
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Creates ServiceNow sp_container records from Fluent container shapes and their nested rows/columns/instances.
|
|
387
|
+
*
|
|
388
|
+
* @param containersArray - Array of Fluent container Shape objects to transform
|
|
389
|
+
* @param pageId - RecordId of the parent sp_page record that owns these containers
|
|
390
|
+
* @param pageTitle - Title of the parent page, used for generating default container names
|
|
391
|
+
* @param factory - Factory instance for creating ServiceNow records with proper relationships
|
|
392
|
+
* @returns Promise that resolves to an array of all created records (containers, rows, columns, instances)
|
|
393
|
+
*/
|
|
394
|
+
const createContainerRecords = async (
|
|
395
|
+
containersArray: Shape[],
|
|
396
|
+
pageId: RecordId,
|
|
397
|
+
pageTitle: string,
|
|
398
|
+
factory: Factory
|
|
399
|
+
): Promise<Record[]> => {
|
|
400
|
+
const records: Record[] = []
|
|
401
|
+
|
|
402
|
+
for (let index = 0; index < containersArray.length; index++) {
|
|
403
|
+
const containerShape = containersArray[index]
|
|
404
|
+
if (containerShape?.isObject()) {
|
|
405
|
+
const container = containerShape.asObject()
|
|
406
|
+
|
|
407
|
+
const name = getContainerName(container, pageTitle, index)
|
|
408
|
+
|
|
409
|
+
const containerRecord = await factory.createRecord({
|
|
410
|
+
source: containerShape,
|
|
411
|
+
table: 'sp_container',
|
|
412
|
+
explicitId: container.get('$id'),
|
|
413
|
+
properties: container.transform(({ $ }) => ({
|
|
414
|
+
name: $.val(name),
|
|
415
|
+
sp_page: $.val(pageId),
|
|
416
|
+
width: $.from('width').def('container'),
|
|
417
|
+
background_style: $.from('backgroundStyle').def('default'),
|
|
418
|
+
background_color: $.from('backgroundColor').def(''),
|
|
419
|
+
background_image: $.from('backgroundImage').def(''),
|
|
420
|
+
class_name: $.from('cssClass').def(''),
|
|
421
|
+
container_class_name: $.from('parentClass').def(''),
|
|
422
|
+
subheader: $.from('subheader').def(false),
|
|
423
|
+
bootstrap_alt: $.from('bootstrapAlt').def(false),
|
|
424
|
+
semantic_tag: $.from('semanticTag').def(''),
|
|
425
|
+
title: $.from('title').def(''),
|
|
426
|
+
order: $.from('order').def(index + 1),
|
|
427
|
+
})),
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
records.push(containerRecord)
|
|
431
|
+
|
|
432
|
+
// Handle rows
|
|
433
|
+
const rows = container.get('rows').ifArray()?.getElements() || []
|
|
434
|
+
const rowRecords = await createRowRecords(rows, containerRecord.getId(), factory, undefined)
|
|
435
|
+
records.push(...rowRecords)
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return records
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Creates ServiceNow sp_row records from Fluent row shapes and their nested columns/instances.
|
|
443
|
+
*
|
|
444
|
+
* @param rowsArray - Array of Fluent row Shape objects to transform
|
|
445
|
+
* @param containerId - RecordId of the parent sp_container record that owns these rows
|
|
446
|
+
* @param factory - Factory instance for creating ServiceNow records with proper relationships
|
|
447
|
+
* @param columnId - RecordId of the parent sp_column record (for nested rows within columns)
|
|
448
|
+
* @returns Promise that resolves to an array of all created records (rows, columns, instances)
|
|
449
|
+
*/
|
|
450
|
+
const createRowRecords = async (
|
|
451
|
+
rowsArray: Shape[],
|
|
452
|
+
containerId: RecordId | undefined,
|
|
453
|
+
factory: Factory,
|
|
454
|
+
columnId: RecordId | undefined
|
|
455
|
+
): Promise<Record[]> => {
|
|
456
|
+
const records: Record[] = []
|
|
457
|
+
|
|
458
|
+
for (let index = 0; index < rowsArray.length; index++) {
|
|
459
|
+
const rowShape = rowsArray[index]
|
|
460
|
+
if (rowShape?.isObject()) {
|
|
461
|
+
const row = rowShape.asObject()
|
|
462
|
+
|
|
463
|
+
const rowRecord = await factory.createRecord({
|
|
464
|
+
source: rowShape,
|
|
465
|
+
table: 'sp_row',
|
|
466
|
+
explicitId: row.get('$id'),
|
|
467
|
+
properties: row.transform(({ $ }) => ({
|
|
468
|
+
sp_container: $.val(containerId),
|
|
469
|
+
class_name: $.from('cssClass').def(''),
|
|
470
|
+
semantic_tag: $.from('semanticTag').def(''),
|
|
471
|
+
order: $.from('order').def(index + 1),
|
|
472
|
+
sp_column: $.val(columnId),
|
|
473
|
+
})),
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
records.push(rowRecord)
|
|
477
|
+
|
|
478
|
+
// Handle columns
|
|
479
|
+
const columns = row.get('columns').ifArray()?.getElements() || []
|
|
480
|
+
const columnRecords = await createColumnRecords(columns, rowRecord.getId(), factory)
|
|
481
|
+
records.push(...columnRecords)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return records
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Creates ServiceNow sp_column records from Fluent column shapes and their nested instances/rows.
|
|
490
|
+
*
|
|
491
|
+
* @param columnsArray - Array of Fluent column Shape objects to transform
|
|
492
|
+
* @param rowId - RecordId of the parent sp_row record that owns these columns
|
|
493
|
+
* @param factory - Factory instance for creating ServiceNow records with proper relationships
|
|
494
|
+
* @returns Promise that resolves to an array of all created records (columns, instances, nested rows)
|
|
495
|
+
*/
|
|
496
|
+
async function createColumnRecords(columnsArray: Shape[], rowId: RecordId, factory: Factory): Promise<Record[]> {
|
|
497
|
+
const records: Record[] = []
|
|
498
|
+
|
|
499
|
+
for (let index = 0; index < columnsArray.length; index++) {
|
|
500
|
+
const columnShape = columnsArray[index]
|
|
501
|
+
if (columnShape?.isObject()) {
|
|
502
|
+
const column = columnShape.asObject()
|
|
503
|
+
|
|
504
|
+
const columnRecord = await factory.createRecord({
|
|
505
|
+
source: columnShape,
|
|
506
|
+
table: 'sp_column',
|
|
507
|
+
explicitId: column.get('$id'),
|
|
508
|
+
properties: column.transform(({ $ }) => ({
|
|
509
|
+
sp_row: $.val(rowId),
|
|
510
|
+
size: $.from('size').def(12),
|
|
511
|
+
size_sm: $.from('sizeSm'),
|
|
512
|
+
size_lg: $.from('sizeLg'),
|
|
513
|
+
size_xs: $.from('sizeXs'),
|
|
514
|
+
class_name: $.from('cssClass').def(''),
|
|
515
|
+
semantic_tag: $.from('semanticTag').def(''),
|
|
516
|
+
order: $.from('order').def(index + 1),
|
|
517
|
+
})),
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
records.push(columnRecord)
|
|
521
|
+
|
|
522
|
+
// Handle instances
|
|
523
|
+
const instances = column.get('instances').ifArray()?.getElements() || []
|
|
524
|
+
const nestedRows = column.get('nestedRows').ifArray()?.getElements() || []
|
|
525
|
+
const nestedRowRecords = await createRowRecords(nestedRows, undefined, factory, columnRecord.getId())
|
|
526
|
+
records.push(...nestedRowRecords)
|
|
527
|
+
const instanceRecords = await createInstanceRecords(instances, columnRecord.getId(), factory)
|
|
528
|
+
records.push(...instanceRecords)
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return records
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Creates ServiceNow sp_instance records from Fluent instance shapes.
|
|
536
|
+
*
|
|
537
|
+
* @param instancesArray - Array of Fluent instance Shape objects to transform
|
|
538
|
+
* @param columnId - RecordId of the parent sp_column record that owns these instances
|
|
539
|
+
* @param factory - Factory instance for creating ServiceNow records with proper relationships
|
|
540
|
+
* @returns Promise that resolves to an array of all created instance records
|
|
541
|
+
*/
|
|
542
|
+
async function createInstanceRecords(instancesArray: Shape[], columnId: RecordId, factory: Factory): Promise<Record[]> {
|
|
543
|
+
const records: Record[] = []
|
|
544
|
+
|
|
545
|
+
for (let index = 0; index < instancesArray.length; index++) {
|
|
546
|
+
const instanceShape = instancesArray[index]
|
|
547
|
+
if (instanceShape?.isObject()) {
|
|
548
|
+
const instance = instanceShape.asObject()
|
|
549
|
+
|
|
550
|
+
// Process roles if they exist as an array
|
|
551
|
+
const rolesArray = instance.get('roles').ifArray()
|
|
552
|
+
const rolesString = getRolesString(rolesArray)
|
|
553
|
+
|
|
554
|
+
const instanceRecord = await factory.createRecord({
|
|
555
|
+
source: instanceShape,
|
|
556
|
+
table: 'sp_instance',
|
|
557
|
+
explicitId: instance.get('$id'),
|
|
558
|
+
properties: instance.transform(({ $ }) => ({
|
|
559
|
+
sp_column: $.val(columnId),
|
|
560
|
+
title: $.from('title').def(''),
|
|
561
|
+
id: $.from('id').def(''),
|
|
562
|
+
sp_widget: $.from('widget').map(toReference).def(''),
|
|
563
|
+
widget_parameters: $.from('widgetParameters').def(''),
|
|
564
|
+
short_description: $.from('shortDescription').def(''),
|
|
565
|
+
css: $.from('css').def(''),
|
|
566
|
+
url: $.from('url').def(''),
|
|
567
|
+
glyph: $.from('glyph').def(''),
|
|
568
|
+
size: $.from('size').def('md'),
|
|
569
|
+
color: $.from('color').def('default'),
|
|
570
|
+
class_name: $.from('cssClass').def(''),
|
|
571
|
+
active: $.from('active').def(defaultValues.instance.active),
|
|
572
|
+
order: $.from('order').def(index + 1),
|
|
573
|
+
roles: $.val(rolesString).def(''),
|
|
574
|
+
async_load: $.from('asyncLoad').def(false),
|
|
575
|
+
async_load_trigger: $.from('asyncLoadTrigger').def('viewport'),
|
|
576
|
+
async_load_device_type: $.def(defaultValues.instance.asyncLoadDeviceType),
|
|
577
|
+
preserve_placeholder_size: $.def(false),
|
|
578
|
+
placeholder_dimensions: $.def(default_placeholder_dimensions),
|
|
579
|
+
advanced_placeholder_dimensions: $.def(false),
|
|
580
|
+
placeholder_dimensions_script: $.def(default_placeholder_script),
|
|
581
|
+
placeholder_template: $.def(default_placeholder_template),
|
|
582
|
+
})),
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
records.push(instanceRecord)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return records
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export const SPPagePlugin = Plugin.create({
|
|
592
|
+
name: 'SPPagePlugin',
|
|
593
|
+
docs: [createSdkDocEntry('SPPage', ['sp_page'])],
|
|
594
|
+
records: {
|
|
595
|
+
sp_page: {
|
|
596
|
+
coalesce: ['id'],
|
|
597
|
+
relationships: {
|
|
598
|
+
sp_container: {
|
|
599
|
+
via: 'sp_page',
|
|
600
|
+
descendant: true,
|
|
601
|
+
relationships: {
|
|
602
|
+
sp_row: {
|
|
603
|
+
via: 'sp_container',
|
|
604
|
+
descendant: true,
|
|
605
|
+
relationships: {
|
|
606
|
+
sp_column: {
|
|
607
|
+
via: 'sp_row',
|
|
608
|
+
descendant: true,
|
|
609
|
+
relationships: {
|
|
610
|
+
sp_instance: {
|
|
611
|
+
via: 'sp_column',
|
|
612
|
+
descendant: true,
|
|
613
|
+
},
|
|
614
|
+
sp_row: {
|
|
615
|
+
via: 'sp_column',
|
|
616
|
+
descendant: true,
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
toShape(record, { descendants }) {
|
|
626
|
+
// Build hierarchical structure from descendants in a single pass
|
|
627
|
+
// to avoid "node that was removed or forgotten" errors
|
|
628
|
+
|
|
629
|
+
// Get all descendants at once to avoid multiple queries
|
|
630
|
+
const allContainers = sortByOrder(
|
|
631
|
+
descendants
|
|
632
|
+
.query('sp_container')
|
|
633
|
+
.filter((container) => container.get('sp_page').equals(record.getId()))
|
|
634
|
+
)
|
|
635
|
+
const allRows = descendants.query('sp_row')
|
|
636
|
+
const allColumns = descendants.query('sp_column')
|
|
637
|
+
const allInstances = descendants.query('sp_instance')
|
|
638
|
+
|
|
639
|
+
const containers = allContainers.map((container) => {
|
|
640
|
+
const containerId = container.getId()
|
|
641
|
+
|
|
642
|
+
// Get rows for this container
|
|
643
|
+
const containerRows = sortByOrder(
|
|
644
|
+
allRows.filter((row) => row.get('sp_container').equals(containerId))
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
const rows = containerRows
|
|
648
|
+
.map((row) => {
|
|
649
|
+
const rowId = row.getId()
|
|
650
|
+
|
|
651
|
+
// Get columns for this row
|
|
652
|
+
const rowColumns = sortByOrder(
|
|
653
|
+
allColumns.filter((column) => column.get('sp_row').equals(rowId))
|
|
654
|
+
)
|
|
655
|
+
const columns = rowColumns
|
|
656
|
+
.map((column) => {
|
|
657
|
+
const columnId = column.getId()
|
|
658
|
+
|
|
659
|
+
// Get instances for this column
|
|
660
|
+
const columnInstances = sortByOrder(
|
|
661
|
+
allInstances.filter((instance) => instance.get('sp_column').equals(columnId))
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
const columnRows = allRows.filter((row) => row.get('sp_column').equals(columnId))
|
|
665
|
+
const nestedRows = getNestedRows(columnRows, allRows, allColumns, allInstances)
|
|
666
|
+
|
|
667
|
+
const instances = columnInstances
|
|
668
|
+
.map((instance) => {
|
|
669
|
+
return getInstanceObject(instance)
|
|
670
|
+
})
|
|
671
|
+
.filter((instanceObject): instanceObject is object => Boolean(instanceObject))
|
|
672
|
+
|
|
673
|
+
return getColumnObject(column, instances, nestedRows)
|
|
674
|
+
})
|
|
675
|
+
.filter((columnObject): columnObject is Dict => Boolean(columnObject))
|
|
676
|
+
|
|
677
|
+
return getRowObject(row, columns)
|
|
678
|
+
})
|
|
679
|
+
.filter((rowObject): rowObject is Dict => Boolean(rowObject))
|
|
680
|
+
|
|
681
|
+
return getContainerObject(container, rows)
|
|
682
|
+
})
|
|
683
|
+
|
|
684
|
+
// Process roles to check if they should be included
|
|
685
|
+
const rolesArray = getRolesArray(record)
|
|
686
|
+
|
|
687
|
+
return {
|
|
688
|
+
success: true,
|
|
689
|
+
value: new CallExpressionShape({
|
|
690
|
+
source: record,
|
|
691
|
+
callee: 'SPPage',
|
|
692
|
+
args: [
|
|
693
|
+
record.transform(({ $ }) => {
|
|
694
|
+
const pageObject: { [key: string]: typeof $ | undefined } = {
|
|
695
|
+
title: $,
|
|
696
|
+
category: $,
|
|
697
|
+
pageId: $.from('id').def(''),
|
|
698
|
+
draft: $.toBoolean().def(false),
|
|
699
|
+
internal: $.toBoolean().def(false),
|
|
700
|
+
omitWatcher: $.from('omit_watcher').toBoolean().def(false),
|
|
701
|
+
public: $.toBoolean().def(false),
|
|
702
|
+
useSeoScript: $.from('use_seo_script').toBoolean().def(false),
|
|
703
|
+
css: $.def(''),
|
|
704
|
+
shortDescription: $.from('short_description').def(''),
|
|
705
|
+
seoScript: $.from('seo_script').def(''),
|
|
706
|
+
dynamicTitleStructure: $.from('dynamic_title_structure').def(''),
|
|
707
|
+
humanReadableUrlStructure: $.from('human_readable_url_structure').def(''),
|
|
708
|
+
}
|
|
709
|
+
// Only add roles if they exist
|
|
710
|
+
if (rolesArray) {
|
|
711
|
+
pageObject['roles'] = $.val(rolesArray)
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Only add containers if they exist
|
|
715
|
+
if (containers.length > 0) {
|
|
716
|
+
pageObject['containers'] = $.val(containers)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return pageObject
|
|
720
|
+
}),
|
|
721
|
+
],
|
|
722
|
+
}),
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
shapes: [
|
|
728
|
+
{
|
|
729
|
+
shape: CallExpressionShape,
|
|
730
|
+
fileTypes: ['fluent'],
|
|
731
|
+
async toRecord(callExpression, { diagnostics, factory }) {
|
|
732
|
+
if (callExpression.getCallee() !== 'SPPage') {
|
|
733
|
+
return { success: false }
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const page = callExpression.getArgument(0).asObject()
|
|
737
|
+
const containers = page.get('containers').ifArray()?.getElements() || []
|
|
738
|
+
|
|
739
|
+
const useSeoScriptShape = page.get('useSeoScript')
|
|
740
|
+
const useSeoScript = useSeoScriptShape.ifBoolean()?.getValue() ?? false
|
|
741
|
+
const seoScriptRef = toReference(page.get('seoScript'))
|
|
742
|
+
const seoScript = typeof seoScriptRef === 'string' ? seoScriptRef : seoScriptRef.getValue()
|
|
743
|
+
|
|
744
|
+
if (useSeoScript && (!seoScript || seoScript.trim() === '')) {
|
|
745
|
+
diagnostics.error(
|
|
746
|
+
useSeoScriptShape.getOriginalNode(),
|
|
747
|
+
`Invalid SPPage configuration: when "useSeoScript" is true, "seoScript" must be added.`
|
|
748
|
+
)
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Process roles if they exist as an array
|
|
752
|
+
const rolesArray = page.get('roles').ifArray()
|
|
753
|
+
const rolesString = getRolesString(rolesArray)
|
|
754
|
+
|
|
755
|
+
const pageIdShape = page.get('pageId')
|
|
756
|
+
const pageId = pageIdShape.asString().getValue()
|
|
757
|
+
|
|
758
|
+
if (!pageId.trim()) {
|
|
759
|
+
diagnostics.error(
|
|
760
|
+
pageIdShape.getOriginalNode(),
|
|
761
|
+
'Invalid SPPage configuration: "pageId" must be a non-empty string.'
|
|
762
|
+
)
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const urlStructureShape = page.get('humanReadableUrlStructure')
|
|
766
|
+
const urlStructure = urlStructureShape.ifString()?.getValue()
|
|
767
|
+
if (urlStructure && urlStructure.length > 0) {
|
|
768
|
+
const delimiter = String(Math.floor(Math.random() * 100000))
|
|
769
|
+
const urlStrings = urlStructure
|
|
770
|
+
.replaceAll(/[/-]/g, delimiter)
|
|
771
|
+
.replaceAll('%', delimiter + '%')
|
|
772
|
+
.split(delimiter)
|
|
773
|
+
const nonVariables: string[] = []
|
|
774
|
+
for (const segment of urlStrings) {
|
|
775
|
+
if (segment.length > 1 && segment.indexOf('%') === 0) {
|
|
776
|
+
continue
|
|
777
|
+
}
|
|
778
|
+
nonVariables.push(segment)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (!/^[a-zA-Z0-9/-]*$/.test(nonVariables.join(''))) {
|
|
782
|
+
diagnostics.error(
|
|
783
|
+
urlStructureShape.getOriginalNode(),
|
|
784
|
+
`Only alphanumeric characters, - and / are allowed in humanReadableUrlStructure.`
|
|
785
|
+
)
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (urlStructure.indexOf('/') !== urlStructure.lastIndexOf('/')) {
|
|
789
|
+
diagnostics.error(
|
|
790
|
+
urlStructureShape.getOriginalNode(),
|
|
791
|
+
`No more than one "/" character is allowed in humanReadableUrlStructure.`
|
|
792
|
+
)
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const title = page.get('title').ifString()?.getValue() || pageId
|
|
797
|
+
|
|
798
|
+
// Create the main page record
|
|
799
|
+
const pageRecord = await factory.createRecord({
|
|
800
|
+
source: callExpression,
|
|
801
|
+
table: 'sp_page',
|
|
802
|
+
properties: page.transform(({ $ }) => ({
|
|
803
|
+
id: $.from('pageId'),
|
|
804
|
+
title: $.val(title),
|
|
805
|
+
category: $.def('custom'),
|
|
806
|
+
css: $.def(''),
|
|
807
|
+
draft: $.def(false),
|
|
808
|
+
dynamic_title_structure: $.from('dynamicTitleStructure').def(''),
|
|
809
|
+
human_readable_url_structure: $.from('humanReadableUrlStructure').def(''),
|
|
810
|
+
internal: $.def(false),
|
|
811
|
+
omit_watcher: $.from('omitWatcher').def(false),
|
|
812
|
+
public: $.def(false),
|
|
813
|
+
roles: $.val(rolesString).def(''),
|
|
814
|
+
seo_script: $.from('seoScript').map(toReference).def(''),
|
|
815
|
+
short_description: $.from('shortDescription').def(''),
|
|
816
|
+
use_seo_script: $.from('useSeoScript').def(false),
|
|
817
|
+
})),
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
// Create container, row, column, and instance records
|
|
821
|
+
const allRecords = await createContainerRecords(containers, pageRecord.getId(), title, factory)
|
|
822
|
+
|
|
823
|
+
return {
|
|
824
|
+
success: true,
|
|
825
|
+
value: pageRecord.with(...allRecords),
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
],
|
|
830
|
+
})
|