@servicenow/sdk-build-plugins 4.5.0 → 4.6.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/column-plugin.js +3 -7
- package/dist/column-plugin.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
- package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +16 -42
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
- package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
- package/dist/flow/plugins/flow-instance-plugin.js +68 -22
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/step-definition-plugin.js +2 -1
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
- package/dist/flow/plugins/step-instance-plugin.js +649 -136
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
- package/dist/flow/post-install.js +1 -0
- package/dist/flow/post-install.js.map +1 -1
- package/dist/flow/utils/complex-object-resolver.js +4 -1
- package/dist/flow/utils/complex-object-resolver.js.map +1 -1
- package/dist/flow/utils/complex-objects.js +1 -1
- package/dist/flow/utils/complex-objects.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +66 -2
- package/dist/flow/utils/flow-constants.js +402 -6
- 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 +37 -16
- package/dist/flow/utils/flow-io-to-record.js.map +1 -1
- package/dist/flow/utils/flow-shapes.js +4 -0
- package/dist/flow/utils/flow-shapes.js.map +1 -1
- package/dist/flow/utils/label-cache-parser.d.ts +9 -2
- package/dist/flow/utils/label-cache-parser.js +32 -4
- package/dist/flow/utils/label-cache-parser.js.map +1 -1
- package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
- package/dist/flow/utils/pill-shape-helpers.js +35 -0
- package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
- package/dist/flow/utils/pill-string-parser.js +1 -0
- package/dist/flow/utils/pill-string-parser.js.map +1 -1
- package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
- package/dist/flow/utils/schema-to-flow-object.js +131 -15
- package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
- package/dist/flow/utils/utils.d.ts +1 -0
- package/dist/flow/utils/utils.js +6 -1
- package/dist/flow/utils/utils.js.map +1 -1
- package/dist/form-plugin.js +7 -9
- package/dist/form-plugin.js.map +1 -1
- package/dist/inbound-email-action-plugin.d.ts +10 -0
- package/dist/inbound-email-action-plugin.js +128 -0
- package/dist/inbound-email-action-plugin.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/instance-scan-plugin.js +0 -5
- package/dist/instance-scan-plugin.js.map +1 -1
- package/dist/property-plugin.js +1 -1
- package/dist/property-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +7 -0
- package/dist/record-plugin.js +10 -2
- package/dist/record-plugin.js.map +1 -1
- package/dist/rest-api-plugin.js +8 -1
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/schedule-script/scheduled-script-plugin.js +8 -3
- package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
- package/dist/service-catalog/service-catalog-base.d.ts +18 -18
- package/dist/service-catalog/service-catalog-base.js +22 -22
- package/dist/service-catalog/service-catalog-base.js.map +1 -1
- package/dist/service-portal/header-footer-plugin.d.ts +2 -0
- package/dist/service-portal/header-footer-plugin.js +50 -0
- package/dist/service-portal/header-footer-plugin.js.map +1 -0
- package/dist/service-portal/menu-plugin.js +3 -22
- package/dist/service-portal/menu-plugin.js.map +1 -1
- package/dist/service-portal/page-plugin.js +3 -24
- package/dist/service-portal/page-plugin.js.map +1 -1
- package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
- package/dist/service-portal/page-route-map-plugin.js +114 -0
- package/dist/service-portal/page-route-map-plugin.js.map +1 -0
- package/dist/service-portal/portal-plugin.js +21 -8
- package/dist/service-portal/portal-plugin.js.map +1 -1
- package/dist/service-portal/utils.d.ts +40 -2
- package/dist/service-portal/utils.js +283 -2
- package/dist/service-portal/utils.js.map +1 -1
- package/dist/service-portal/widget-plugin.js +9 -218
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/static-content-plugin.js +4 -0
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +190 -26
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-action-plugin.js +1 -4
- package/dist/ui-action-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +68 -13
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/view-plugin.js +8 -3
- package/dist/view-plugin.js.map +1 -1
- package/dist/workspace-plugin.js +39 -36
- package/dist/workspace-plugin.js.map +1 -1
- package/package.json +5 -4
- package/src/column-plugin.ts +3 -8
- package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
- package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
- package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
- package/src/flow/plugins/flow-definition-plugin.ts +12 -47
- package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
- package/src/flow/plugins/flow-instance-plugin.ts +98 -22
- package/src/flow/plugins/step-definition-plugin.ts +2 -1
- package/src/flow/plugins/step-instance-plugin.ts +772 -156
- package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
- package/src/flow/post-install.ts +1 -0
- package/src/flow/utils/complex-object-resolver.ts +4 -1
- package/src/flow/utils/complex-objects.ts +1 -1
- package/src/flow/utils/flow-constants.ts +421 -5
- package/src/flow/utils/flow-io-to-record.ts +43 -17
- package/src/flow/utils/flow-shapes.ts +4 -0
- package/src/flow/utils/label-cache-parser.ts +33 -4
- package/src/flow/utils/pill-shape-helpers.ts +42 -0
- package/src/flow/utils/pill-string-parser.ts +1 -0
- package/src/flow/utils/schema-to-flow-object.ts +183 -15
- package/src/flow/utils/utils.ts +12 -1
- package/src/form-plugin.ts +1 -3
- package/src/inbound-email-action-plugin.ts +145 -0
- package/src/index.ts +4 -0
- package/src/instance-scan-plugin.ts +0 -5
- package/src/property-plugin.ts +4 -1
- package/src/record-plugin.ts +14 -4
- package/src/rest-api-plugin.ts +7 -1
- package/src/schedule-script/scheduled-script-plugin.ts +14 -3
- package/src/service-catalog/service-catalog-base.ts +22 -22
- package/src/service-portal/header-footer-plugin.ts +57 -0
- package/src/service-portal/menu-plugin.ts +1 -23
- package/src/service-portal/page-plugin.ts +3 -28
- package/src/service-portal/page-route-map-plugin.ts +124 -0
- package/src/service-portal/portal-plugin.ts +33 -10
- package/src/service-portal/utils.ts +404 -3
- package/src/service-portal/widget-plugin.ts +14 -290
- package/src/static-content-plugin.ts +3 -0
- package/src/table-plugin.ts +226 -36
- package/src/ui-action-plugin.ts +1 -8
- package/src/ui-page-plugin.ts +76 -13
- package/src/view-plugin.ts +10 -4
- package/src/workspace-plugin.ts +43 -43
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Logger } from '@servicenow/sdk-build-core'
|
|
2
|
+
import { parseSinglePill } from './pill-string-parser'
|
|
2
3
|
|
|
3
4
|
interface LabelCacheEntry {
|
|
4
5
|
name: string
|
|
@@ -13,10 +14,20 @@ interface LabelCacheEntry {
|
|
|
13
14
|
attributes?: Record<string, string>
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/** Action label cache uses pill category ("action", "step", "static") as the type field */
|
|
18
|
+
const ACTION_PILL_CATEGORIES = new Set(['action', 'step', 'static'])
|
|
19
|
+
|
|
16
20
|
/**
|
|
17
|
-
* Creates a map of name to type from a label_cache string
|
|
21
|
+
* Creates a map of name to type from a label_cache string.
|
|
22
|
+
*
|
|
23
|
+
* Flow/subflow label cache entries use the actual data type in the `type` field and
|
|
24
|
+
* plain names without braces (e.g., name="flow_variable.userName", type="string").
|
|
25
|
+
*
|
|
26
|
+
* Action label cache entries use a pill category in `type` (e.g., "action") and store the
|
|
27
|
+
* actual data type in `base_type`. Names include braces (e.g., "{{action.variable}}").
|
|
28
|
+
*
|
|
18
29
|
* @param labelCacheString - The stringified JSON array from label_cache
|
|
19
|
-
* @returns Map where key is the name
|
|
30
|
+
* @returns Map where key is the pill name (without braces) and value is the data type
|
|
20
31
|
*/
|
|
21
32
|
export function createLableCacheNameToTypeMap(labelCacheString: string, logger: Logger): Map<string, string> {
|
|
22
33
|
const nameToTypeMap = new Map<string, string>()
|
|
@@ -25,8 +36,26 @@ export function createLableCacheNameToTypeMap(labelCacheString: string, logger:
|
|
|
25
36
|
const entries: LabelCacheEntry[] = JSON.parse(labelCacheString)
|
|
26
37
|
|
|
27
38
|
for (const entry of entries) {
|
|
28
|
-
if (entry.name
|
|
29
|
-
|
|
39
|
+
if (!entry.name) {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (ACTION_PILL_CATEGORIES.has(entry.type)) {
|
|
44
|
+
// Action label cache: strip braces from name, use base_type for actual data type
|
|
45
|
+
const parsed = parseSinglePill(entry.name, true)
|
|
46
|
+
const key = parsed ? `${parsed.prefix}.${parsed.path}` : entry.name
|
|
47
|
+
nameToTypeMap.set(key, entry.base_type || 'string')
|
|
48
|
+
} else if (entry.type) {
|
|
49
|
+
// Flow/subflow label cache: name is plain, type is the actual data type
|
|
50
|
+
let type = entry.type
|
|
51
|
+
// DEF0792627: The platform sometimes stores 'reference' for the sys_id field
|
|
52
|
+
// when the pill is used in a reference field context (e.g., sysapproval).
|
|
53
|
+
// sys_id is always a string/GUID field never a reference — except for
|
|
54
|
+
// step output Record.sys_id which IS a reference to the record itself.
|
|
55
|
+
if (type === 'reference' && entry.name.endsWith('.sys_id') && !entry.name.includes('.Record.sys_id')) {
|
|
56
|
+
type = 'string'
|
|
57
|
+
}
|
|
58
|
+
nameToTypeMap.set(entry.name, type)
|
|
30
59
|
}
|
|
31
60
|
}
|
|
32
61
|
} catch (error) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CallExpressionShape,
|
|
3
|
+
type IdentifierShape,
|
|
4
|
+
type PropertyAccessShape,
|
|
5
|
+
StringLiteralShape,
|
|
6
|
+
type Source,
|
|
7
|
+
} from '@servicenow/sdk-build-core'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a PropertyAccessShape or IdentifierShape with wfa.dataPill(expression, type).
|
|
11
|
+
* Used by both flow-definition-plugin and flow-action-definition-plugin.
|
|
12
|
+
*
|
|
13
|
+
* @param expression - The property access or identifier to wrap
|
|
14
|
+
* @param source - Source for the shape
|
|
15
|
+
* @param dataType - The data type for the pill
|
|
16
|
+
*/
|
|
17
|
+
export function wrapWithDataPillCall(
|
|
18
|
+
expression: PropertyAccessShape | IdentifierShape,
|
|
19
|
+
source: Source,
|
|
20
|
+
dataType = 'string'
|
|
21
|
+
): CallExpressionShape {
|
|
22
|
+
return new CallExpressionShape({
|
|
23
|
+
source,
|
|
24
|
+
callee: 'wfa.dataPill',
|
|
25
|
+
args: [expression, new StringLiteralShape({ source, literalText: dataType })],
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extracts all datapill names (without type annotations) from a string containing {{...}} patterns.
|
|
31
|
+
* Used by both flow-definition-plugin and flow-action-definition-plugin.
|
|
32
|
+
*/
|
|
33
|
+
export function extractDataPillNames(stringValue: string): string[] {
|
|
34
|
+
const names: string[] = []
|
|
35
|
+
for (const match of stringValue.matchAll(/\{\{([^}]+)\}\}/g)) {
|
|
36
|
+
const pillContent = match[1]?.split('|')[0]
|
|
37
|
+
if (pillContent) {
|
|
38
|
+
names.push(pillContent)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return names
|
|
42
|
+
}
|
|
@@ -121,6 +121,7 @@ export function mapPillPrefixToTsRoot(prefix: string, parameterName: string = 'p
|
|
|
121
121
|
inputs: `${parameterName}.inputs`,
|
|
122
122
|
outputs: `${parameterName}.outputs`, // Schema reference (no property access)
|
|
123
123
|
subflow: `${parameterName}.inputs`, // ServiceNow uses {{subflow.x}} for subflow inputs
|
|
124
|
+
action: `${parameterName}.inputs`, // ServiceNow uses {{action.x}} for action inputs → params.inputs.x
|
|
124
125
|
|
|
125
126
|
// Record triggers (prefixed with trigger name + _1)
|
|
126
127
|
Created_1: `${parameterName}.trigger`,
|
|
@@ -38,20 +38,129 @@ const SCHEMA_TYPE_TO_COLUMN_API: Record<string, string> = {
|
|
|
38
38
|
reference: ReferenceColumn.name,
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// ------------------------------------------------------------
|
|
42
|
+
// Snapshot value extraction helpers
|
|
43
|
+
// ------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Recursively walk a snapshot's `complexObjectSchema` and extract all
|
|
47
|
+
* datapill mappings from `mapped` fields inside `$field_facets.SimpleMapFacet`.
|
|
48
|
+
*/
|
|
49
|
+
export function extractAllMappedValues(obj: any, result: globalThis.Record<string, string>): void {
|
|
50
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (Array.isArray(obj)) {
|
|
55
|
+
for (const item of obj) {
|
|
56
|
+
extractAllMappedValues(item, result)
|
|
57
|
+
}
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
62
|
+
if (key.endsWith('.$field_facets')) {
|
|
63
|
+
const facet = (value as any)?.SimpleMapFacet
|
|
64
|
+
if (facet) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(facet)
|
|
67
|
+
if (parsed.mapped) {
|
|
68
|
+
const mappedObj = JSON.parse(parsed.mapped)
|
|
69
|
+
for (const [childName, datapill] of Object.entries(mappedObj)) {
|
|
70
|
+
if (typeof datapill === 'string' && datapill) {
|
|
71
|
+
result[childName] = datapill
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// skip unparseable facets
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
80
|
+
extractAllMappedValues(value, result)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Recursively walk a snapshot's `complexObject` and extract static values
|
|
87
|
+
* from `{ $cv: { $c: "type", $v: "value" } }` structures.
|
|
88
|
+
*/
|
|
89
|
+
function extractAllStaticValues(obj: any, result: globalThis.Record<string, string>): void {
|
|
90
|
+
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
95
|
+
if (key.startsWith('$') || key.endsWith('$')) {
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === 'object' && value !== null && (value as any).$cv) {
|
|
99
|
+
const staticVal = (value as any).$cv.$v
|
|
100
|
+
if (staticVal !== undefined && staticVal !== '') {
|
|
101
|
+
result[key] = String(staticVal)
|
|
102
|
+
}
|
|
103
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
104
|
+
extractAllStaticValues(value, result)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parse a snapshot value JSON for a complex object output and extract
|
|
111
|
+
* per-child value mappings (datapills take priority over static values).
|
|
112
|
+
*/
|
|
113
|
+
function parseSnapshotChildValues(snapshotValue: string | undefined): globalThis.Record<string, string> {
|
|
114
|
+
if (!snapshotValue) {
|
|
115
|
+
return {}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(snapshotValue)
|
|
120
|
+
const result: globalThis.Record<string, string> = {}
|
|
121
|
+
|
|
122
|
+
// Extract datapill mappings from schema (higher priority)
|
|
123
|
+
const schema = parsed.complexObjectSchema
|
|
124
|
+
if (schema) {
|
|
125
|
+
extractAllMappedValues(schema, result)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Extract static values from complexObject (lower priority — only if no datapill)
|
|
129
|
+
const complexObj = parsed.complexObject
|
|
130
|
+
if (complexObj) {
|
|
131
|
+
const staticValues: globalThis.Record<string, string> = {}
|
|
132
|
+
extractAllStaticValues(complexObj, staticValues)
|
|
133
|
+
for (const [k, v] of Object.entries(staticValues)) {
|
|
134
|
+
if (!result[k]) {
|
|
135
|
+
result[k] = v
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result
|
|
141
|
+
} catch {
|
|
142
|
+
return {}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
41
146
|
/**
|
|
42
147
|
* Build a `CallExpressionShape` for a simple (non-object) field given its
|
|
43
148
|
* schema primitive type and associated facets.
|
|
44
149
|
*/
|
|
45
|
-
function buildPrimitiveFieldCallExpr(
|
|
150
|
+
function buildPrimitiveFieldCallExpr(
|
|
151
|
+
fieldFacet: ObjectShape,
|
|
152
|
+
source: FluentRecord,
|
|
153
|
+
childValue?: string
|
|
154
|
+
): CallExpressionShape {
|
|
46
155
|
const childType = fieldFacet.get('childType').ifDefined()?.asString()?.getValue()
|
|
47
156
|
const schemaType = childType ?? fieldFacet.get('type')?.asString()?.getValue()
|
|
48
157
|
const callee = SCHEMA_TYPE_TO_COLUMN_API[schemaType] ?? StringColumn.name
|
|
49
158
|
|
|
50
159
|
const additionalProps: globalThis.Record<string, unknown> = {}
|
|
51
160
|
|
|
52
|
-
// Extract referenceTable for
|
|
161
|
+
// Extract referenceTable for reference and records column types
|
|
53
162
|
const referenceTable = fieldFacet.get('reference')?.ifString()?.getValue()
|
|
54
|
-
if (referenceTable && schemaType === 'reference') {
|
|
163
|
+
if (referenceTable && (schemaType === 'reference' || schemaType === 'records')) {
|
|
55
164
|
additionalProps['referenceTable'] = referenceTable
|
|
56
165
|
}
|
|
57
166
|
|
|
@@ -99,6 +208,7 @@ function buildPrimitiveFieldCallExpr(fieldFacet: ObjectShape, source: FluentReco
|
|
|
99
208
|
})
|
|
100
209
|
.def(choiceDropdown[ChoiceDropdown.DROPDOWN_WITH_NONE]),
|
|
101
210
|
}),
|
|
211
|
+
...(childValue ? { default: $.val(childValue) } : {}),
|
|
102
212
|
}))
|
|
103
213
|
.merge(additionalProps),
|
|
104
214
|
],
|
|
@@ -115,7 +225,8 @@ function buildFlowArrayFromSchema(
|
|
|
115
225
|
source: FluentRecord,
|
|
116
226
|
parentTypeFacet: ObjectShape,
|
|
117
227
|
complexObjectSysId?: string,
|
|
118
|
-
co_type_name?: string
|
|
228
|
+
co_type_name?: string,
|
|
229
|
+
childValues?: globalThis.Record<string, string>
|
|
119
230
|
): CallExpressionShape {
|
|
120
231
|
const rootContent = schemaContent['$COCollectionField']
|
|
121
232
|
if (!rootContent) {
|
|
@@ -129,7 +240,14 @@ function buildFlowArrayFromSchema(
|
|
|
129
240
|
let elementType: CallExpressionShape
|
|
130
241
|
if (type === 'array.object') {
|
|
131
242
|
const childKey = parentTypeFacet.get('childName')?.asString()?.getValue()
|
|
132
|
-
elementType = buildFlowObjectFromSchema(
|
|
243
|
+
elementType = buildFlowObjectFromSchema(
|
|
244
|
+
rootContent[0][childKey],
|
|
245
|
+
source,
|
|
246
|
+
rootTypeFacet,
|
|
247
|
+
undefined,
|
|
248
|
+
undefined,
|
|
249
|
+
childValues
|
|
250
|
+
)
|
|
133
251
|
} else {
|
|
134
252
|
elementType = buildPrimitiveFieldCallExpr(parentTypeFacet, source)
|
|
135
253
|
}
|
|
@@ -163,7 +281,8 @@ function buildFlowArrayFromSchema(
|
|
|
163
281
|
function buildNestedFlowArrayFromSchema(
|
|
164
282
|
schemaContent: Record<string, any>,
|
|
165
283
|
source: FluentRecord,
|
|
166
|
-
parentTypeFacet: ObjectShape
|
|
284
|
+
parentTypeFacet: ObjectShape,
|
|
285
|
+
childValues?: globalThis.Record<string, string>
|
|
167
286
|
): CallExpressionShape {
|
|
168
287
|
const type = parentTypeFacet.get('type')?.asString()?.getValue()
|
|
169
288
|
let elementType: CallExpressionShape
|
|
@@ -175,7 +294,14 @@ function buildNestedFlowArrayFromSchema(
|
|
|
175
294
|
const keys = Object.keys(schemaContent)
|
|
176
295
|
const wrapperKey = keys.find((k) => /^\$\d+_/.test(k)) ?? keys.find((k) => !k.endsWith('.$field_facets'))
|
|
177
296
|
const innerContent = wrapperKey ? schemaContent[wrapperKey] : schemaContent
|
|
178
|
-
elementType = buildFlowObjectFromSchema(
|
|
297
|
+
elementType = buildFlowObjectFromSchema(
|
|
298
|
+
innerContent,
|
|
299
|
+
source,
|
|
300
|
+
parentTypeFacet,
|
|
301
|
+
undefined,
|
|
302
|
+
undefined,
|
|
303
|
+
childValues
|
|
304
|
+
)
|
|
179
305
|
} else {
|
|
180
306
|
elementType = buildPrimitiveFieldCallExpr(parentTypeFacet, source)
|
|
181
307
|
}
|
|
@@ -214,7 +340,9 @@ function buildFlowObjectFromSchema(
|
|
|
214
340
|
source: FluentRecord,
|
|
215
341
|
parentTypeFacet: ObjectShape,
|
|
216
342
|
complexObjectSysId?: string,
|
|
217
|
-
co_type_name?: string
|
|
343
|
+
co_type_name?: string,
|
|
344
|
+
childValues?: globalThis.Record<string, string>,
|
|
345
|
+
topLevelDefault?: string
|
|
218
346
|
): CallExpressionShape {
|
|
219
347
|
const properties: Record<string, unknown> = {}
|
|
220
348
|
|
|
@@ -230,15 +358,22 @@ function buildFlowObjectFromSchema(
|
|
|
230
358
|
throw new Error(`Invalid complex object schema: ${key} facet not found`)
|
|
231
359
|
}
|
|
232
360
|
if (typeof value === 'string') {
|
|
233
|
-
// Primitive field
|
|
234
|
-
properties[key] = buildPrimitiveFieldCallExpr(fieldFacet, source)
|
|
361
|
+
// Primitive field — pass per-child snapshot value if available
|
|
362
|
+
properties[key] = buildPrimitiveFieldCallExpr(fieldFacet, source, childValues?.[key])
|
|
235
363
|
} else if (typeof value === 'object' && value !== null) {
|
|
236
364
|
if (Array.isArray(value)) {
|
|
237
365
|
// Nested FlowArray
|
|
238
|
-
properties[key] = buildNestedFlowArrayFromSchema(value[0], source, fieldFacet)
|
|
366
|
+
properties[key] = buildNestedFlowArrayFromSchema(value[0], source, fieldFacet, childValues)
|
|
239
367
|
} else {
|
|
240
368
|
// Nested FlowObject
|
|
241
|
-
properties[key] = buildFlowObjectFromSchema(
|
|
369
|
+
properties[key] = buildFlowObjectFromSchema(
|
|
370
|
+
value,
|
|
371
|
+
source,
|
|
372
|
+
fieldFacet,
|
|
373
|
+
undefined,
|
|
374
|
+
undefined,
|
|
375
|
+
childValues
|
|
376
|
+
)
|
|
242
377
|
}
|
|
243
378
|
}
|
|
244
379
|
}
|
|
@@ -256,6 +391,7 @@ function buildFlowObjectFromSchema(
|
|
|
256
391
|
.map((label, child) => (child.isDefined() ? child : label))
|
|
257
392
|
.def(''),
|
|
258
393
|
mandatory: $.toBoolean().def(false),
|
|
394
|
+
...(topLevelDefault ? { default: $.val(topLevelDefault) } : {}),
|
|
259
395
|
})),
|
|
260
396
|
],
|
|
261
397
|
})
|
|
@@ -274,7 +410,8 @@ export function buildComplexObjectFromSchema(
|
|
|
274
410
|
schema: Record<string, any>,
|
|
275
411
|
attributes: COAttributes,
|
|
276
412
|
complexObjectSysId?: string,
|
|
277
|
-
co_type_name?: string
|
|
413
|
+
co_type_name?: string,
|
|
414
|
+
snapshotValue?: string
|
|
278
415
|
): CallExpressionShape {
|
|
279
416
|
const { co_type_name: attrCoTypeName, uiType, ...rest } = attributes
|
|
280
417
|
const rootKey = `FlowDesigner:${attrCoTypeName}`
|
|
@@ -292,11 +429,42 @@ export function buildComplexObjectFromSchema(
|
|
|
292
429
|
type: uiType,
|
|
293
430
|
...rest,
|
|
294
431
|
})
|
|
432
|
+
// Parse snapshot value into per-child value mappings (datapills and static values)
|
|
433
|
+
const childValues = parseSnapshotChildValues(snapshotValue)
|
|
434
|
+
const hasChildValues = Object.keys(childValues).length > 0 ? childValues : undefined
|
|
435
|
+
|
|
436
|
+
// Detect plain-string snapshot values (e.g. a whole-object datapill like
|
|
437
|
+
// {{step[UUID].__step_status__}}) that are not valid JSON complex-object
|
|
438
|
+
// structures. These map to a top-level `default` on the FlowObject.
|
|
439
|
+
let topLevelDefault: string | undefined
|
|
440
|
+
if (snapshotValue && !hasChildValues) {
|
|
441
|
+
try {
|
|
442
|
+
JSON.parse(snapshotValue)
|
|
443
|
+
} catch {
|
|
444
|
+
topLevelDefault = snapshotValue
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
295
448
|
// Pass the explicit co_type_name parameter (undefined for deterministic, value for non-deterministic)
|
|
296
449
|
if (uiType.startsWith('array.')) {
|
|
297
|
-
return buildFlowArrayFromSchema(
|
|
450
|
+
return buildFlowArrayFromSchema(
|
|
451
|
+
rootContent,
|
|
452
|
+
source,
|
|
453
|
+
rootTypeFacet,
|
|
454
|
+
complexObjectSysId,
|
|
455
|
+
co_type_name,
|
|
456
|
+
hasChildValues
|
|
457
|
+
)
|
|
298
458
|
}
|
|
299
|
-
return buildFlowObjectFromSchema(
|
|
459
|
+
return buildFlowObjectFromSchema(
|
|
460
|
+
rootContent,
|
|
461
|
+
source,
|
|
462
|
+
rootTypeFacet,
|
|
463
|
+
complexObjectSysId,
|
|
464
|
+
co_type_name,
|
|
465
|
+
hasChildValues,
|
|
466
|
+
topLevelDefault
|
|
467
|
+
)
|
|
300
468
|
}
|
|
301
469
|
|
|
302
470
|
function parseTypeFacet(source: FluentRecord, schema: Record<string, any>, key: string): ObjectShape | undefined {
|
package/src/flow/utils/utils.ts
CHANGED
|
@@ -20,7 +20,13 @@ import { CallExpressionPlugin } from '../../call-expression-plugin'
|
|
|
20
20
|
import { FlowLogicPlugin } from '../flow-logic/flow-logic-plugin'
|
|
21
21
|
import { FlowInstancePlugin } from '../plugins/flow-instance-plugin'
|
|
22
22
|
import { FDInstanceShape } from './flow-shapes'
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
CORE_ACTIONS_PREFIX,
|
|
25
|
+
CORE_ACTIONS_SYS_ID_NAME_MAP,
|
|
26
|
+
BUILT_IN_STEP_PREFIX,
|
|
27
|
+
BUILT_IN_STEP_SYS_ID_NAME_MAP,
|
|
28
|
+
UNSUPPORTED_FLOW_DESCENDANTS,
|
|
29
|
+
} from './flow-constants'
|
|
24
30
|
|
|
25
31
|
export function sysIdToUuid(id: string): string {
|
|
26
32
|
return id.length === 32
|
|
@@ -383,6 +389,11 @@ export const getCoreActionIdentifier = (sysId: string) => {
|
|
|
383
389
|
return actionName ? `${CORE_ACTIONS_PREFIX}.${actionName}` : undefined
|
|
384
390
|
}
|
|
385
391
|
|
|
392
|
+
export const getBuiltInStepIdentifier = (sysId: string) => {
|
|
393
|
+
const stepName = BUILT_IN_STEP_SYS_ID_NAME_MAP[sysId]
|
|
394
|
+
return stepName ? `${BUILT_IN_STEP_PREFIX}.${stepName}` : undefined
|
|
395
|
+
}
|
|
396
|
+
|
|
386
397
|
/**
|
|
387
398
|
* Extracts the order value from a record for sorting purposes.
|
|
388
399
|
* Converts the 'order' field to a number, defaulting to 0 if not present or invalid.
|
package/src/form-plugin.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { create } from 'xmlbuilder2'
|
|
16
16
|
import { createSdkDocEntry, showGuidFieldDiagnostic } from './utils'
|
|
17
17
|
import { NowIdShape } from './now-id-plugin'
|
|
18
|
+
import { AnnotationType, Formatter } from '@servicenow/sdk-core/runtime/ui'
|
|
18
19
|
|
|
19
20
|
const DEFAULT_VIEW = 'Default view'
|
|
20
21
|
const DEFAULT_ANNOTATION_TYPE = '753f88a80f930000b12e6903cfe01206'
|
|
@@ -29,9 +30,6 @@ function buildReverseMap(ns: { [key: string]: string }): Map<string, string> {
|
|
|
29
30
|
return map
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
//TODO:: Import AnnotationType and Formatter from sdk-api once Form plugin is fixed.
|
|
33
|
-
const AnnotationType = {}
|
|
34
|
-
const Formatter = {}
|
|
35
33
|
const ANNOTATION_TYPE_MAP = buildReverseMap(AnnotationType)
|
|
36
34
|
const FORMATTER_MAP = buildReverseMap(Formatter)
|
|
37
35
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { CallExpressionShape, Plugin, isGUID, type Shape, type ShapeTransform } from '@servicenow/sdk-build-core'
|
|
2
|
+
import { NowIdShape } from './now-id-plugin'
|
|
3
|
+
import { NowIncludeShape } from './now-include-plugin'
|
|
4
|
+
import { ModuleFunctionShape } from './server-module-plugin'
|
|
5
|
+
import { createSdkDocEntry, showGuidFieldDiagnostic } from './utils'
|
|
6
|
+
import { parseString, convertRolesToString } from './service-catalog/utils'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Plugin for handling Inbound Email Action records (sysevent_in_email_action).
|
|
10
|
+
*
|
|
11
|
+
* Default Behavior:
|
|
12
|
+
* - When reading from XML: If 'action' field is missing or empty, defaults to 'record_action'
|
|
13
|
+
* - When writing to Fluent: The 'action' field is required in the type definition for proper
|
|
14
|
+
* discriminated union type narrowing
|
|
15
|
+
*/
|
|
16
|
+
export const InboundEmailActionPlugin = Plugin.create({
|
|
17
|
+
name: 'InboundEmailActionPlugin',
|
|
18
|
+
docs: [createSdkDocEntry('InboundEmailAction', ['sysevent_in_email_action'])],
|
|
19
|
+
records: {
|
|
20
|
+
sysevent_in_email_action: {
|
|
21
|
+
async toShape(record, { transform }) {
|
|
22
|
+
const actionType = record.get('action').getValue() || 'record_action'
|
|
23
|
+
|
|
24
|
+
// Process script
|
|
25
|
+
const script = await NowIncludeShape.fromRecord(record, record.get('script'), transform)
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
value: new CallExpressionShape({
|
|
30
|
+
source: record,
|
|
31
|
+
callee: 'InboundEmailAction',
|
|
32
|
+
args: [
|
|
33
|
+
record.transform(({ $ }) => {
|
|
34
|
+
const props: Record<string, ShapeTransform | undefined> = {
|
|
35
|
+
$id: $.val(NowIdShape.from(record)),
|
|
36
|
+
name: $,
|
|
37
|
+
description: $.def(''),
|
|
38
|
+
type: $.def('new'),
|
|
39
|
+
action: $.val(actionType),
|
|
40
|
+
active: $.toBoolean().def(false),
|
|
41
|
+
order: $.toNumber().def(100),
|
|
42
|
+
eventName: $.from('event_name').def('email.read'),
|
|
43
|
+
stopProcessing: $.from('stop_processing').toBoolean().def(false),
|
|
44
|
+
conditionScript: $.from('condition_script').def(''),
|
|
45
|
+
filterCondition: $.from('filter_condition').def(''),
|
|
46
|
+
from: $.def(''),
|
|
47
|
+
requiredRoles: $.from('required_roles')
|
|
48
|
+
.map((v: Shape) => parseString(v))
|
|
49
|
+
.def([]),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
props['replyEmail'] = $.from('reply_email').def('')
|
|
53
|
+
props['table'] = $.def('')
|
|
54
|
+
props['script'] = $.val(script).def('')
|
|
55
|
+
props['fieldAction'] = $.from('template').def('')
|
|
56
|
+
props['assignmentOperator'] = $.from('assignment_operator').def('')
|
|
57
|
+
|
|
58
|
+
return props
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
shapes: [
|
|
67
|
+
{
|
|
68
|
+
shape: CallExpressionShape,
|
|
69
|
+
fileTypes: ['fluent'],
|
|
70
|
+
async toRecord(callExpression, { factory, diagnostics }) {
|
|
71
|
+
if (callExpression.getCallee() !== 'InboundEmailAction') {
|
|
72
|
+
return { success: false }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const args = callExpression.getArgument(0).asObject()
|
|
76
|
+
|
|
77
|
+
// Get action type to validate field restrictions
|
|
78
|
+
const actionArg = args.get('action')
|
|
79
|
+
const actionType = actionArg?.getValue() ?? 'record_action'
|
|
80
|
+
|
|
81
|
+
// Validate that fieldAction can only be used when table is set (for record_action)
|
|
82
|
+
if (actionType === 'record_action') {
|
|
83
|
+
const tableArg = args.get('table')
|
|
84
|
+
const fieldActionArg = args.get('fieldAction')
|
|
85
|
+
const hasTable = tableArg?.isDefined() && !tableArg.toString().isEmpty()
|
|
86
|
+
const hasFieldAction = fieldActionArg?.isDefined() && !fieldActionArg.toString().isEmpty()
|
|
87
|
+
|
|
88
|
+
if (hasFieldAction && !hasTable) {
|
|
89
|
+
diagnostics.error(
|
|
90
|
+
fieldActionArg,
|
|
91
|
+
`Field 'fieldAction' can only be used when 'table' is specified.`
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Validate the 'from' field: must be a GUID string or a Record<'sys_user'>
|
|
97
|
+
const fromArg = args.get('from')
|
|
98
|
+
if (fromArg?.isDefined()) {
|
|
99
|
+
// Validate that if it's a string, it must be a valid GUID
|
|
100
|
+
if (fromArg.isString() && !fromArg.isRecord()) {
|
|
101
|
+
const fromStr = fromArg.asString().getValue()
|
|
102
|
+
if (fromStr !== '' && !isGUID(fromStr)) {
|
|
103
|
+
showGuidFieldDiagnostic(fromArg, 'from', 'sys_user', diagnostics)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const record = await factory.createRecord({
|
|
109
|
+
source: callExpression,
|
|
110
|
+
table: 'sysevent_in_email_action',
|
|
111
|
+
explicitId: args.get('$id'),
|
|
112
|
+
properties: args.transform(({ $ }) => ({
|
|
113
|
+
name: $,
|
|
114
|
+
description: $.def(''),
|
|
115
|
+
table: $,
|
|
116
|
+
type: $.def('new'),
|
|
117
|
+
action: $.def('record_action'),
|
|
118
|
+
active: $.toBoolean().def(false),
|
|
119
|
+
order: $.toNumber().def(100),
|
|
120
|
+
event_name: $.from('eventName').def('email.read'),
|
|
121
|
+
stop_processing: $.from('stopProcessing').toBoolean().def(false),
|
|
122
|
+
script: $.map(
|
|
123
|
+
(v: Shape) =>
|
|
124
|
+
v
|
|
125
|
+
.if(ModuleFunctionShape)
|
|
126
|
+
?.toString(
|
|
127
|
+
(n) => `${n}({{PARAMS}})`,
|
|
128
|
+
['current', 'event', 'email', 'logger', 'classifier']
|
|
129
|
+
) ?? v
|
|
130
|
+
).toCdata(),
|
|
131
|
+
condition_script: $.from('conditionScript').toCdata().def(''),
|
|
132
|
+
filter_condition: $.from('filterCondition').def(''),
|
|
133
|
+
template: $.from('fieldAction').toCdata().def(''),
|
|
134
|
+
from: $.toString(),
|
|
135
|
+
reply_email: $.from('replyEmail').toCdata().def(''),
|
|
136
|
+
required_roles: $.from('requiredRoles').map(convertRolesToString).def(''),
|
|
137
|
+
assignment_operator: $.from('assignmentOperator').def(''),
|
|
138
|
+
})),
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return { success: true, value: record }
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ export * from './data-plugin'
|
|
|
24
24
|
export * from './ui-page-plugin'
|
|
25
25
|
export * from './list-plugin'
|
|
26
26
|
export * from './ui-action-plugin'
|
|
27
|
+
export * from './form-plugin'
|
|
27
28
|
export * from './script-include-plugin'
|
|
28
29
|
export * from './arrow-function-plugin'
|
|
29
30
|
export * from './atf/test-plugin'
|
|
@@ -34,6 +35,8 @@ export * from './service-portal/portal-plugin'
|
|
|
34
35
|
export * from './service-portal/page-plugin'
|
|
35
36
|
export * from './service-portal/theme-plugin'
|
|
36
37
|
export * from './service-portal/menu-plugin'
|
|
38
|
+
export * from './service-portal/header-footer-plugin'
|
|
39
|
+
export * from './service-portal/page-route-map-plugin'
|
|
37
40
|
export * from './rest-api-plugin'
|
|
38
41
|
export * from './html-import-plugin'
|
|
39
42
|
export * from './static-content-plugin'
|
|
@@ -57,6 +60,7 @@ export * from './import-sets-plugin'
|
|
|
57
60
|
export * from './now-attach-plugin'
|
|
58
61
|
export * from './sla-plugin'
|
|
59
62
|
export * from './email-notification-plugin'
|
|
63
|
+
export * from './inbound-email-action-plugin'
|
|
60
64
|
|
|
61
65
|
export * from './service-catalog'
|
|
62
66
|
export * from './ux-list-menu-config-plugin'
|
|
@@ -19,11 +19,6 @@ export const InstanceScanPlugin = Plugin.create({
|
|
|
19
19
|
],
|
|
20
20
|
|
|
21
21
|
records: {
|
|
22
|
-
scan_check: {
|
|
23
|
-
toShape() {
|
|
24
|
-
return { success: false }
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
22
|
[SCAN_COLUMN_TYPE_CHECK]: {
|
|
28
23
|
getUpdateName(record: Record, _context: Context) {
|
|
29
24
|
return { success: true, value: `${SCAN_COLUMN_TYPE_CHECK}_${record.getId().getValue()}` }
|
package/src/property-plugin.ts
CHANGED
|
@@ -70,7 +70,10 @@ export const PropertyPlugin = Plugin.create({
|
|
|
70
70
|
const name = prop.get('name').asString()
|
|
71
71
|
|
|
72
72
|
if (!isSNScope(scope) && config.scope !== 'global' && !name.asString().startsWith(`${scope}.`)) {
|
|
73
|
-
diagnostics.warn(
|
|
73
|
+
diagnostics.warn(
|
|
74
|
+
name.getOriginalNode(),
|
|
75
|
+
`Property names should begin with '${scope}.' to match the app scope and avoid collisions with properties from other scopes`
|
|
76
|
+
)
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
return {
|