@servicenow/sdk-build-plugins 4.3.0 → 4.4.1

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.
Files changed (64) hide show
  1. package/dist/acl-plugin.js +2 -0
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/column/column-to-record.d.ts +10 -3
  4. package/dist/column/column-to-record.js +44 -7
  5. package/dist/column/column-to-record.js.map +1 -1
  6. package/dist/column-plugin.d.ts +3 -1
  7. package/dist/column-plugin.js +11 -11
  8. package/dist/column-plugin.js.map +1 -1
  9. package/dist/flow/plugins/flow-instance-plugin.js +285 -10
  10. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  11. package/dist/flow/plugins/flow-trigger-instance-plugin.js +21 -7
  12. package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
  13. package/dist/flow/utils/flow-constants.d.ts +7 -0
  14. package/dist/flow/utils/flow-constants.js +12 -5
  15. package/dist/flow/utils/flow-constants.js.map +1 -1
  16. package/dist/flow/utils/service-catalog.d.ts +47 -0
  17. package/dist/flow/utils/service-catalog.js +137 -0
  18. package/dist/flow/utils/service-catalog.js.map +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/now-attach-plugin.js +7 -10
  21. package/dist/now-attach-plugin.js.map +1 -1
  22. package/dist/now-ref-plugin.js +1 -1
  23. package/dist/now-ref-plugin.js.map +1 -1
  24. package/dist/server-module-plugin/index.d.ts +10 -0
  25. package/dist/server-module-plugin/index.js +45 -55
  26. package/dist/server-module-plugin/index.js.map +1 -1
  27. package/dist/service-catalog/sc-record-producer-plugin.js +1 -0
  28. package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
  29. package/dist/service-catalog/service-catalog-base.js +2 -2
  30. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  31. package/dist/service-catalog/service-catalog-diagnostics.js +4 -1
  32. package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -1
  33. package/dist/service-catalog/shape-to-record.d.ts +1 -0
  34. package/dist/service-catalog/shape-to-record.js +4 -1
  35. package/dist/service-catalog/shape-to-record.js.map +1 -1
  36. package/dist/service-catalog/utils.d.ts +10 -0
  37. package/dist/service-catalog/utils.js +72 -0
  38. package/dist/service-catalog/utils.js.map +1 -1
  39. package/dist/static-content-plugin.js +25 -2
  40. package/dist/static-content-plugin.js.map +1 -1
  41. package/dist/table-plugin.js +16 -13
  42. package/dist/table-plugin.js.map +1 -1
  43. package/dist/ui-page-plugin.js +832 -19
  44. package/dist/ui-page-plugin.js.map +1 -1
  45. package/package.json +5 -5
  46. package/src/acl-plugin.ts +2 -0
  47. package/src/column/column-to-record.ts +54 -8
  48. package/src/column-plugin.ts +28 -12
  49. package/src/flow/plugins/flow-instance-plugin.ts +364 -13
  50. package/src/flow/plugins/flow-trigger-instance-plugin.ts +25 -7
  51. package/src/flow/utils/flow-constants.ts +13 -4
  52. package/src/flow/utils/service-catalog.ts +174 -0
  53. package/src/index.ts +0 -1
  54. package/src/now-attach-plugin.ts +10 -11
  55. package/src/now-ref-plugin.ts +1 -1
  56. package/src/server-module-plugin/index.ts +59 -69
  57. package/src/service-catalog/sc-record-producer-plugin.ts +1 -1
  58. package/src/service-catalog/service-catalog-base.ts +2 -2
  59. package/src/service-catalog/service-catalog-diagnostics.ts +4 -1
  60. package/src/service-catalog/shape-to-record.ts +6 -2
  61. package/src/service-catalog/utils.ts +93 -0
  62. package/src/static-content-plugin.ts +25 -2
  63. package/src/table-plugin.ts +30 -14
  64. 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
- // Use displayValue for proper type conversion (e.g., boolean: '1' → true)
543
- props[name] = normalizeInputValue(value as string, uiType ?? parameter.type, record, logger)
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
- // Check for datapills and resolve them
864
- const dataPillResults = await checkAndResolveDataPills(instanceInputs, transform)
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(inputName, objShape, {
898
- actionDefRecord,
899
- inputTableName: 'sys_hub_action_input',
900
- logger,
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(instanceInputs: ObjectShape, transform: Transform) {
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] = value // 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'
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(value, uiType, record, logger)
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
- // service_catalog: 'trigger.application.serviceCatalog',
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
- // af51fd0e73141300612c273ffff6a785: 'createCatalogTask',
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
- // '330ba3abc31013002841b63b12d3aee8': 'getCatalogVariables',
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
- // '87a397837333001098e194596bf6a7bc': 'recordProducer',
97
+ '87a397837333001098e194596bf6a7bc': 'recordProducer',
94
98
  c1806bf4a70323008299b39f087901cb: 'sendEmail',
95
99
  '0d8dd9f10f11101051218e8ebc767e27': 'sendNotification',
96
100
  '027499be7333001099792f163cf6a7a7': 'sendSms',
97
101
  a1a50c4573873300d70877186bf6a762: 'slaPercentageTimer',
98
- // c7a04c4607312001098e194596bf6a72a: 'submitCatalogItemRequest',
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']