@servicenow/sdk-build-plugins 4.3.0 → 4.4.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 +2 -0
- package/dist/acl-plugin.js.map +1 -1
- package/dist/column/column-to-record.d.ts +10 -3
- package/dist/column/column-to-record.js +44 -7
- package/dist/column/column-to-record.js.map +1 -1
- package/dist/column-plugin.d.ts +3 -1
- package/dist/column-plugin.js +11 -11
- package/dist/column-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-instance-plugin.js +285 -10
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-trigger-instance-plugin.js +21 -7
- package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +7 -0
- package/dist/flow/utils/flow-constants.js +12 -5
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/service-catalog.d.ts +47 -0
- package/dist/flow/utils/service-catalog.js +137 -0
- package/dist/flow/utils/service-catalog.js.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/now-attach-plugin.js +7 -10
- package/dist/now-attach-plugin.js.map +1 -1
- package/dist/now-ref-plugin.js +1 -1
- package/dist/now-ref-plugin.js.map +1 -1
- package/dist/server-module-plugin/index.d.ts +10 -0
- package/dist/server-module-plugin/index.js +45 -55
- package/dist/server-module-plugin/index.js.map +1 -1
- package/dist/service-catalog/sc-record-producer-plugin.js +1 -0
- package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
- package/dist/service-catalog/service-catalog-base.js +2 -2
- package/dist/service-catalog/service-catalog-base.js.map +1 -1
- package/dist/service-catalog/service-catalog-diagnostics.js +4 -1
- package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -1
- package/dist/service-catalog/shape-to-record.d.ts +1 -0
- package/dist/service-catalog/shape-to-record.js +4 -1
- package/dist/service-catalog/shape-to-record.js.map +1 -1
- package/dist/service-catalog/utils.d.ts +10 -0
- package/dist/service-catalog/utils.js +72 -0
- package/dist/service-catalog/utils.js.map +1 -1
- package/dist/static-content-plugin.js +25 -2
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +16 -13
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +832 -19
- package/dist/ui-page-plugin.js.map +1 -1
- package/package.json +5 -5
- package/src/acl-plugin.ts +2 -0
- package/src/column/column-to-record.ts +54 -8
- package/src/column-plugin.ts +28 -12
- package/src/flow/plugins/flow-instance-plugin.ts +364 -13
- package/src/flow/plugins/flow-trigger-instance-plugin.ts +25 -7
- package/src/flow/utils/flow-constants.ts +13 -4
- package/src/flow/utils/service-catalog.ts +174 -0
- package/src/index.ts +0 -1
- package/src/now-attach-plugin.ts +10 -11
- package/src/now-ref-plugin.ts +1 -1
- package/src/server-module-plugin/index.ts +59 -69
- package/src/service-catalog/sc-record-producer-plugin.ts +1 -1
- package/src/service-catalog/service-catalog-base.ts +2 -2
- package/src/service-catalog/service-catalog-diagnostics.ts +4 -1
- package/src/service-catalog/shape-to-record.ts +6 -2
- package/src/service-catalog/utils.ts +93 -0
- package/src/static-content-plugin.ts +25 -2
- package/src/table-plugin.ts +30 -14
- package/src/ui-page-plugin.ts +1063 -20
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CallExpressionShape,
|
|
3
|
+
type Database,
|
|
4
|
+
type Diagnostics,
|
|
3
5
|
DurationShape,
|
|
4
6
|
type Factory,
|
|
5
7
|
IdentifierShape,
|
|
@@ -26,10 +28,17 @@ import { resolveComplexInput as _resolveComplexInput } from '../utils/complex-ob
|
|
|
26
28
|
import {
|
|
27
29
|
getCoreActionIdentifier,
|
|
28
30
|
getDirectVariableIdentifier,
|
|
31
|
+
getIdentifierFromRecord,
|
|
29
32
|
getIdentifierFromShape,
|
|
30
33
|
resolveDataPillShape,
|
|
31
34
|
sysIdToUuid,
|
|
32
35
|
} from '../utils/utils'
|
|
36
|
+
import {
|
|
37
|
+
convertSlushBucketToCatalogVariableArray,
|
|
38
|
+
createTemplateExpressionFromIdentifier,
|
|
39
|
+
findCatalogItemBySysId,
|
|
40
|
+
isCatalogAction,
|
|
41
|
+
} from '../utils/service-catalog'
|
|
33
42
|
import { getAttributeValue } from '../utils/schema-to-flow-object'
|
|
34
43
|
import {
|
|
35
44
|
ActionSubflowInstanceShape,
|
|
@@ -44,6 +53,7 @@ import {
|
|
|
44
53
|
ACTION_INSTANCE_API_NAME,
|
|
45
54
|
APPROVAL_RULES_DATA_TYPE_VALUE,
|
|
46
55
|
APPROVAL_DUE_DATE_DATA_TYPE_VALUE,
|
|
56
|
+
CORE_ACTIONS_SYS_ID_NAME_MAP,
|
|
47
57
|
DURATION_DATA_TYPE_VALUE,
|
|
48
58
|
SUBFLOW_INSTANCE_API_NAME,
|
|
49
59
|
TEMPLATE_DATA_TYPE_VALUE,
|
|
@@ -52,7 +62,10 @@ import {
|
|
|
52
62
|
UTC_TIMEZONE_VALUE,
|
|
53
63
|
APPROVAL_DUE_DATE_INPUT_FIELD_ACTIONS,
|
|
54
64
|
ACTION_TYPE_KEY_NAME,
|
|
65
|
+
CATALOG_VARIABLE_TABLE,
|
|
66
|
+
CATALOG_VARIABLE_SET_TABLE,
|
|
55
67
|
} from '../utils/flow-constants'
|
|
68
|
+
|
|
56
69
|
import type { ApprovalDueDateType, ApprovalRulesType } from '@servicenow/sdk-core/runtime/flow'
|
|
57
70
|
import type { Duration } from '@servicenow/sdk-core/runtime/db'
|
|
58
71
|
|
|
@@ -304,8 +317,10 @@ function buildInstanceToShape({
|
|
|
304
317
|
let inputsShape = buildInputsShapeFromZipped({
|
|
305
318
|
zippedInputs,
|
|
306
319
|
definitionInputs,
|
|
320
|
+
actionDefinition: instanceDef,
|
|
307
321
|
record,
|
|
308
322
|
logger,
|
|
323
|
+
database,
|
|
309
324
|
})
|
|
310
325
|
if (inputsShape === undefined) {
|
|
311
326
|
return { success: false as const }
|
|
@@ -361,6 +376,79 @@ function buildInstanceToShape({
|
|
|
361
376
|
}
|
|
362
377
|
}
|
|
363
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Validates that all property accesses on a getCatalogVariables action output variable
|
|
381
|
+
* are declared in its catalog_variables input.
|
|
382
|
+
*
|
|
383
|
+
* Runs once per getCatalogVariables action instance (forward search from declaration to usages)
|
|
384
|
+
* rather than once per template expression (backward search from usage to declaration).
|
|
385
|
+
*/
|
|
386
|
+
function validateCatalogVariableOutputReferences(
|
|
387
|
+
callExpression: ActionSubflowInstanceShape,
|
|
388
|
+
instanceInputs: ObjectShape,
|
|
389
|
+
diagnostics: Diagnostics
|
|
390
|
+
): void {
|
|
391
|
+
// Extract allowed variable names from the catalog_variables input
|
|
392
|
+
const catalogVarsShape = instanceInputs.get('catalog_variables')
|
|
393
|
+
if (!catalogVarsShape) {
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const allowedVariables: string[] = []
|
|
398
|
+
if (catalogVarsShape.isArray()) {
|
|
399
|
+
for (const el of catalogVarsShape.asArray().getElements(false)) {
|
|
400
|
+
if (el instanceof PropertyAccessShape) {
|
|
401
|
+
const propElements = el.getElements()
|
|
402
|
+
const lastEl = propElements[propElements.length - 1]
|
|
403
|
+
if (lastEl instanceof IdentifierShape) {
|
|
404
|
+
allowedVariables.push(lastEl.getName())
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (allowedVariables.length === 0) {
|
|
411
|
+
return
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Navigate from the wfa.action(...) call to its parent variable declaration
|
|
415
|
+
const originalNode = callExpression.getOriginalNode()
|
|
416
|
+
if (!ts.Node.isCallExpression(originalNode)) {
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const parent = originalNode.getParent()
|
|
421
|
+
if (!ts.Node.isVariableDeclaration(parent)) {
|
|
422
|
+
return
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const nameNode = parent.getNameNode()
|
|
426
|
+
if (!ts.Node.isIdentifier(nameNode)) {
|
|
427
|
+
return
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Find all references to the action output variable and validate each property access
|
|
431
|
+
for (const ref of nameNode.findReferencesAsNodes()) {
|
|
432
|
+
const refParent = ref.getParent()
|
|
433
|
+
if (!ts.Node.isPropertyAccessExpression(refParent)) {
|
|
434
|
+
continue
|
|
435
|
+
}
|
|
436
|
+
if (refParent.getExpression() !== ref) {
|
|
437
|
+
continue
|
|
438
|
+
} // ref must be the object base, not the property name
|
|
439
|
+
|
|
440
|
+
const propertyName = refParent.getName()
|
|
441
|
+
if (propertyName && !allowedVariables.includes(propertyName)) {
|
|
442
|
+
diagnostics.error(
|
|
443
|
+
catalogVarsShape,
|
|
444
|
+
`Property '${propertyName}' is not available in the getCatalogVariables output. ` +
|
|
445
|
+
`Only the following variables are accessible: ${allowedVariables.join(', ')}. ` +
|
|
446
|
+
`Please add '${propertyName}' to the catalog_variables input if you need to access it.`
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
364
452
|
export const FlowInstancePlugin = Plugin.create({
|
|
365
453
|
name: 'FlowInstancePlugin',
|
|
366
454
|
records: {
|
|
@@ -411,6 +499,15 @@ export const FlowInstancePlugin = Plugin.create({
|
|
|
411
499
|
let instanceRecord: Record | undefined
|
|
412
500
|
|
|
413
501
|
if (instanceType === ACTION_INSTANCE_API_NAME) {
|
|
502
|
+
// Validate getCatalogVariables output references once, from the declaration
|
|
503
|
+
if (instanceParentDef?.isRecord()) {
|
|
504
|
+
const actionSysId = instanceParentDef.as(Record).getId().getValue()
|
|
505
|
+
const actionName = CORE_ACTIONS_SYS_ID_NAME_MAP[actionSysId]
|
|
506
|
+
if (actionName === 'getCatalogVariables') {
|
|
507
|
+
validateCatalogVariableOutputReferences(callExpression, instanceInputs, diagnostics)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
414
511
|
instanceRecord = await buildActionInstance({
|
|
415
512
|
actionDef: instanceParentDef,
|
|
416
513
|
inputs: instanceInputs,
|
|
@@ -485,13 +582,17 @@ function buildInlineScriptShapeFromXml(
|
|
|
485
582
|
function buildInputsShapeFromZipped({
|
|
486
583
|
zippedInputs,
|
|
487
584
|
definitionInputs,
|
|
585
|
+
actionDefinition,
|
|
488
586
|
record,
|
|
489
587
|
logger,
|
|
588
|
+
database,
|
|
490
589
|
}: {
|
|
491
590
|
zippedInputs: string
|
|
492
591
|
definitionInputs: Record[] | undefined
|
|
592
|
+
actionDefinition: Shape | undefined
|
|
493
593
|
record: Record
|
|
494
594
|
logger: Logger
|
|
595
|
+
database?: Database
|
|
495
596
|
}): ObjectShape | undefined {
|
|
496
597
|
if (!zippedInputs) {
|
|
497
598
|
return undefined
|
|
@@ -509,6 +610,21 @@ function buildInputsShapeFromZipped({
|
|
|
509
610
|
parameter: { type: string; attributes: { [key: string]: unknown } }
|
|
510
611
|
}> = JSON.parse(unzipped)
|
|
511
612
|
|
|
613
|
+
// Check if this is a catalog-related action (getCatalogVariables or createCatalogTask)
|
|
614
|
+
const catalogAction = isCatalogAction(actionDefinition)
|
|
615
|
+
let catalogItemRecord: Record | undefined
|
|
616
|
+
if (catalogAction) {
|
|
617
|
+
// Find the catalog item record
|
|
618
|
+
const templateCatalogItemValue = values.find((v) => v.name === 'template_catalog_item')
|
|
619
|
+
catalogItemRecord = findCatalogItemBySysId(templateCatalogItemValue?.value as string, database)
|
|
620
|
+
if (!catalogItemRecord) {
|
|
621
|
+
logger.warn(
|
|
622
|
+
`Catalog item not found for sys_id '${templateCatalogItemValue?.value}'. ` +
|
|
623
|
+
`template_catalog_item and catalog_variables will fall back to raw string values.`
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
512
628
|
for (const { name, value, scriptActive, script, parameter } of values) {
|
|
513
629
|
const attributes = definitionInputs
|
|
514
630
|
?.find((v) => v.get('element').asString()?.getValue() === name)
|
|
@@ -536,11 +652,31 @@ function buildInputsShapeFromZipped({
|
|
|
536
652
|
// Check if this is an inline script and convert to wfa.inlineScript() format
|
|
537
653
|
const inlineScriptShape = buildInlineScriptShapeFromXml(name, scriptActive, script, record)
|
|
538
654
|
|
|
655
|
+
// Determine the appropriate shape for this input
|
|
656
|
+
let inputShape: Shape | undefined
|
|
657
|
+
|
|
539
658
|
if (inlineScriptShape) {
|
|
540
659
|
props[name] = inlineScriptShape
|
|
541
660
|
} else {
|
|
542
|
-
|
|
543
|
-
|
|
661
|
+
if (catalogAction) {
|
|
662
|
+
if (name === 'template_catalog_item' && catalogItemRecord) {
|
|
663
|
+
// Create template expression: `${catalogItemIdentifier}`
|
|
664
|
+
const catalogItemIdentifier = getIdentifierFromRecord(catalogItemRecord)
|
|
665
|
+
inputShape = catalogItemIdentifier
|
|
666
|
+
? createTemplateExpressionFromIdentifier(catalogItemIdentifier, record)
|
|
667
|
+
: undefined
|
|
668
|
+
} else if (name === 'catalog_variables' && catalogItemRecord) {
|
|
669
|
+
// Convert slushbucket to array of catalog variable references
|
|
670
|
+
inputShape = convertSlushBucketToCatalogVariableArray(
|
|
671
|
+
value as string,
|
|
672
|
+
catalogItemRecord,
|
|
673
|
+
record
|
|
674
|
+
)
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// Use the specialized shape or fallback to normalized value
|
|
678
|
+
props[name] =
|
|
679
|
+
inputShape ?? normalizeInputValue(value as string, uiType ?? parameter.type, record, logger)
|
|
544
680
|
}
|
|
545
681
|
} catch (e) {
|
|
546
682
|
const recordInfo = `${record.getTable()}.${record.getId().getValue()}`
|
|
@@ -767,7 +903,8 @@ function processInputValue(
|
|
|
767
903
|
actionDefRecord?: Record
|
|
768
904
|
inputTableName?: string
|
|
769
905
|
logger?: Logger
|
|
770
|
-
}
|
|
906
|
+
},
|
|
907
|
+
displayValues?: Map<string, string>
|
|
771
908
|
): unknown {
|
|
772
909
|
const value = objShape.get(inputName)
|
|
773
910
|
const primitiveValue = value.getValue()
|
|
@@ -797,7 +934,7 @@ function processInputValue(
|
|
|
797
934
|
return {
|
|
798
935
|
name: inputName,
|
|
799
936
|
value: finalValue,
|
|
800
|
-
displayValue: finalValue,
|
|
937
|
+
displayValue: displayValues && displayValues.has(inputName) ? displayValues.get(inputName) : finalValue,
|
|
801
938
|
scriptActive: false,
|
|
802
939
|
parameter: {
|
|
803
940
|
type: finalType,
|
|
@@ -849,6 +986,178 @@ function processDefaultOrHiddenInput(inputDef: Record, inputName: string): unkno
|
|
|
849
986
|
return undefined
|
|
850
987
|
}
|
|
851
988
|
|
|
989
|
+
/**
|
|
990
|
+
* Resolve a catalog item record from a Shape (direct Record, TemplateExpressionShape, or IdentifierShape).
|
|
991
|
+
*/
|
|
992
|
+
function resolveCatalogItemRecord(shape: Shape): Record | undefined {
|
|
993
|
+
// Handle direct record reference (e.g., softwareInstallationCatalogItem)
|
|
994
|
+
if (shape?.isRecord && shape.isRecord()) {
|
|
995
|
+
return shape.as(Record)
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Handle template expression (e.g., `${softwareInstallationCatalogItem}`)
|
|
999
|
+
if (shape instanceof TemplateExpressionShape) {
|
|
1000
|
+
const spans = shape.getSpans()
|
|
1001
|
+
if (spans[0]) {
|
|
1002
|
+
const expr = spans[0].getExpression()
|
|
1003
|
+
const resolved = expr instanceof IdentifierShape ? expr.resolve() : undefined
|
|
1004
|
+
if (resolved?.isRecord()) {
|
|
1005
|
+
return resolved.as(Record)
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Handle identifier (e.g., variable reference)
|
|
1011
|
+
if (shape instanceof IdentifierShape) {
|
|
1012
|
+
const resolved = shape.resolve()
|
|
1013
|
+
if (resolved?.isRecord()) {
|
|
1014
|
+
return resolved.as(Record)
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
return undefined
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Convert comma-separated variable names to SlushBucket format with sys_ids.
|
|
1023
|
+
* Input: "html,email" + catalog item record
|
|
1024
|
+
* Output: "sys_id1:item_option_new,sys_id2:item_option_new"
|
|
1025
|
+
*/
|
|
1026
|
+
function convertVariableNamesToSlushBucket(variableNames: string, catalogItemRecord: Record, logger: Logger): string {
|
|
1027
|
+
const names = variableNames
|
|
1028
|
+
.split(',')
|
|
1029
|
+
.map((n) => n.trim())
|
|
1030
|
+
.filter(Boolean)
|
|
1031
|
+
if (names.length === 0) {
|
|
1032
|
+
return ''
|
|
1033
|
+
}
|
|
1034
|
+
// Create a map of variable name to sys_id for quick lookup
|
|
1035
|
+
const variableMap = new Map<string, string>()
|
|
1036
|
+
|
|
1037
|
+
// Single loop: flatten records and build the map
|
|
1038
|
+
for (const rec of catalogItemRecord.flat()) {
|
|
1039
|
+
// Check direct record
|
|
1040
|
+
if (rec.getTable() === CATALOG_VARIABLE_TABLE) {
|
|
1041
|
+
const varName = rec.get('name')?.asString()?.getValue()
|
|
1042
|
+
if (varName && !variableMap.has(varName)) {
|
|
1043
|
+
const sysId = rec.getId().getValue()
|
|
1044
|
+
if (sysId && typeof sysId === 'string') {
|
|
1045
|
+
variableMap.set(varName, sysId)
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// Check variable set records and their nested variables
|
|
1050
|
+
if (rec.getTable() === CATALOG_VARIABLE_SET_TABLE) {
|
|
1051
|
+
const variableSetRef = rec.get('variable_set')
|
|
1052
|
+
if (!variableSetRef?.isRecord()) {
|
|
1053
|
+
continue
|
|
1054
|
+
}
|
|
1055
|
+
for (const nestedRec of variableSetRef.flat().filter((r) => r.getTable() === CATALOG_VARIABLE_TABLE)) {
|
|
1056
|
+
const varName = nestedRec.get('name')?.asString()?.getValue()
|
|
1057
|
+
if (varName && !variableMap.has(varName)) {
|
|
1058
|
+
const sysId = nestedRec.getId().getValue()
|
|
1059
|
+
if (sysId) {
|
|
1060
|
+
variableMap.set(varName, sysId)
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Generate entries in the same order as input names
|
|
1068
|
+
const entries: string[] = []
|
|
1069
|
+
for (const name of names) {
|
|
1070
|
+
const variableSysId = variableMap.get(name)
|
|
1071
|
+
if (variableSysId) {
|
|
1072
|
+
entries.push(`${variableSysId}:${CATALOG_VARIABLE_TABLE}`)
|
|
1073
|
+
} else {
|
|
1074
|
+
logger.warn(`Catalog variable '${name}' not found in catalog item. It will be skipped.`)
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
return entries.join(',')
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Process getCatalogVariables action inputs to extract sys_ids and display values.
|
|
1083
|
+
* Handles:
|
|
1084
|
+
* - template_catalog_item: extracts catalog item sys_id and name
|
|
1085
|
+
* - catalog_variables: converts variable names to slushbucket format
|
|
1086
|
+
*/
|
|
1087
|
+
function processGetCatalogVariablesInputs(
|
|
1088
|
+
instanceInputs: ObjectShape,
|
|
1089
|
+
logger: Logger
|
|
1090
|
+
): {
|
|
1091
|
+
overrideValues: Map<string, string>
|
|
1092
|
+
displayValues: Map<string, string>
|
|
1093
|
+
} {
|
|
1094
|
+
const overrideValues = new Map<string, string>()
|
|
1095
|
+
const displayValues = new Map<string, string>()
|
|
1096
|
+
|
|
1097
|
+
// Get catalog item record from template_catalog_item input
|
|
1098
|
+
const templateShape = instanceInputs.get('template_catalog_item')
|
|
1099
|
+
const catalogItemRecord = resolveCatalogItemRecord(templateShape)
|
|
1100
|
+
|
|
1101
|
+
if (!catalogItemRecord) {
|
|
1102
|
+
return { overrideValues, displayValues }
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Extract sys_id and name for template_catalog_item
|
|
1106
|
+
const sysId = catalogItemRecord.getId().getValue()
|
|
1107
|
+
const name = catalogItemRecord.get('name')?.asString()?.getValue()
|
|
1108
|
+
|
|
1109
|
+
if (sysId && typeof sysId === 'string') {
|
|
1110
|
+
overrideValues.set('template_catalog_item', sysId)
|
|
1111
|
+
if (name) {
|
|
1112
|
+
displayValues.set('template_catalog_item', name)
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Convert catalog_variables to slushbucket format
|
|
1117
|
+
const catalogVariablesShape = instanceInputs.get('catalog_variables')
|
|
1118
|
+
|
|
1119
|
+
// Handle string format: "html,email,ipAddress"
|
|
1120
|
+
if (catalogVariablesShape?.isString()) {
|
|
1121
|
+
const variableNames = catalogVariablesShape.asString().getValue()
|
|
1122
|
+
if (variableNames) {
|
|
1123
|
+
const slushBucketString = convertVariableNamesToSlushBucket(variableNames, catalogItemRecord, logger)
|
|
1124
|
+
if (slushBucketString) {
|
|
1125
|
+
overrideValues.set('catalog_variables', slushBucketString)
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
// Handle array format: [catalogItem.variables.html, catalogItem.variables.email, ...]
|
|
1130
|
+
else if (catalogVariablesShape?.isArray()) {
|
|
1131
|
+
const arrayShape = catalogVariablesShape.asArray()
|
|
1132
|
+
const elements = arrayShape.getElements(false) // Get unresolved elements
|
|
1133
|
+
const variableNames: string[] = []
|
|
1134
|
+
|
|
1135
|
+
// Extract variable names from PropertyAccessShapes
|
|
1136
|
+
for (const element of elements) {
|
|
1137
|
+
if (element instanceof PropertyAccessShape) {
|
|
1138
|
+
const propElements = element.getElements()
|
|
1139
|
+
const lastElement = propElements[propElements.length - 1]
|
|
1140
|
+
|
|
1141
|
+
if (lastElement instanceof IdentifierShape) {
|
|
1142
|
+
const varName = lastElement.getName()
|
|
1143
|
+
variableNames.push(varName)
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// Look up sys_ids from catalog item's item_option_new descendants
|
|
1149
|
+
if (variableNames.length > 0) {
|
|
1150
|
+
const variableNamesString = variableNames.join(',')
|
|
1151
|
+
const slushBucketString = convertVariableNamesToSlushBucket(variableNamesString, catalogItemRecord, logger)
|
|
1152
|
+
if (slushBucketString) {
|
|
1153
|
+
overrideValues.set('catalog_variables', slushBucketString)
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return { overrideValues, displayValues }
|
|
1159
|
+
}
|
|
1160
|
+
|
|
852
1161
|
async function prepareActionInstanceValueJson(
|
|
853
1162
|
instanceInputs: ObjectShape | undefined,
|
|
854
1163
|
actionDef: Record | StringShape,
|
|
@@ -860,8 +1169,29 @@ async function prepareActionInstanceValueJson(
|
|
|
860
1169
|
return []
|
|
861
1170
|
}
|
|
862
1171
|
const isActionDefString = actionDef instanceof StringShape
|
|
863
|
-
|
|
864
|
-
|
|
1172
|
+
|
|
1173
|
+
// Special handling for getCatalogVariables action
|
|
1174
|
+
let overrideValues = new Map<string, string>()
|
|
1175
|
+
let displayValues = new Map<string, string>()
|
|
1176
|
+
|
|
1177
|
+
if (actionDefRecord) {
|
|
1178
|
+
const actionSysId = actionDefRecord.getId().getValue()
|
|
1179
|
+
const actionName = typeof actionSysId === 'string' ? CORE_ACTIONS_SYS_ID_NAME_MAP[actionSysId] : undefined
|
|
1180
|
+
|
|
1181
|
+
// Check if this is the getCatalogVariables action or createCatalogTask action
|
|
1182
|
+
if (actionName === 'getCatalogVariables' || actionName === 'createCatalogTask') {
|
|
1183
|
+
try {
|
|
1184
|
+
const result = processGetCatalogVariablesInputs(instanceInputs, logger)
|
|
1185
|
+
overrideValues = result.overrideValues
|
|
1186
|
+
displayValues = result.displayValues
|
|
1187
|
+
} catch (error) {
|
|
1188
|
+
logger.error('[getCatalogVariables] Error:', error)
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Check for datapills and resolve them (skip keys already in overrideValues)
|
|
1194
|
+
const dataPillResults = await checkAndResolveDataPills(instanceInputs, transform, overrideValues)
|
|
865
1195
|
|
|
866
1196
|
// Check for inline script tags and resolve them (only returns scripts)
|
|
867
1197
|
const inlineScriptResults = await checkAndResolveInlineScripts(instanceInputs, transform)
|
|
@@ -869,7 +1199,13 @@ async function prepareActionInstanceValueJson(
|
|
|
869
1199
|
|
|
870
1200
|
// Merge: use inline scripts where they exist, otherwise use datapill results
|
|
871
1201
|
// Note: Inline script can be applied to only top level properties; No need to handle nested objects
|
|
1202
|
+
// Merge results: prioritize overrideValues > inline scripts > datapill results
|
|
872
1203
|
const mergedResults = dataPillResults.map(([key, value]) => {
|
|
1204
|
+
// Use override value if available (catalog item sys_id or slushbucket string)
|
|
1205
|
+
if (overrideValues.has(key)) {
|
|
1206
|
+
return [key, overrideValues.get(key)]
|
|
1207
|
+
}
|
|
1208
|
+
// Otherwise use inline script if available
|
|
873
1209
|
return inlineScriptMap.has(key) ? [key, inlineScriptMap.get(key)] : [key, value]
|
|
874
1210
|
})
|
|
875
1211
|
|
|
@@ -894,11 +1230,16 @@ async function prepareActionInstanceValueJson(
|
|
|
894
1230
|
}
|
|
895
1231
|
// If input is provided in objShape(ie provided by user), process it
|
|
896
1232
|
if (providedInputNames.has(inputName)) {
|
|
897
|
-
const processedValue = processInputValue(
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1233
|
+
const processedValue = processInputValue(
|
|
1234
|
+
inputName,
|
|
1235
|
+
objShape,
|
|
1236
|
+
{
|
|
1237
|
+
actionDefRecord,
|
|
1238
|
+
inputTableName: 'sys_hub_action_input',
|
|
1239
|
+
logger,
|
|
1240
|
+
},
|
|
1241
|
+
displayValues
|
|
1242
|
+
)
|
|
902
1243
|
result.push(processedValue)
|
|
903
1244
|
} else {
|
|
904
1245
|
// Process default or hidden input
|
|
@@ -910,7 +1251,7 @@ async function prepareActionInstanceValueJson(
|
|
|
910
1251
|
}
|
|
911
1252
|
} else if (isActionDefString) {
|
|
912
1253
|
for (const inputName of objShape.keys()) {
|
|
913
|
-
const processedValue = processInputValue(inputName, objShape, { type: 'string' })
|
|
1254
|
+
const processedValue = processInputValue(inputName, objShape, { type: 'string' }, displayValues)
|
|
914
1255
|
result.push(processedValue)
|
|
915
1256
|
}
|
|
916
1257
|
}
|
|
@@ -1087,11 +1428,21 @@ function wrapSpecialShape(shape: Shape, resolvedValue: unknown, source: Source):
|
|
|
1087
1428
|
* @param transform - The transform instance
|
|
1088
1429
|
* @returns Array of [key, value] pairs with resolved datapills
|
|
1089
1430
|
*/
|
|
1090
|
-
async function checkAndResolveDataPills(
|
|
1431
|
+
async function checkAndResolveDataPills(
|
|
1432
|
+
instanceInputs: ObjectShape,
|
|
1433
|
+
transform: Transform,
|
|
1434
|
+
skipKeys?: Map<string, string>
|
|
1435
|
+
) {
|
|
1091
1436
|
const entries = instanceInputs.entries({ resolve: false })
|
|
1092
1437
|
const results: [string, unknown][] = []
|
|
1093
1438
|
|
|
1094
1439
|
for (const [key, shape] of entries) {
|
|
1440
|
+
// Skip datapill resolution for keys that have already been processed (e.g., catalog_variables in array format)
|
|
1441
|
+
// But still add the override value so the key exists in results
|
|
1442
|
+
if (skipKeys?.has(key)) {
|
|
1443
|
+
results.push([key, skipKeys.get(key)])
|
|
1444
|
+
continue
|
|
1445
|
+
}
|
|
1095
1446
|
if (shape && isDataPillShape(shape)) {
|
|
1096
1447
|
const resolvedValue = await resolveDataPillShape(shape, transform)
|
|
1097
1448
|
results.push([key, resolvedValue])
|
|
@@ -26,9 +26,10 @@ export const TriggerInstancePlugin = Plugin.create({
|
|
|
26
26
|
name: 'TriggerInstancePlugin',
|
|
27
27
|
records: {
|
|
28
28
|
sys_hub_trigger_instance_v2: {
|
|
29
|
-
toShape(record, { logger }) {
|
|
29
|
+
toShape(record, { logger, database }) {
|
|
30
30
|
const gzStr = record.get('trigger_inputs').as(StringShape).getValue()
|
|
31
31
|
const inputsObj: globalThis.Record<string, unknown> = {}
|
|
32
|
+
const triggerType = record.get('trigger_type')?.getValue()
|
|
32
33
|
|
|
33
34
|
try {
|
|
34
35
|
const arr = JSON.parse(gunzipSync(Buffer.from(gzStr, 'base64')).toString('utf8')) as Array<{
|
|
@@ -50,10 +51,28 @@ export const TriggerInstancePlugin = Plugin.create({
|
|
|
50
51
|
}
|
|
51
52
|
// Safely extract uiType, guarding against missing parameter or attributes
|
|
52
53
|
const uiType = (parameter?.attributes?.['uiType'] ?? parameter?.type) as string | undefined
|
|
54
|
+
|
|
55
|
+
let finalValue = value
|
|
56
|
+
// For service catalog trigger's run_flow_in, read from sys_flow_catalog_trigger table when empty
|
|
57
|
+
if (
|
|
58
|
+
name === 'run_flow_in' &&
|
|
59
|
+
(value === '' || value === undefined) &&
|
|
60
|
+
triggerType === 'service_catalog'
|
|
61
|
+
) {
|
|
62
|
+
const flowSysId = record.get('flow')?.ifString()?.getValue()
|
|
63
|
+
const flowRecord = flowSysId ? database.get('sys_hub_flow', flowSysId) : undefined
|
|
64
|
+
const remoteTriggerIdValue = flowRecord?.get('remote_trigger_id')?.ifString()?.getValue()
|
|
65
|
+
const catalogTriggerRecord = remoteTriggerIdValue
|
|
66
|
+
? database.get('sys_flow_catalog_trigger', remoteTriggerIdValue)
|
|
67
|
+
: undefined
|
|
68
|
+
const runFlowInValue = catalogTriggerRecord?.get('run_flow_in')?.ifString()?.getValue()
|
|
69
|
+
finalValue = runFlowInValue || 'background'
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
if (name === RUN_ON_EXTENDED) {
|
|
54
|
-
inputsObj[name] =
|
|
73
|
+
inputsObj[name] = finalValue // As this is a choice value, we don't need to normalize it. It should remain as a string. Keep it as 'true' or 'false'
|
|
55
74
|
} else {
|
|
56
|
-
inputsObj[name] = normalizeInputValue(
|
|
75
|
+
inputsObj[name] = normalizeInputValue(finalValue, uiType, record, logger)
|
|
57
76
|
}
|
|
58
77
|
}
|
|
59
78
|
} catch (err: unknown) {
|
|
@@ -68,7 +87,6 @@ export const TriggerInstancePlugin = Plugin.create({
|
|
|
68
87
|
}
|
|
69
88
|
|
|
70
89
|
let triggerIdentifier: IdentifierShape | undefined
|
|
71
|
-
const triggerType = record.get('trigger_type')?.getValue()
|
|
72
90
|
|
|
73
91
|
// Map sys_hub_trigger_instance_v2.trigger_type values to the corresponding
|
|
74
92
|
// `*.now.ts` TriggerDefinition identifier exported from /automation
|
|
@@ -89,7 +107,7 @@ export const TriggerInstancePlugin = Plugin.create({
|
|
|
89
107
|
|
|
90
108
|
// Application triggers
|
|
91
109
|
email: 'trigger.application.inboundEmail',
|
|
92
|
-
|
|
110
|
+
service_catalog: 'trigger.application.serviceCatalog',
|
|
93
111
|
remote_table_query: 'trigger.application.remoteTableQuery',
|
|
94
112
|
sla_task: 'trigger.application.slaTask',
|
|
95
113
|
knowledge_management: 'trigger.application.knowledgeManagement',
|
|
@@ -342,8 +360,8 @@ function createTriggerInputsForTriggerInstance({
|
|
|
342
360
|
userValue = userValue.join(',') // Convert array to comma-separated string
|
|
343
361
|
}
|
|
344
362
|
|
|
345
|
-
// Apply default value if user didn't specify a value
|
|
346
|
-
if (userValue === undefined && fieldProps?.default !== undefined) {
|
|
363
|
+
// Apply default value if user didn't specify a value or provided empty string
|
|
364
|
+
if ((userValue === undefined || userValue === '') && fieldProps?.default !== undefined) {
|
|
347
365
|
userValue = fieldProps.default
|
|
348
366
|
}
|
|
349
367
|
|
|
@@ -10,6 +10,10 @@ export const SUBFLOW_INSTANCE_TABLE_V2 = 'sys_hub_sub_flow_instance_v2'
|
|
|
10
10
|
export const FLOW_LOGIC_INSTANCE_TABLE = 'sys_hub_flow_logic'
|
|
11
11
|
export const FLOW_STAGE_TABLE = 'sys_hub_flow_stage'
|
|
12
12
|
export const PILL_COMPOUND_TABLE = 'sys_hub_pill_compound'
|
|
13
|
+
export const CATALOG_ITEM_TABLE = 'sc_cat_item'
|
|
14
|
+
export const CATALOG_VARIABLE_TABLE = 'item_option_new'
|
|
15
|
+
export const CATALOG_VARIABLE_SET_TABLE = 'io_set_item'
|
|
16
|
+
|
|
13
17
|
export const UNSUPPORTED_FLOW_DESCENDANTS = [
|
|
14
18
|
FLOW_STAGE_TABLE,
|
|
15
19
|
PILL_COMPOUND_TABLE,
|
|
@@ -72,7 +76,7 @@ export const CORE_ACTIONS_SYS_ID_NAME_MAP: { [sysId: string]: string } = {
|
|
|
72
76
|
f8f2e9920b10030085c083eb37673abd: 'askForApproval',
|
|
73
77
|
'7b95a4c0a73133008299b39f087901b4': 'associateRecordToEmail',
|
|
74
78
|
'30fe6cdb0f973300dfd3694e68767e5a': 'copyAttachment',
|
|
75
|
-
|
|
79
|
+
af51fd0e73141300612c273ffff6a785: 'createCatalogTask',
|
|
76
80
|
ab575ae253b3230034c6ddeeff7b12f1: 'createOrUpdateRecord',
|
|
77
81
|
'2de05916c31332002841b63b12d3aee1': 'createRecord',
|
|
78
82
|
'2b3a6335531003003bf1d9109ec58759': 'createTask',
|
|
@@ -80,7 +84,7 @@ export const CORE_ACTIONS_SYS_ID_NAME_MAP: { [sysId: string]: string } = {
|
|
|
80
84
|
c3e09916c31332002841b63b12d3aedf: 'deleteRecord',
|
|
81
85
|
'80a30edeff30311077a95dac793bf19b': 'fireEvent',
|
|
82
86
|
'082e70db0f4273003788001ea8767e02': 'getAttachmentsOnRecord',
|
|
83
|
-
|
|
87
|
+
'330ba3abc31013002841b63b12d3aee8': 'getCatalogVariables',
|
|
84
88
|
a8cfa3efa70223008299b39f0879018b: 'getEmailHeader',
|
|
85
89
|
a159d7820f13101051218e8ebc767eea: 'getLatestResponseTextFromEmail',
|
|
86
90
|
'5bc1bcc6531003003bf1d9109ec587d4': 'log',
|
|
@@ -90,12 +94,12 @@ export const CORE_ACTIONS_SYS_ID_NAME_MAP: { [sysId: string]: string } = {
|
|
|
90
94
|
'6ecdee36935023005e69925cf67ffb47': 'lookupAttachment',
|
|
91
95
|
'7159823353cc10103a33ddeeff7b1216': 'moveAttachment',
|
|
92
96
|
bd8fcd48a73133008299b39f087901fc: 'moveEmailAttachmentsToRecord',
|
|
93
|
-
|
|
97
|
+
'87a397837333001098e194596bf6a7bc': 'recordProducer',
|
|
94
98
|
c1806bf4a70323008299b39f087901cb: 'sendEmail',
|
|
95
99
|
'0d8dd9f10f11101051218e8ebc767e27': 'sendNotification',
|
|
96
100
|
'027499be7333001099792f163cf6a7a7': 'sendSms',
|
|
97
101
|
a1a50c4573873300d70877186bf6a762: 'slaPercentageTimer',
|
|
98
|
-
|
|
102
|
+
c7a04c4607312001098e194596bf6a72a: 'submitCatalogItemRequest',
|
|
99
103
|
'9daf2a72c75100105312134c95c26073': 'updateMultipleRecords',
|
|
100
104
|
f9d01dd2c31332002841b63b12d3aea1: 'updateRecord',
|
|
101
105
|
'89ce8a4187120010663ca1bb36cb0be3': 'waitForCondition',
|
|
@@ -274,3 +278,8 @@ export function slugifyString(value: unknown, opts: SlugifyOptions = {}): string
|
|
|
274
278
|
|
|
275
279
|
return str.toLowerCase()
|
|
276
280
|
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Core actions that require special catalog variable handling
|
|
284
|
+
*/
|
|
285
|
+
export const CATALOG_VARIABLE_ACTIONS = ['getCatalogVariables', 'createCatalogTask']
|