snow-flow 10.0.1-dev.480 → 10.0.1-dev.481
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
|
@@ -1222,9 +1222,9 @@ async function getFlowTriggerInfo(
|
|
|
1222
1222
|
// This API returns XML (not JSON). We parse trigger info from the XML string.
|
|
1223
1223
|
try {
|
|
1224
1224
|
debug.processflow_api = 'attempting';
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1225
|
+
// Note: do NOT pass custom headers in config — some Axios interceptors freeze the config
|
|
1226
|
+
// object, causing "Attempted to assign to readonly property" errors.
|
|
1227
|
+
var pfResp = await client.get('/api/now/processflow/flow/' + flowId);
|
|
1228
1228
|
var pfRaw = pfResp.data;
|
|
1229
1229
|
debug.processflow_api = 'success';
|
|
1230
1230
|
debug.processflow_type = typeof pfRaw;
|
|
@@ -1300,16 +1300,28 @@ async function getFlowTriggerInfo(
|
|
|
1300
1300
|
var parsed = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
|
1301
1301
|
debug.version_payload_keys = Object.keys(parsed);
|
|
1302
1302
|
var trigInst = parsed.triggerInstances || parsed.trigger_instances || [];
|
|
1303
|
+
debug.version_trigger_count = Array.isArray(trigInst) ? trigInst.length : typeof trigInst;
|
|
1303
1304
|
if (Array.isArray(trigInst) && trigInst.length > 0) {
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1305
|
+
var t0 = trigInst[0];
|
|
1306
|
+
debug.version_trigger_keys = Object.keys(t0);
|
|
1307
|
+
debug.version_trigger_name_field = t0.name || t0.triggerName || t0.triggerDefinitionName || '';
|
|
1308
|
+
if (!triggerName) triggerName = t0.name || t0.triggerName || t0.triggerDefinitionName || '';
|
|
1309
|
+
// Also try getting the trigger definition name (e.g. "Created or Updated")
|
|
1310
|
+
if (!triggerName && t0.triggerDefinition) {
|
|
1311
|
+
triggerName = t0.triggerDefinition.name || '';
|
|
1312
|
+
}
|
|
1313
|
+
if (!table && t0.inputs) {
|
|
1314
|
+
var t0Inputs = Array.isArray(t0.inputs) ? t0.inputs : [];
|
|
1315
|
+
for (var vi = 0; vi < t0Inputs.length; vi++) {
|
|
1316
|
+
if (t0Inputs[vi].name === 'table') {
|
|
1317
|
+
table = t0Inputs[vi].value?.value || str(t0Inputs[vi].value) || '';
|
|
1318
|
+
debug.version_table_input = t0Inputs[vi];
|
|
1309
1319
|
break;
|
|
1310
1320
|
}
|
|
1311
1321
|
}
|
|
1312
1322
|
}
|
|
1323
|
+
// Fallback: try reading table directly from trigger object
|
|
1324
|
+
if (!table && t0.table) table = str(t0.table);
|
|
1313
1325
|
}
|
|
1314
1326
|
}
|
|
1315
1327
|
} catch (_) {}
|
|
@@ -1494,14 +1506,20 @@ function transformConditionToDataPills(conditionValue: string, dataPillBase: str
|
|
|
1494
1506
|
}
|
|
1495
1507
|
|
|
1496
1508
|
/**
|
|
1497
|
-
* Build labelCache entries for field-level data pills used in flow logic conditions.
|
|
1509
|
+
* Build labelCache INSERT entries for field-level data pills used in flow logic conditions.
|
|
1498
1510
|
*
|
|
1499
|
-
* Returns
|
|
1500
|
-
*
|
|
1501
|
-
* - updates: field-level pills with minimal fields (name + usedInstances, matching UI mutation)
|
|
1511
|
+
* Returns an array of labelCache INSERT entries with full metadata, matching the UI's exact
|
|
1512
|
+
* mutation format (captured from Flow Designer network tab when editing a programmatic flow):
|
|
1502
1513
|
*
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1514
|
+
* labelCache: { insert: [{
|
|
1515
|
+
* name: "Created or Updated_1.current.category",
|
|
1516
|
+
* label: "Trigger - Record Created or Updated➛Incident Record➛Category",
|
|
1517
|
+
* reference: "", reference_display: "Category",
|
|
1518
|
+
* type: "choice", base_type: "choice",
|
|
1519
|
+
* parent_table_name: "incident", column_name: "category",
|
|
1520
|
+
* usedInstances: [{uiUniqueIdentifier: "...", inputName: "condition"}],
|
|
1521
|
+
* choices: {}
|
|
1522
|
+
* }] }
|
|
1505
1523
|
*/
|
|
1506
1524
|
async function buildConditionLabelCache(
|
|
1507
1525
|
client: any,
|
|
@@ -1535,27 +1553,54 @@ async function buildConditionLabelCache(
|
|
|
1535
1553
|
}
|
|
1536
1554
|
if (uniqueFields.length === 0) return [];
|
|
1537
1555
|
|
|
1538
|
-
//
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1556
|
+
// Batch-query sys_dictionary for field metadata (type, label, reference)
|
|
1557
|
+
var fieldMeta: Record<string, { type: string; label: string; reference: string }> = {};
|
|
1558
|
+
try {
|
|
1559
|
+
var dictResp = await client.get('/api/now/table/sys_dictionary', {
|
|
1560
|
+
params: {
|
|
1561
|
+
sysparm_query: 'name=' + table + '^elementIN' + uniqueFields.join(','),
|
|
1562
|
+
sysparm_fields: 'element,column_label,internal_type,reference',
|
|
1563
|
+
sysparm_display_value: 'false',
|
|
1564
|
+
sysparm_limit: uniqueFields.length + 5
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
var dictResults = dictResp.data.result || [];
|
|
1568
|
+
for (var d = 0; d < dictResults.length; d++) {
|
|
1569
|
+
var rec = dictResults[d];
|
|
1570
|
+
var elName = str(rec.element);
|
|
1571
|
+
var intType = str(rec.internal_type?.value || rec.internal_type || 'string');
|
|
1572
|
+
var colLabel = str(rec.column_label);
|
|
1573
|
+
var refTable = str(rec.reference?.value || rec.reference || '');
|
|
1574
|
+
if (elName) fieldMeta[elName] = { type: intType, label: colLabel, reference: refTable };
|
|
1575
|
+
}
|
|
1576
|
+
} catch (_) {
|
|
1577
|
+
// Fallback: use "string" type and generated labels if dictionary lookup fails
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// Build labelCache INSERT entries with full metadata for each field-level pill.
|
|
1581
|
+
// This matches the UI's mutation format when editing a programmatically-created flow.
|
|
1582
|
+
var inserts: any[] = [];
|
|
1547
1583
|
|
|
1548
1584
|
for (var j = 0; j < uniqueFields.length; j++) {
|
|
1549
1585
|
var f = uniqueFields[j];
|
|
1550
1586
|
var pillName = dataPillBase + '.' + f;
|
|
1587
|
+
var meta = fieldMeta[f] || { type: 'string', label: f.replace(/_/g, ' ').replace(/\b\w/g, function (c: string) { return c.toUpperCase(); }), reference: '' };
|
|
1551
1588
|
|
|
1552
|
-
|
|
1589
|
+
inserts.push({
|
|
1553
1590
|
name: pillName,
|
|
1554
|
-
|
|
1591
|
+
label: 'Trigger - Record ' + triggerName + '\u279b' + tableLabel + ' Record\u279b' + meta.label,
|
|
1592
|
+
reference: meta.reference,
|
|
1593
|
+
reference_display: meta.label,
|
|
1594
|
+
type: meta.type,
|
|
1595
|
+
base_type: meta.type,
|
|
1596
|
+
parent_table_name: table,
|
|
1597
|
+
column_name: f,
|
|
1598
|
+
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1599
|
+
choices: {}
|
|
1555
1600
|
});
|
|
1556
1601
|
}
|
|
1557
1602
|
|
|
1558
|
-
return
|
|
1603
|
+
return inserts;
|
|
1559
1604
|
}
|
|
1560
1605
|
|
|
1561
1606
|
// ── DATA PILL SUPPORT FOR RECORD ACTIONS (Update/Create Record) ──────
|
|
@@ -1886,22 +1931,41 @@ async function addFlowLogicViaGraphQL(
|
|
|
1886
1931
|
var needsConditionUpdate = false;
|
|
1887
1932
|
var conditionTriggerInfo: any = null;
|
|
1888
1933
|
|
|
1889
|
-
// Pre-process: detect
|
|
1890
|
-
//
|
|
1891
|
-
// "trigger.current.category = software"
|
|
1892
|
-
// "trigger.current.category == 'software'"
|
|
1893
|
-
// "current.
|
|
1894
|
-
//
|
|
1895
|
-
// "{{
|
|
1896
|
-
var
|
|
1897
|
-
|
|
1934
|
+
// Pre-process: detect dot notation conditions and convert to shorthand pill format.
|
|
1935
|
+
// Supports both symbol operators and word operators:
|
|
1936
|
+
// "trigger.current.category = software" → "{{trigger.current.category}}=software"
|
|
1937
|
+
// "trigger.current.category == 'software'" → "{{trigger.current.category}}=software"
|
|
1938
|
+
// "trigger.current.category equals software" → "{{trigger.current.category}}=software"
|
|
1939
|
+
// "trigger.current.priority != 1" → "{{trigger.current.priority}}!=1"
|
|
1940
|
+
// "current.active is true" → "{{current.active}}=true"
|
|
1941
|
+
var WORD_OP_MAP: Record<string, string> = {
|
|
1942
|
+
'equals': '=', 'is': '=', 'eq': '=',
|
|
1943
|
+
'not_equals': '!=', 'is not': '!=', 'neq': '!=', 'not equals': '!=',
|
|
1944
|
+
'greater than': '>', 'gt': '>', 'less than': '<', 'lt': '<',
|
|
1945
|
+
'greater or equals': '>=', 'gte': '>=', 'less or equals': '<=', 'lte': '<=',
|
|
1946
|
+
'contains': 'LIKE', 'starts with': 'STARTSWITH', 'ends with': 'ENDSWITH',
|
|
1947
|
+
'not contains': 'NOT LIKE', 'is empty': 'ISEMPTY', 'is not empty': 'ISNOTEMPTY'
|
|
1948
|
+
};
|
|
1949
|
+
// First replace word operators with symbols so the regex can match uniformly
|
|
1950
|
+
var dotOriginal = rawCondition;
|
|
1951
|
+
var dotProcessed = rawCondition;
|
|
1952
|
+
var WORD_OPS_SORTED = Object.keys(WORD_OP_MAP).sort(function (a, b) { return b.length - a.length; }); // longest first
|
|
1953
|
+
for (var wi = 0; wi < WORD_OPS_SORTED.length; wi++) {
|
|
1954
|
+
var wordOp = WORD_OPS_SORTED[wi];
|
|
1955
|
+
// Only replace word operators that appear between a dot-notation field and a value
|
|
1956
|
+
var wordRe = new RegExp('((?:trigger\\.)?current\\.\\w+)\\s+' + wordOp.replace(/ /g, '\\s+') + '\\s+', 'gi');
|
|
1957
|
+
dotProcessed = dotProcessed.replace(wordRe, function (m: string, prefix: string) {
|
|
1958
|
+
return prefix + WORD_OP_MAP[wordOp];
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
var DOT_NOTATION_RE = /((?:trigger\.)?current)\.(\w+)(===?|!==?|>=|<=|>|<|=|LIKE|STARTSWITH|ENDSWITH|NOT LIKE|ISEMPTY|ISNOTEMPTY)\s*(?:'([^']*)'|"([^"]*)"|(\S*))/g;
|
|
1962
|
+
if (DOT_NOTATION_RE.test(dotProcessed)) {
|
|
1898
1963
|
DOT_NOTATION_RE.lastIndex = 0;
|
|
1899
|
-
|
|
1900
|
-
rawCondition = rawCondition.replace(DOT_NOTATION_RE, function (_m: string, prefix: string, field: string, op: string, qv1: string, qv2: string, uv: string) {
|
|
1964
|
+
rawCondition = dotProcessed.replace(DOT_NOTATION_RE, function (_m: string, prefix: string, field: string, op: string, qv1: string, qv2: string, uv: string) {
|
|
1901
1965
|
var snOp = op;
|
|
1902
1966
|
if (op === '==' || op === '===') snOp = '=';
|
|
1903
1967
|
else if (op === '!=' || op === '!==') snOp = '!=';
|
|
1904
|
-
var val = qv1 !== undefined ? qv1 : (qv2 !== undefined ? qv2 : uv);
|
|
1968
|
+
var val = qv1 !== undefined ? qv1 : (qv2 !== undefined ? qv2 : (uv || ''));
|
|
1905
1969
|
return '{{' + prefix + '.' + field + '}}' + snOp + val;
|
|
1906
1970
|
});
|
|
1907
1971
|
// Replace JS && with ServiceNow ^ (AND separator)
|
|
@@ -1909,6 +1973,29 @@ async function addFlowLogicViaGraphQL(
|
|
|
1909
1973
|
steps.dot_notation_rewrite = { original: dotOriginal, rewritten: rawCondition };
|
|
1910
1974
|
}
|
|
1911
1975
|
|
|
1976
|
+
// Pre-process: convert word operators in pill-format conditions
|
|
1977
|
+
// e.g. "{{trigger.current.category}} equals software" → "{{trigger.current.category}}=software"
|
|
1978
|
+
if (rawCondition.includes('{{')) {
|
|
1979
|
+
var PILL_WORD_OPS: [RegExp, string][] = [
|
|
1980
|
+
[/(\}\})\s+not\s+equals\s+/gi, '$1!='],
|
|
1981
|
+
[/(\}\})\s+is\s+not\s+/gi, '$1!='],
|
|
1982
|
+
[/(\}\})\s+equals\s+/gi, '$1='],
|
|
1983
|
+
[/(\}\})\s+is\s+/gi, '$1='],
|
|
1984
|
+
[/(\}\})\s+contains\s+/gi, '$1LIKE'],
|
|
1985
|
+
[/(\}\})\s+starts\s+with\s+/gi, '$1STARTSWITH'],
|
|
1986
|
+
[/(\}\})\s+ends\s+with\s+/gi, '$1ENDSWITH'],
|
|
1987
|
+
[/(\}\})\s+greater\s+than\s+/gi, '$1>'],
|
|
1988
|
+
[/(\}\})\s+less\s+than\s+/gi, '$1<'],
|
|
1989
|
+
];
|
|
1990
|
+
var pillWordOriginal = rawCondition;
|
|
1991
|
+
for (var pwi = 0; pwi < PILL_WORD_OPS.length; pwi++) {
|
|
1992
|
+
rawCondition = rawCondition.replace(PILL_WORD_OPS[pwi][0], PILL_WORD_OPS[pwi][1]);
|
|
1993
|
+
}
|
|
1994
|
+
if (rawCondition !== pillWordOriginal) {
|
|
1995
|
+
steps.pill_word_op_rewrite = { original: pillWordOriginal, rewritten: rawCondition };
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1912
1999
|
// Shorthand patterns that need rewriting to the real data pill base
|
|
1913
2000
|
// e.g. {{trigger.current.category}} → {{Created or Updated_1.current.category}}
|
|
1914
2001
|
var PILL_SHORTHANDS = ['trigger.current', 'current', 'trigger_record', 'trigger.record'];
|
|
@@ -2032,10 +2119,11 @@ async function addFlowLogicViaGraphQL(
|
|
|
2032
2119
|
steps.label_cache = labelCacheResult.map(function (e: any) { return e.name; });
|
|
2033
2120
|
|
|
2034
2121
|
try {
|
|
2035
|
-
// Match the UI's exact format for condition UPDATE (from
|
|
2036
|
-
//
|
|
2037
|
-
// -
|
|
2038
|
-
// - condition input:
|
|
2122
|
+
// Match the UI's exact format for condition UPDATE (captured from Flow Designer
|
|
2123
|
+
// when editing a programmatically-created flow):
|
|
2124
|
+
// - labelCache.insert: field-level pills with FULL metadata (type, parent_table_name, etc.)
|
|
2125
|
+
// - condition input: name + value + displayValue
|
|
2126
|
+
// - flowLogicDefinition: with condition_name attributes
|
|
2039
2127
|
var updatePatch: any = {
|
|
2040
2128
|
flowId: flowId,
|
|
2041
2129
|
flowLogics: {
|
|
@@ -2044,13 +2132,18 @@ async function addFlowLogicViaGraphQL(
|
|
|
2044
2132
|
type: 'flowlogic',
|
|
2045
2133
|
inputs: [{
|
|
2046
2134
|
name: 'condition',
|
|
2135
|
+
displayValue: { value: '' },
|
|
2047
2136
|
value: { schemaless: false, schemalessValue: '', value: transformedCondition }
|
|
2048
|
-
}]
|
|
2137
|
+
}],
|
|
2138
|
+
flowLogicDefinition: {
|
|
2139
|
+
inputs: [{ name: 'condition_name', attributes: 'use_basic_input=true,' }],
|
|
2140
|
+
variables: 'undefined'
|
|
2141
|
+
}
|
|
2049
2142
|
}]
|
|
2050
2143
|
}
|
|
2051
2144
|
};
|
|
2052
2145
|
if (labelCacheResult.length > 0) {
|
|
2053
|
-
updatePatch.labelCache = {
|
|
2146
|
+
updatePatch.labelCache = { insert: labelCacheResult };
|
|
2054
2147
|
}
|
|
2055
2148
|
// Log the exact GraphQL mutation for debugging
|
|
2056
2149
|
steps.condition_update_mutation = jsToGraphQL(updatePatch);
|