snow-flow-test 10.0.1-test.178 → 10.0.1-test.180
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
|
@@ -141,7 +141,7 @@ async function buildActionInputsForInsert(
|
|
|
141
141
|
}
|
|
142
142
|
var match = actionParams.find(function (p: any) {
|
|
143
143
|
var el = str(p.element);
|
|
144
|
-
return el.endsWith('_' + key) || el === key || str(p.label).toLowerCase() === key.toLowerCase();
|
|
144
|
+
return el.endsWith('_' + key) || el.startsWith(key + '_') || el === key || str(p.label).toLowerCase() === key.toLowerCase();
|
|
145
145
|
});
|
|
146
146
|
if (match) resolvedInputs[str(match.element)] = value;
|
|
147
147
|
else resolvedInputs[key] = value;
|
|
@@ -1001,6 +1001,15 @@ async function addActionViaGraphQL(
|
|
|
1001
1001
|
steps.insert_order = resolvedOrder;
|
|
1002
1002
|
|
|
1003
1003
|
const uuid = generateUUID();
|
|
1004
|
+
|
|
1005
|
+
// ── Data pill transformation for record actions (Update/Create Record) ──
|
|
1006
|
+
// These actions need: record → data pill, table_name → displayValue, field values → packed into values string
|
|
1007
|
+
var recordActionResult = await transformActionInputsForRecordAction(
|
|
1008
|
+
client, flowId, inputResult.inputs, inputResult.resolvedInputs,
|
|
1009
|
+
inputResult.actionParams, uuid
|
|
1010
|
+
);
|
|
1011
|
+
steps.record_action = recordActionResult.steps;
|
|
1012
|
+
|
|
1004
1013
|
const actionResponseFields = 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
1005
1014
|
' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
1006
1015
|
' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
@@ -1019,11 +1028,16 @@ async function addActionViaGraphQL(
|
|
|
1019
1028
|
uiUniqueIdentifier: uuid,
|
|
1020
1029
|
type: 'action',
|
|
1021
1030
|
parentUiId: parentUiId || '',
|
|
1022
|
-
inputs:
|
|
1031
|
+
inputs: recordActionResult.inputs
|
|
1023
1032
|
}]
|
|
1024
1033
|
}
|
|
1025
1034
|
};
|
|
1026
1035
|
|
|
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
|
+
|
|
1027
1041
|
// Add parent flow logic update signal (tells GraphQL the parent was modified)
|
|
1028
1042
|
if (parentUiId) {
|
|
1029
1043
|
flowPatch.flowLogics = { update: [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }] };
|
|
@@ -1040,6 +1054,427 @@ async function addActionViaGraphQL(
|
|
|
1040
1054
|
}
|
|
1041
1055
|
}
|
|
1042
1056
|
|
|
1057
|
+
// ── DATA PILL CONDITION HELPERS ────────────────────────────────────────
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Get trigger info from a flow for constructing data pill references.
|
|
1061
|
+
* Reads the flow version payload to find the trigger name, table, and outputs.
|
|
1062
|
+
*
|
|
1063
|
+
* Returns the data pill base (e.g., "Created or Updated_1") and table (e.g., "incident").
|
|
1064
|
+
* The data pill base is used as: {{dataPillBase.fieldName}} in condition values.
|
|
1065
|
+
*/
|
|
1066
|
+
async function getFlowTriggerInfo(
|
|
1067
|
+
client: any,
|
|
1068
|
+
flowId: string
|
|
1069
|
+
): Promise<{ dataPillBase: string; triggerName: string; table: string; tableLabel: string; tableRef: string; error?: string }> {
|
|
1070
|
+
var triggerName = '';
|
|
1071
|
+
var table = '';
|
|
1072
|
+
var tableLabel = '';
|
|
1073
|
+
|
|
1074
|
+
try {
|
|
1075
|
+
// Read the flow version payload which contains all flow elements
|
|
1076
|
+
var resp = await client.get('/api/now/table/sys_hub_flow_version', {
|
|
1077
|
+
params: {
|
|
1078
|
+
sysparm_query: 'flow=' + flowId + '^ORDERBYDESCsys_created_on',
|
|
1079
|
+
sysparm_fields: 'sys_id,payload',
|
|
1080
|
+
sysparm_limit: 1
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
var payload = resp.data.result?.[0]?.payload;
|
|
1084
|
+
if (payload) {
|
|
1085
|
+
var parsed = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
|
1086
|
+
// The payload contains triggerInstances array with trigger data
|
|
1087
|
+
var triggerInstances = parsed.triggerInstances || parsed.trigger_instances || [];
|
|
1088
|
+
if (!Array.isArray(triggerInstances)) {
|
|
1089
|
+
// Some payloads nest elements differently
|
|
1090
|
+
if (parsed.elements) {
|
|
1091
|
+
triggerInstances = (Array.isArray(parsed.elements) ? parsed.elements : []).filter(
|
|
1092
|
+
(e: any) => e.type === 'trigger' || e.elementType === 'trigger'
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (triggerInstances.length > 0) {
|
|
1097
|
+
var trigger = triggerInstances[0];
|
|
1098
|
+
triggerName = trigger.name || trigger.triggerName || '';
|
|
1099
|
+
// Look for table in trigger inputs
|
|
1100
|
+
if (trigger.inputs && Array.isArray(trigger.inputs)) {
|
|
1101
|
+
for (var ti = 0; ti < trigger.inputs.length; ti++) {
|
|
1102
|
+
if (trigger.inputs[ti].name === 'table') {
|
|
1103
|
+
table = trigger.inputs[ti].value?.value || trigger.inputs[ti].value || '';
|
|
1104
|
+
break;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
} catch (_) {}
|
|
1111
|
+
|
|
1112
|
+
// Look up table label for display in label cache
|
|
1113
|
+
if (table) {
|
|
1114
|
+
try {
|
|
1115
|
+
var labelResp = await client.get('/api/now/table/sys_db_object', {
|
|
1116
|
+
params: {
|
|
1117
|
+
sysparm_query: 'name=' + table,
|
|
1118
|
+
sysparm_fields: 'label',
|
|
1119
|
+
sysparm_display_value: 'true',
|
|
1120
|
+
sysparm_limit: 1
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
tableLabel = str(labelResp.data.result?.[0]?.label) || '';
|
|
1124
|
+
} catch (_) {}
|
|
1125
|
+
if (!tableLabel) {
|
|
1126
|
+
// Fallback: capitalize the table name
|
|
1127
|
+
tableLabel = table.charAt(0).toUpperCase() + table.slice(1).replace(/_/g, ' ');
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (!triggerName) {
|
|
1132
|
+
return { dataPillBase: '', triggerName: '', table: table, tableLabel: tableLabel, tableRef: table, error: 'Could not determine trigger name from flow version payload' };
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
var dataPillBase = triggerName + '_1.current';
|
|
1136
|
+
return { dataPillBase, triggerName, table, tableLabel, tableRef: table };
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Parse a ServiceNow encoded query into individual condition clauses.
|
|
1141
|
+
* Each clause has: prefix (^ or ^OR), field, operator, value.
|
|
1142
|
+
*
|
|
1143
|
+
* Example: "category=inquiry^priority!=1^ORshort_descriptionLIKEtest"
|
|
1144
|
+
* → [
|
|
1145
|
+
* { prefix: '', field: 'category', operator: '=', value: 'inquiry' },
|
|
1146
|
+
* { prefix: '^', field: 'priority', operator: '!=', value: '1' },
|
|
1147
|
+
* { prefix: '^OR', field: 'short_description', operator: 'LIKE', value: 'test' }
|
|
1148
|
+
* ]
|
|
1149
|
+
*/
|
|
1150
|
+
function parseEncodedQuery(query: string): { prefix: string; field: string; operator: string; value: string }[] {
|
|
1151
|
+
if (!query || query === '^EQ') return [];
|
|
1152
|
+
|
|
1153
|
+
// Remove trailing ^EQ
|
|
1154
|
+
var q = query.replace(/\^EQ$/, '');
|
|
1155
|
+
if (!q) return [];
|
|
1156
|
+
|
|
1157
|
+
// Split on ^OR and ^ while keeping the separators
|
|
1158
|
+
var clauses: { prefix: string; raw: string }[] = [];
|
|
1159
|
+
var parts = q.split(/(\^OR|\^NQ|\^)/);
|
|
1160
|
+
var currentPrefix = '';
|
|
1161
|
+
|
|
1162
|
+
for (var i = 0; i < parts.length; i++) {
|
|
1163
|
+
var part = parts[i];
|
|
1164
|
+
if (part === '^' || part === '^OR' || part === '^NQ') {
|
|
1165
|
+
currentPrefix = part;
|
|
1166
|
+
} else if (part.length > 0) {
|
|
1167
|
+
clauses.push({ prefix: currentPrefix, raw: part });
|
|
1168
|
+
currentPrefix = '';
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// Operators sorted by length descending to match longest first
|
|
1173
|
+
var operators = [
|
|
1174
|
+
'VALCHANGES', 'CHANGESFROM', 'CHANGESTO',
|
|
1175
|
+
'ISNOTEMPTY', 'ISEMPTY', 'EMPTYSTRING', 'ANYTHING',
|
|
1176
|
+
'NOT LIKE', 'NOT IN', 'NSAMEAS',
|
|
1177
|
+
'STARTSWITH', 'ENDSWITH', 'BETWEEN', 'INSTANCEOF',
|
|
1178
|
+
'DYNAMIC', 'SAMEAS',
|
|
1179
|
+
'LIKE', 'IN',
|
|
1180
|
+
'!=', '>=', '<=', '>', '<', '='
|
|
1181
|
+
];
|
|
1182
|
+
|
|
1183
|
+
var result: { prefix: string; field: string; operator: string; value: string }[] = [];
|
|
1184
|
+
for (var j = 0; j < clauses.length; j++) {
|
|
1185
|
+
var clause = clauses[j];
|
|
1186
|
+
var raw = clause.raw;
|
|
1187
|
+
var matched = false;
|
|
1188
|
+
|
|
1189
|
+
for (var k = 0; k < operators.length; k++) {
|
|
1190
|
+
var op = operators[k];
|
|
1191
|
+
var opIdx = raw.indexOf(op);
|
|
1192
|
+
if (opIdx > 0) {
|
|
1193
|
+
result.push({
|
|
1194
|
+
prefix: clause.prefix,
|
|
1195
|
+
field: raw.substring(0, opIdx),
|
|
1196
|
+
operator: op,
|
|
1197
|
+
value: raw.substring(opIdx + op.length)
|
|
1198
|
+
});
|
|
1199
|
+
matched = true;
|
|
1200
|
+
break;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
if (!matched) {
|
|
1204
|
+
// Unrecognized format — keep as-is
|
|
1205
|
+
result.push({ prefix: clause.prefix, field: raw, operator: '', value: '' });
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
return result;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* Transform an encoded query condition value to use data pill references.
|
|
1214
|
+
* Replaces field names with {{dataPillBase.fieldName}} syntax.
|
|
1215
|
+
*
|
|
1216
|
+
* Example: "category=inquiry" → "{{Created or Updated_1.current.category}}=inquiry"
|
|
1217
|
+
*/
|
|
1218
|
+
function transformConditionToDataPills(conditionValue: string, dataPillBase: string): string {
|
|
1219
|
+
if (!conditionValue || !dataPillBase) return conditionValue;
|
|
1220
|
+
|
|
1221
|
+
var clauses = parseEncodedQuery(conditionValue);
|
|
1222
|
+
if (clauses.length === 0) return conditionValue;
|
|
1223
|
+
|
|
1224
|
+
var result = '';
|
|
1225
|
+
for (var i = 0; i < clauses.length; i++) {
|
|
1226
|
+
var clause = clauses[i];
|
|
1227
|
+
result += clause.prefix;
|
|
1228
|
+
result += '{{' + dataPillBase + '.' + clause.field + '}}';
|
|
1229
|
+
result += clause.operator;
|
|
1230
|
+
result += clause.value;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
return result;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Build labelCache entries for data pills used in flow logic conditions.
|
|
1238
|
+
* Each unique data pill reference needs a labelCache entry so the Flow Designer UI
|
|
1239
|
+
* can display the data pill label correctly.
|
|
1240
|
+
*
|
|
1241
|
+
* The entry format matches the captured UI mutation format.
|
|
1242
|
+
*/
|
|
1243
|
+
function buildConditionLabelCache(
|
|
1244
|
+
conditionValue: string,
|
|
1245
|
+
dataPillBase: string,
|
|
1246
|
+
triggerName: string,
|
|
1247
|
+
table: string,
|
|
1248
|
+
tableLabel: string,
|
|
1249
|
+
logicUiId: string
|
|
1250
|
+
): any[] {
|
|
1251
|
+
if (!conditionValue || !dataPillBase) return [];
|
|
1252
|
+
|
|
1253
|
+
var clauses = parseEncodedQuery(conditionValue);
|
|
1254
|
+
if (clauses.length === 0) return [];
|
|
1255
|
+
|
|
1256
|
+
// Build a label cache entry for each unique field-level data pill
|
|
1257
|
+
var seen: Record<string, boolean> = {};
|
|
1258
|
+
var entries: any[] = [];
|
|
1259
|
+
|
|
1260
|
+
// Also register the record-level data pill (the base: TriggerName_1.current)
|
|
1261
|
+
// This is needed for the condition builder to understand the context
|
|
1262
|
+
if (!seen[dataPillBase]) {
|
|
1263
|
+
seen[dataPillBase] = true;
|
|
1264
|
+
entries.push({
|
|
1265
|
+
name: dataPillBase,
|
|
1266
|
+
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record',
|
|
1267
|
+
reference: table,
|
|
1268
|
+
reference_display: tableLabel,
|
|
1269
|
+
type: 'reference',
|
|
1270
|
+
base_type: 'reference',
|
|
1271
|
+
attributes: '',
|
|
1272
|
+
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1273
|
+
choices: {}
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
for (var i = 0; i < clauses.length; i++) {
|
|
1278
|
+
var field = clauses[i].field;
|
|
1279
|
+
if (!field) continue;
|
|
1280
|
+
var pillName = dataPillBase + '.' + field;
|
|
1281
|
+
if (seen[pillName]) continue;
|
|
1282
|
+
seen[pillName] = true;
|
|
1283
|
+
|
|
1284
|
+
// Capitalize field name for label
|
|
1285
|
+
var fieldLabel = field.replace(/_/g, ' ').replace(/\b\w/g, function (c: string) { return c.toUpperCase(); });
|
|
1286
|
+
|
|
1287
|
+
entries.push({
|
|
1288
|
+
name: pillName,
|
|
1289
|
+
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record\u27a1' + fieldLabel,
|
|
1290
|
+
reference: '',
|
|
1291
|
+
reference_display: '',
|
|
1292
|
+
type: 'string',
|
|
1293
|
+
base_type: 'string',
|
|
1294
|
+
attributes: '',
|
|
1295
|
+
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1296
|
+
choices: {}
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
return entries;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// ── DATA PILL SUPPORT FOR RECORD ACTIONS (Update/Create Record) ──────
|
|
1304
|
+
|
|
1305
|
+
/** Shorthands that users can pass for `record` to mean "the trigger's current record". */
|
|
1306
|
+
const RECORD_PILL_SHORTHANDS = ['current', 'trigger.current', 'trigger_record', 'trigger record'];
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Post-process action inputs for record-modifying actions (Update Record, Create Record).
|
|
1310
|
+
*
|
|
1311
|
+
* These actions have 3 key inputs:
|
|
1312
|
+
* - `record`: reference to the record → needs data pill format {{TriggerName_1.current}}
|
|
1313
|
+
* - `table_name`: target table → needs displayValue (e.g. "Incident")
|
|
1314
|
+
* - `values`: packed field=value pairs → e.g. "priority=2^state=3"
|
|
1315
|
+
*
|
|
1316
|
+
* User-provided field-value pairs that don't match defined action parameters are
|
|
1317
|
+
* automatically packed into the `values` string.
|
|
1318
|
+
*
|
|
1319
|
+
* Returns the transformed inputs and labelCache entries.
|
|
1320
|
+
*/
|
|
1321
|
+
async function transformActionInputsForRecordAction(
|
|
1322
|
+
client: any,
|
|
1323
|
+
flowId: string,
|
|
1324
|
+
actionInputs: any[],
|
|
1325
|
+
resolvedInputs: Record<string, string>,
|
|
1326
|
+
actionParams: any[],
|
|
1327
|
+
uuid: string
|
|
1328
|
+
): Promise<{ inputs: any[]; labelCacheEntries: any[]; steps: any }> {
|
|
1329
|
+
var steps: any = {};
|
|
1330
|
+
|
|
1331
|
+
// Detect if this is a record action: must have both `record` and `table_name` parameters
|
|
1332
|
+
var definedParamNames = actionParams.map(function (p: any) { return str(p.element); });
|
|
1333
|
+
var hasRecord = definedParamNames.includes('record');
|
|
1334
|
+
var hasTableName = definedParamNames.includes('table_name');
|
|
1335
|
+
var hasValues = definedParamNames.includes('values');
|
|
1336
|
+
|
|
1337
|
+
if (!hasRecord || !hasTableName) {
|
|
1338
|
+
steps.record_action = false;
|
|
1339
|
+
return { inputs: actionInputs, labelCacheEntries: [], steps };
|
|
1340
|
+
}
|
|
1341
|
+
steps.record_action = true;
|
|
1342
|
+
|
|
1343
|
+
// Get trigger info for data pill construction
|
|
1344
|
+
var triggerInfo = await getFlowTriggerInfo(client, flowId);
|
|
1345
|
+
steps.trigger_info = {
|
|
1346
|
+
dataPillBase: triggerInfo.dataPillBase,
|
|
1347
|
+
triggerName: triggerInfo.triggerName,
|
|
1348
|
+
table: triggerInfo.table,
|
|
1349
|
+
tableLabel: triggerInfo.tableLabel,
|
|
1350
|
+
error: triggerInfo.error
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
var dataPillBase = triggerInfo.dataPillBase; // e.g. "Created or Updated_1.current"
|
|
1354
|
+
var labelCacheEntries: any[] = [];
|
|
1355
|
+
var usedInstances: { uiUniqueIdentifier: string; inputName: string }[] = [];
|
|
1356
|
+
|
|
1357
|
+
// ── 1. Transform `record` input to data pill ──────────────────────
|
|
1358
|
+
var recordInput = actionInputs.find(function (inp: any) { return inp.name === 'record'; });
|
|
1359
|
+
if (recordInput && dataPillBase) {
|
|
1360
|
+
var recordVal = recordInput.value?.value || '';
|
|
1361
|
+
var isShorthand = RECORD_PILL_SHORTHANDS.includes(recordVal.toLowerCase());
|
|
1362
|
+
var isAlreadyPill = recordVal.startsWith('{{');
|
|
1363
|
+
|
|
1364
|
+
if (isShorthand || !recordVal) {
|
|
1365
|
+
// Auto-fill with trigger's current record data pill
|
|
1366
|
+
recordInput.value = { schemaless: false, schemalessValue: '', value: '{{' + dataPillBase + '}}' };
|
|
1367
|
+
usedInstances.push({ uiUniqueIdentifier: uuid, inputName: 'record' });
|
|
1368
|
+
steps.record_transform = { original: recordVal, pill: '{{' + dataPillBase + '}}' };
|
|
1369
|
+
} else if (isAlreadyPill) {
|
|
1370
|
+
usedInstances.push({ uiUniqueIdentifier: uuid, inputName: 'record' });
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// ── 2. Transform `table_name` input with displayValue ─────────────
|
|
1375
|
+
var tableNameInput = actionInputs.find(function (inp: any) { return inp.name === 'table_name'; });
|
|
1376
|
+
if (tableNameInput) {
|
|
1377
|
+
var tableVal = tableNameInput.value?.value || '';
|
|
1378
|
+
// Also accept `table` as user key (maps to table_name)
|
|
1379
|
+
if (!tableVal && resolvedInputs['table']) {
|
|
1380
|
+
tableVal = resolvedInputs['table'];
|
|
1381
|
+
}
|
|
1382
|
+
// If still empty, use trigger's table
|
|
1383
|
+
if (!tableVal && triggerInfo.table) {
|
|
1384
|
+
tableVal = triggerInfo.table;
|
|
1385
|
+
}
|
|
1386
|
+
if (tableVal) {
|
|
1387
|
+
// Look up display name for the table
|
|
1388
|
+
var tableDisplayName = triggerInfo.tableLabel || '';
|
|
1389
|
+
if (tableVal !== triggerInfo.table || !tableDisplayName) {
|
|
1390
|
+
// Different table than trigger — look up its label
|
|
1391
|
+
try {
|
|
1392
|
+
var tblResp = await client.get('/api/now/table/sys_db_object', {
|
|
1393
|
+
params: { sysparm_query: 'name=' + tableVal, sysparm_fields: 'label', sysparm_display_value: 'true', sysparm_limit: 1 }
|
|
1394
|
+
});
|
|
1395
|
+
tableDisplayName = str(tblResp.data.result?.[0]?.label) || tableVal.charAt(0).toUpperCase() + tableVal.slice(1).replace(/_/g, ' ');
|
|
1396
|
+
} catch (_) {
|
|
1397
|
+
tableDisplayName = tableVal.charAt(0).toUpperCase() + tableVal.slice(1).replace(/_/g, ' ');
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
tableNameInput.value = { schemaless: false, schemalessValue: '', value: tableVal };
|
|
1401
|
+
tableNameInput.displayValue = { schemaless: false, schemalessValue: '', value: tableDisplayName };
|
|
1402
|
+
steps.table_name_transform = { value: tableVal, displayValue: tableDisplayName };
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// ── 3. Pack non-parameter field values into `values` string ───────
|
|
1407
|
+
// Any user-provided key that is NOT a defined action parameter goes into the values string
|
|
1408
|
+
var valuesInput = actionInputs.find(function (inp: any) { return inp.name === 'values'; });
|
|
1409
|
+
if (valuesInput) {
|
|
1410
|
+
var fieldPairs: string[] = [];
|
|
1411
|
+
var existingValues = valuesInput.value?.value || '';
|
|
1412
|
+
|
|
1413
|
+
// If user already passed a pre-built values string, use it
|
|
1414
|
+
if (existingValues && existingValues.includes('=')) {
|
|
1415
|
+
fieldPairs.push(existingValues);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// Find user-provided keys that are not defined action parameters
|
|
1419
|
+
for (var key of Object.keys(resolvedInputs)) {
|
|
1420
|
+
if (definedParamNames.includes(key)) continue;
|
|
1421
|
+
// Also skip table (alias for table_name) and record
|
|
1422
|
+
if (key === 'table' || key === 'record') continue;
|
|
1423
|
+
|
|
1424
|
+
var val = resolvedInputs[key];
|
|
1425
|
+
|
|
1426
|
+
// Check if value should be a data pill reference
|
|
1427
|
+
if (val && dataPillBase) {
|
|
1428
|
+
var valLower = val.toLowerCase();
|
|
1429
|
+
if (RECORD_PILL_SHORTHANDS.includes(valLower)) {
|
|
1430
|
+
// Shorthand → record-level data pill
|
|
1431
|
+
val = '{{' + dataPillBase + '}}';
|
|
1432
|
+
usedInstances.push({ uiUniqueIdentifier: uuid, inputName: key });
|
|
1433
|
+
} else if (valLower.startsWith('trigger.current.') || valLower.startsWith('current.')) {
|
|
1434
|
+
// Field-level data pill: "trigger.current.assigned_to" → {{dataPillBase.assigned_to}}
|
|
1435
|
+
var fieldName = valLower.startsWith('trigger.current.') ? val.substring(16) : val.substring(8);
|
|
1436
|
+
val = '{{' + dataPillBase + '.' + fieldName + '}}';
|
|
1437
|
+
usedInstances.push({ uiUniqueIdentifier: uuid, inputName: key });
|
|
1438
|
+
} else if (val.startsWith('{{')) {
|
|
1439
|
+
// Already a data pill
|
|
1440
|
+
usedInstances.push({ uiUniqueIdentifier: uuid, inputName: key });
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
fieldPairs.push(key + '=' + val);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
if (fieldPairs.length > 0) {
|
|
1448
|
+
var packedValues = fieldPairs.join('^');
|
|
1449
|
+
valuesInput.value = { schemaless: false, schemalessValue: '', value: packedValues };
|
|
1450
|
+
steps.values_transform = { packed: packedValues, fieldCount: fieldPairs.length };
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// ── 4. Build labelCache entries for data pills ────────────────────
|
|
1455
|
+
if (dataPillBase && usedInstances.length > 0) {
|
|
1456
|
+
var tableRef = triggerInfo.tableRef || triggerInfo.table || '';
|
|
1457
|
+
var tableLabel = triggerInfo.tableLabel || '';
|
|
1458
|
+
|
|
1459
|
+
// Record-level data pill entry
|
|
1460
|
+
labelCacheEntries.push({
|
|
1461
|
+
name: dataPillBase,
|
|
1462
|
+
label: 'Trigger - Record ' + triggerInfo.triggerName + '\u27a1' + tableLabel + ' Record',
|
|
1463
|
+
reference: tableRef,
|
|
1464
|
+
reference_display: tableLabel,
|
|
1465
|
+
type: 'reference',
|
|
1466
|
+
base_type: 'reference',
|
|
1467
|
+
attributes: '',
|
|
1468
|
+
usedInstances: usedInstances,
|
|
1469
|
+
choices: {}
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1472
|
+
steps.label_cache = { count: labelCacheEntries.length, pills: [dataPillBase], usedInstances: usedInstances.length };
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
return { inputs: actionInputs, labelCacheEntries, steps };
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1043
1478
|
// ── FLOW LOGIC (If/Else, For Each, etc.) ─────────────────────────────
|
|
1044
1479
|
|
|
1045
1480
|
async function addFlowLogicViaGraphQL(
|
|
@@ -1112,11 +1547,37 @@ async function addFlowLogicViaGraphQL(
|
|
|
1112
1547
|
steps.resolved_inputs = inputResult.resolvedInputs;
|
|
1113
1548
|
steps.input_query_stats = { defParamsFound: inputResult.defParamsCount, inputsBuilt: inputResult.inputs.length, error: inputResult.inputQueryError };
|
|
1114
1549
|
|
|
1550
|
+
// ── Data pill transformation for condition inputs ───────────────────
|
|
1551
|
+
// Flow Designer conditions require data pill references ({{TriggerName_1.current.field}})
|
|
1552
|
+
// instead of bare field names. Transform encoded query conditions and build labelCache.
|
|
1553
|
+
const uuid = generateUUID();
|
|
1554
|
+
var labelCacheEntries: any[] = [];
|
|
1555
|
+
|
|
1556
|
+
var conditionInput = inputResult.inputs.find(function (inp: any) { return inp.name === 'condition'; });
|
|
1557
|
+
var rawCondition = conditionInput?.value?.value || '';
|
|
1558
|
+
if (rawCondition && rawCondition !== '^EQ') {
|
|
1559
|
+
var triggerInfo = await getFlowTriggerInfo(client, flowId);
|
|
1560
|
+
steps.trigger_info = { dataPillBase: triggerInfo.dataPillBase, triggerName: triggerInfo.triggerName, table: triggerInfo.table, tableLabel: triggerInfo.tableLabel, error: triggerInfo.error };
|
|
1561
|
+
|
|
1562
|
+
if (triggerInfo.dataPillBase) {
|
|
1563
|
+
// Transform condition value: "category=inquiry" → "{{Created or Updated_1.current.category}}=inquiry"
|
|
1564
|
+
var transformedCondition = transformConditionToDataPills(rawCondition, triggerInfo.dataPillBase);
|
|
1565
|
+
conditionInput.value = { schemaless: false, schemalessValue: '', value: transformedCondition };
|
|
1566
|
+
steps.condition_transform = { original: rawCondition, transformed: transformedCondition };
|
|
1567
|
+
|
|
1568
|
+
// Build labelCache entries for each data pill used in the condition
|
|
1569
|
+
labelCacheEntries = buildConditionLabelCache(
|
|
1570
|
+
rawCondition, triggerInfo.dataPillBase, triggerInfo.triggerName,
|
|
1571
|
+
triggerInfo.tableRef, triggerInfo.tableLabel, uuid
|
|
1572
|
+
);
|
|
1573
|
+
steps.label_cache = { count: labelCacheEntries.length, pills: labelCacheEntries.map(function (e: any) { return e.name; }) };
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1115
1577
|
// Calculate insertion order
|
|
1116
1578
|
const resolvedOrder = await calculateInsertOrder(client, flowId, parentUiId, order);
|
|
1117
1579
|
steps.insert_order = resolvedOrder;
|
|
1118
1580
|
|
|
1119
|
-
const uuid = generateUUID();
|
|
1120
1581
|
const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
1121
1582
|
' actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
1122
1583
|
' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
@@ -1147,6 +1608,11 @@ async function addFlowLogicViaGraphQL(
|
|
|
1147
1608
|
}
|
|
1148
1609
|
};
|
|
1149
1610
|
|
|
1611
|
+
// Add labelCache entries for data pill references in conditions
|
|
1612
|
+
if (labelCacheEntries.length > 0) {
|
|
1613
|
+
flowPatch.labelCache = { insert: labelCacheEntries };
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1150
1616
|
// Add parent flow logic update signal (tells GraphQL the parent was modified)
|
|
1151
1617
|
if (parentUiId) {
|
|
1152
1618
|
flowPatch.flowLogics.update = [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }];
|