snow-flow 10.0.1-dev.467 → 10.0.1-dev.469
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/package.json
CHANGED
|
@@ -1009,12 +1009,30 @@ async function addActionViaGraphQL(
|
|
|
1009
1009
|
inputResult.actionParams, uuid
|
|
1010
1010
|
);
|
|
1011
1011
|
steps.record_action = recordActionResult.steps;
|
|
1012
|
+
var hasRecordPills = recordActionResult.labelCacheEntries.length > 0;
|
|
1013
|
+
|
|
1014
|
+
// For record actions: clear data pill values from INSERT — they'll be set via separate UPDATE
|
|
1015
|
+
// (Flow Designer's GraphQL API ignores labelCache during INSERT, it only works with UPDATE)
|
|
1016
|
+
var insertInputs = recordActionResult.inputs;
|
|
1017
|
+
if (hasRecordPills) {
|
|
1018
|
+
// Clone inputs and clear data pill values for INSERT
|
|
1019
|
+
insertInputs = recordActionResult.inputs.map(function (inp: any) {
|
|
1020
|
+
if (inp.name === 'record' && inp.value?.value?.startsWith('{{')) {
|
|
1021
|
+
return { ...inp, value: { schemaless: false, schemalessValue: '', value: '' } };
|
|
1022
|
+
}
|
|
1023
|
+
if (inp.name === 'values' && inp.value?.value?.includes('{{')) {
|
|
1024
|
+
return { ...inp, value: { schemaless: false, schemalessValue: '', value: '' } };
|
|
1025
|
+
}
|
|
1026
|
+
return inp;
|
|
1027
|
+
});
|
|
1028
|
+
steps.record_action_strategy = 'two_step';
|
|
1029
|
+
}
|
|
1012
1030
|
|
|
1013
1031
|
const actionResponseFields = 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
1014
1032
|
' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
1015
1033
|
' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
1016
1034
|
|
|
1017
|
-
// Build mutation payload —
|
|
1035
|
+
// Build mutation payload — INSERT with inputs (data pill values cleared for record actions)
|
|
1018
1036
|
const flowPatch: any = {
|
|
1019
1037
|
flowId: flowId,
|
|
1020
1038
|
actions: {
|
|
@@ -1028,25 +1046,62 @@ async function addActionViaGraphQL(
|
|
|
1028
1046
|
uiUniqueIdentifier: uuid,
|
|
1029
1047
|
type: 'action',
|
|
1030
1048
|
parentUiId: parentUiId || '',
|
|
1031
|
-
inputs:
|
|
1049
|
+
inputs: insertInputs
|
|
1032
1050
|
}]
|
|
1033
1051
|
}
|
|
1034
1052
|
};
|
|
1035
1053
|
|
|
1036
|
-
// Add labelCache entries for data pill references in record actions
|
|
1037
|
-
if (recordActionResult.labelCacheEntries.length > 0) {
|
|
1038
|
-
flowPatch.labelCache = { insert: recordActionResult.labelCacheEntries };
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
1054
|
// Add parent flow logic update signal (tells GraphQL the parent was modified)
|
|
1042
1055
|
if (parentUiId) {
|
|
1043
1056
|
flowPatch.flowLogics = { update: [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }] };
|
|
1044
1057
|
}
|
|
1045
1058
|
|
|
1046
1059
|
try {
|
|
1060
|
+
// Step 1: INSERT the action element
|
|
1047
1061
|
const result = await executeFlowPatchMutation(client, flowPatch, actionResponseFields);
|
|
1048
1062
|
const actionId = result?.actions?.inserts?.[0]?.sysId;
|
|
1049
1063
|
steps.insert = { success: !!actionId, actionId, uuid };
|
|
1064
|
+
if (!actionId) return { success: false, steps, error: 'GraphQL action INSERT returned no ID' };
|
|
1065
|
+
|
|
1066
|
+
// Step 2: UPDATE with data pill values + labelCache (separate mutation, matching UI behavior)
|
|
1067
|
+
// The Flow Designer UI always sets data pill references via a separate UPDATE.
|
|
1068
|
+
if (hasRecordPills) {
|
|
1069
|
+
var updateInputs: any[] = [];
|
|
1070
|
+
// Collect all inputs that have data pill values
|
|
1071
|
+
for (var ri = 0; ri < recordActionResult.inputs.length; ri++) {
|
|
1072
|
+
var inp = recordActionResult.inputs[ri];
|
|
1073
|
+
var val = inp.value?.value || '';
|
|
1074
|
+
if (inp.name === 'record' && val.startsWith('{{')) {
|
|
1075
|
+
updateInputs.push({ name: 'record', value: { schemaless: false, schemalessValue: '', value: val } });
|
|
1076
|
+
} else if (inp.name === 'table_name') {
|
|
1077
|
+
updateInputs.push({ name: 'table_name', displayValue: inp.displayValue || { value: '' }, value: inp.value || { value: '' } });
|
|
1078
|
+
} else if (inp.name === 'values' && val) {
|
|
1079
|
+
updateInputs.push({ name: 'values', value: { schemaless: false, schemalessValue: '', value: val } });
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (updateInputs.length > 0) {
|
|
1084
|
+
try {
|
|
1085
|
+
var updatePatch: any = {
|
|
1086
|
+
flowId: flowId,
|
|
1087
|
+
labelCache: { insert: recordActionResult.labelCacheEntries },
|
|
1088
|
+
actions: {
|
|
1089
|
+
update: [{
|
|
1090
|
+
uiUniqueIdentifier: uuid,
|
|
1091
|
+
type: 'action',
|
|
1092
|
+
inputs: updateInputs
|
|
1093
|
+
}]
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
await executeFlowPatchMutation(client, updatePatch, actionResponseFields);
|
|
1097
|
+
steps.record_update = { success: true, inputCount: updateInputs.length };
|
|
1098
|
+
} catch (ue: any) {
|
|
1099
|
+
steps.record_update = { success: false, error: ue.message };
|
|
1100
|
+
// Action was created — update failure is non-fatal
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1050
1105
|
return { success: true, actionId: actionId || undefined, steps };
|
|
1051
1106
|
} catch (e: any) {
|
|
1052
1107
|
steps.insert = { success: false, error: e.message };
|
|
@@ -1268,58 +1323,81 @@ function transformConditionToDataPills(conditionValue: string, dataPillBase: str
|
|
|
1268
1323
|
* Build labelCache entries for field-level data pills used in flow logic conditions.
|
|
1269
1324
|
*
|
|
1270
1325
|
* Each field referenced in the condition needs a labelCache entry so the Flow Designer UI
|
|
1271
|
-
* can display the data pill correctly.
|
|
1326
|
+
* can display the data pill correctly. Queries sys_dictionary for actual field types/labels.
|
|
1327
|
+
*
|
|
1328
|
+
* The UI only sends field-level pills (NOT a record-level parent pill).
|
|
1329
|
+
* Each entry includes parent_table_name and column_name for the UI to resolve the field.
|
|
1272
1330
|
*
|
|
1273
|
-
* Example for "category=
|
|
1274
|
-
* -
|
|
1275
|
-
* - Field pill: { name: "Created or Updated_1.current.category", type: "string" }
|
|
1331
|
+
* Example for "category=inquiry" on incident table:
|
|
1332
|
+
* - { name: "Created or Updated_1.current.category", type: "choice", parent_table_name: "incident", column_name: "category" }
|
|
1276
1333
|
*/
|
|
1277
|
-
function buildConditionLabelCache(
|
|
1334
|
+
async function buildConditionLabelCache(
|
|
1335
|
+
client: any,
|
|
1278
1336
|
conditionValue: string,
|
|
1279
1337
|
dataPillBase: string,
|
|
1280
1338
|
triggerName: string,
|
|
1281
1339
|
table: string,
|
|
1282
1340
|
tableLabel: string,
|
|
1283
1341
|
logicUiId: string
|
|
1284
|
-
): any[] {
|
|
1342
|
+
): Promise<any[]> {
|
|
1285
1343
|
if (!dataPillBase) return [];
|
|
1286
1344
|
|
|
1287
1345
|
var clauses = parseEncodedQuery(conditionValue);
|
|
1288
|
-
|
|
1289
|
-
var seen: Record<string, boolean> = {};
|
|
1290
|
-
|
|
1291
|
-
// Register the record-level parent pill (always needed as context)
|
|
1292
|
-
entries.push({
|
|
1293
|
-
name: dataPillBase,
|
|
1294
|
-
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record',
|
|
1295
|
-
reference: table,
|
|
1296
|
-
reference_display: tableLabel,
|
|
1297
|
-
type: 'reference',
|
|
1298
|
-
base_type: 'reference',
|
|
1299
|
-
attributes: '',
|
|
1300
|
-
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1301
|
-
choices: {}
|
|
1302
|
-
});
|
|
1303
|
-
seen[dataPillBase] = true;
|
|
1346
|
+
if (clauses.length === 0) return [];
|
|
1304
1347
|
|
|
1305
|
-
//
|
|
1348
|
+
// Collect unique field names
|
|
1349
|
+
var uniqueFields: string[] = [];
|
|
1350
|
+
var seen: Record<string, boolean> = {};
|
|
1306
1351
|
for (var i = 0; i < clauses.length; i++) {
|
|
1307
1352
|
var field = clauses[i].field;
|
|
1308
|
-
if (!field)
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1353
|
+
if (field && !seen[field]) {
|
|
1354
|
+
seen[field] = true;
|
|
1355
|
+
uniqueFields.push(field);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
if (uniqueFields.length === 0) return [];
|
|
1312
1359
|
|
|
1313
|
-
|
|
1360
|
+
// Batch-query sys_dictionary for field metadata (type, label)
|
|
1361
|
+
var fieldMeta: Record<string, { type: string; label: string }> = {};
|
|
1362
|
+
try {
|
|
1363
|
+
var dictResp = await client.get('/api/now/table/sys_dictionary', {
|
|
1364
|
+
params: {
|
|
1365
|
+
sysparm_query: 'name=' + table + '^elementIN' + uniqueFields.join(','),
|
|
1366
|
+
sysparm_fields: 'element,column_label,internal_type',
|
|
1367
|
+
sysparm_display_value: 'false',
|
|
1368
|
+
sysparm_limit: uniqueFields.length + 5
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
var dictResults = dictResp.data.result || [];
|
|
1372
|
+
for (var d = 0; d < dictResults.length; d++) {
|
|
1373
|
+
var rec = dictResults[d];
|
|
1374
|
+
var elName = str(rec.element);
|
|
1375
|
+
var intType = str(rec.internal_type?.value || rec.internal_type || 'string');
|
|
1376
|
+
var colLabel = str(rec.column_label);
|
|
1377
|
+
if (elName) fieldMeta[elName] = { type: intType, label: colLabel };
|
|
1378
|
+
}
|
|
1379
|
+
} catch (_) {
|
|
1380
|
+
// Fallback: use "string" type and generated labels if dictionary lookup fails
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Build field-level labelCache entries (no record-level parent — UI doesn't send one)
|
|
1384
|
+
var entries: any[] = [];
|
|
1385
|
+
for (var j = 0; j < uniqueFields.length; j++) {
|
|
1386
|
+
var f = uniqueFields[j];
|
|
1387
|
+
var meta = fieldMeta[f];
|
|
1388
|
+
var fType = meta ? meta.type : 'string';
|
|
1389
|
+
var fLabel = meta ? meta.label : f.replace(/_/g, ' ').replace(/\b\w/g, function (c: string) { return c.toUpperCase(); });
|
|
1390
|
+
var pillName = dataPillBase + '.' + f;
|
|
1314
1391
|
|
|
1315
1392
|
entries.push({
|
|
1316
1393
|
name: pillName,
|
|
1317
|
-
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record\u27a1' +
|
|
1394
|
+
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record\u27a1' + fLabel,
|
|
1318
1395
|
reference: '',
|
|
1319
|
-
reference_display:
|
|
1320
|
-
type:
|
|
1321
|
-
base_type:
|
|
1322
|
-
|
|
1396
|
+
reference_display: fLabel,
|
|
1397
|
+
type: fType,
|
|
1398
|
+
base_type: fType,
|
|
1399
|
+
parent_table_name: table,
|
|
1400
|
+
column_name: f,
|
|
1323
1401
|
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1324
1402
|
choices: {}
|
|
1325
1403
|
});
|
|
@@ -1655,8 +1733,8 @@ async function addFlowLogicViaGraphQL(
|
|
|
1655
1733
|
if (needsConditionUpdate && conditionTriggerInfo) {
|
|
1656
1734
|
var dataPillBase = conditionTriggerInfo.dataPillBase;
|
|
1657
1735
|
var transformedCondition = transformConditionToDataPills(rawCondition, dataPillBase);
|
|
1658
|
-
var labelCacheEntries = buildConditionLabelCache(
|
|
1659
|
-
rawCondition, dataPillBase, conditionTriggerInfo.triggerName,
|
|
1736
|
+
var labelCacheEntries = await buildConditionLabelCache(
|
|
1737
|
+
client, rawCondition, dataPillBase, conditionTriggerInfo.triggerName,
|
|
1660
1738
|
conditionTriggerInfo.tableRef, conditionTriggerInfo.tableLabel, returnedUuid
|
|
1661
1739
|
);
|
|
1662
1740
|
|
|
@@ -1673,8 +1751,13 @@ async function addFlowLogicViaGraphQL(
|
|
|
1673
1751
|
type: 'flowlogic',
|
|
1674
1752
|
inputs: [{
|
|
1675
1753
|
name: 'condition',
|
|
1754
|
+
displayValue: { value: '' },
|
|
1676
1755
|
value: { schemaless: false, schemalessValue: '', value: transformedCondition }
|
|
1677
|
-
}]
|
|
1756
|
+
}],
|
|
1757
|
+
flowLogicDefinition: {
|
|
1758
|
+
inputs: [{ name: 'condition_name', attributes: 'use_basic_input=true,' }],
|
|
1759
|
+
variables: 'undefined'
|
|
1760
|
+
}
|
|
1678
1761
|
}]
|
|
1679
1762
|
}
|
|
1680
1763
|
};
|