snow-flow 10.0.1-dev.455 → 10.0.1-dev.456
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
|
@@ -194,6 +194,119 @@ async function buildActionInputsForInsert(
|
|
|
194
194
|
return { inputs, resolvedInputs, actionParams };
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Build full flow logic input objects AND flowLogicDefinition matching the Flow Designer UI format.
|
|
199
|
+
* The UI sends inputs WITH parameter definitions and the full flowLogicDefinition in the INSERT mutation.
|
|
200
|
+
*
|
|
201
|
+
* Flow logic definitions (IF, ELSE, FOR_EACH, etc.) store their input parameters in the same
|
|
202
|
+
* sys_hub_action_input table as actions, using the definition's sys_id as the 'model' reference.
|
|
203
|
+
*/
|
|
204
|
+
async function buildFlowLogicInputsForInsert(
|
|
205
|
+
client: any,
|
|
206
|
+
defId: string,
|
|
207
|
+
defRecord: { name?: string; type?: string; description?: string; order?: string; attributes?: string; compilation_class?: string; quiescence?: string; visible?: string; category?: string; connected_to?: string },
|
|
208
|
+
userValues?: Record<string, string>
|
|
209
|
+
): Promise<{ inputs: any[]; flowLogicDefinition: any; resolvedInputs: Record<string, string> }> {
|
|
210
|
+
// Query sys_hub_action_input for this definition's inputs (same table as actions — uses generic 'model' reference)
|
|
211
|
+
var defParams: any[] = [];
|
|
212
|
+
try {
|
|
213
|
+
var resp = await client.get('/api/now/table/sys_hub_action_input', {
|
|
214
|
+
params: {
|
|
215
|
+
sysparm_query: 'model=' + defId,
|
|
216
|
+
sysparm_fields: 'sys_id,element,label,internal_type,mandatory,default_value,order,max_length,hint,read_only,extended,data_structure,reference,reference_display,ref_qual,choice_option,table_name,column_name,use_dependent,dependent_on,show_ref_finder,local,attributes,sys_class_name',
|
|
217
|
+
sysparm_display_value: 'false',
|
|
218
|
+
sysparm_limit: 50
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
defParams = resp.data.result || [];
|
|
222
|
+
} catch (_) {}
|
|
223
|
+
|
|
224
|
+
// Fuzzy-match user-provided values to actual field names
|
|
225
|
+
var resolvedInputs: Record<string, string> = {};
|
|
226
|
+
if (userValues) {
|
|
227
|
+
var paramElements = defParams.map(function (p: any) { return str(p.element); });
|
|
228
|
+
for (var [key, value] of Object.entries(userValues)) {
|
|
229
|
+
if (paramElements.includes(key)) {
|
|
230
|
+
resolvedInputs[key] = value;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
var match = defParams.find(function (p: any) {
|
|
234
|
+
var el = str(p.element);
|
|
235
|
+
return el.endsWith('_' + key) || el === key || str(p.label).toLowerCase() === key.toLowerCase();
|
|
236
|
+
});
|
|
237
|
+
if (match) resolvedInputs[str(match.element)] = value;
|
|
238
|
+
else resolvedInputs[key] = value;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Build parameter definition objects (shared between inputs array and flowLogicDefinition.inputs)
|
|
243
|
+
var inputDefs = defParams.map(function (rec: any) {
|
|
244
|
+
var paramType = str(rec.internal_type) || 'string';
|
|
245
|
+
var element = str(rec.element);
|
|
246
|
+
return {
|
|
247
|
+
id: str(rec.sys_id),
|
|
248
|
+
label: str(rec.label) || element,
|
|
249
|
+
name: element,
|
|
250
|
+
type: paramType,
|
|
251
|
+
type_label: TYPE_LABELS[paramType] || paramType.charAt(0).toUpperCase() + paramType.slice(1),
|
|
252
|
+
hint: str(rec.hint),
|
|
253
|
+
order: parseInt(str(rec.order) || '0', 10),
|
|
254
|
+
extended: str(rec.extended) === 'true',
|
|
255
|
+
mandatory: str(rec.mandatory) === 'true',
|
|
256
|
+
readonly: str(rec.read_only) === 'true',
|
|
257
|
+
maxsize: parseInt(str(rec.max_length) || '8000', 10),
|
|
258
|
+
data_structure: str(rec.data_structure),
|
|
259
|
+
reference: str(rec.reference),
|
|
260
|
+
reference_display: str(rec.reference_display),
|
|
261
|
+
ref_qual: str(rec.ref_qual),
|
|
262
|
+
choiceOption: str(rec.choice_option),
|
|
263
|
+
table: str(rec.table_name),
|
|
264
|
+
columnName: str(rec.column_name),
|
|
265
|
+
defaultValue: str(rec.default_value),
|
|
266
|
+
use_dependent: str(rec.use_dependent) === 'true',
|
|
267
|
+
dependent_on: str(rec.dependent_on),
|
|
268
|
+
show_ref_finder: str(rec.show_ref_finder) === 'true',
|
|
269
|
+
local: str(rec.local) === 'true',
|
|
270
|
+
attributes: str(rec.attributes),
|
|
271
|
+
sys_class_name: str(rec.sys_class_name),
|
|
272
|
+
children: []
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Build full input objects with parameter definitions and user values
|
|
277
|
+
var inputs = inputDefs.map(function (paramDef: any) {
|
|
278
|
+
var userVal = resolvedInputs[paramDef.name] || '';
|
|
279
|
+
return {
|
|
280
|
+
id: paramDef.id,
|
|
281
|
+
name: paramDef.name,
|
|
282
|
+
children: [],
|
|
283
|
+
displayValue: { value: '' },
|
|
284
|
+
value: { schemaless: false, schemalessValue: '', value: userVal },
|
|
285
|
+
parameter: paramDef
|
|
286
|
+
};
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Build flowLogicDefinition object (matching UI format)
|
|
290
|
+
var flowLogicDefinition: any = {
|
|
291
|
+
id: defId,
|
|
292
|
+
name: defRecord.name || '',
|
|
293
|
+
description: str(defRecord.description),
|
|
294
|
+
connectedTo: str(defRecord.connected_to),
|
|
295
|
+
quiescence: str(defRecord.quiescence) || 'never',
|
|
296
|
+
compilationClass: str(defRecord.compilation_class),
|
|
297
|
+
order: parseInt(str(defRecord.order) || '1', 10),
|
|
298
|
+
type: str(defRecord.type) || '',
|
|
299
|
+
visible: str(defRecord.visible) !== 'false',
|
|
300
|
+
attributes: str(defRecord.attributes),
|
|
301
|
+
userCanRead: true,
|
|
302
|
+
category: str(defRecord.category),
|
|
303
|
+
inputs: inputDefs,
|
|
304
|
+
variables: '[]'
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return { inputs, flowLogicDefinition, resolvedInputs };
|
|
308
|
+
}
|
|
309
|
+
|
|
197
310
|
// Note: reordering of existing elements is NOT possible via Table API because
|
|
198
311
|
// Flow Designer elements only exist in the version payload (managed by GraphQL).
|
|
199
312
|
// The caller must provide the correct global order. When inserting between existing
|
|
@@ -455,26 +568,31 @@ async function addFlowLogicViaGraphQL(
|
|
|
455
568
|
logicType: string,
|
|
456
569
|
inputs?: Record<string, string>,
|
|
457
570
|
order?: number,
|
|
458
|
-
parentUiId?: string
|
|
459
|
-
|
|
571
|
+
parentUiId?: string,
|
|
572
|
+
connectedTo?: string
|
|
573
|
+
): Promise<{ success: boolean; logicId?: string; uiUniqueIdentifier?: string; steps?: any; error?: string }> {
|
|
460
574
|
const steps: any = {};
|
|
461
575
|
|
|
462
576
|
// Dynamically look up flow logic definition in sys_hub_flow_logic_definition
|
|
577
|
+
// Fetch extra fields needed for the flowLogicDefinition object in the mutation
|
|
578
|
+
const defFields = 'sys_id,type,name,description,order,attributes,compilation_class,quiescence,visible,category,connected_to';
|
|
463
579
|
let defId: string | null = null;
|
|
464
580
|
let defName = '';
|
|
465
581
|
let defType = logicType;
|
|
466
|
-
|
|
582
|
+
let defRecord: any = {};
|
|
583
|
+
// Try exact match on type (IF, ELSE, FOR_EACH, DO_UNTIL, SWITCH), then name
|
|
467
584
|
for (const field of ['type', 'name']) {
|
|
468
585
|
if (defId) break;
|
|
469
586
|
try {
|
|
470
587
|
const resp = await client.get('/api/now/table/sys_hub_flow_logic_definition', {
|
|
471
|
-
params: { sysparm_query: field + '=' + logicType, sysparm_fields:
|
|
588
|
+
params: { sysparm_query: field + '=' + logicType, sysparm_fields: defFields, sysparm_limit: 1 }
|
|
472
589
|
});
|
|
473
590
|
const found = resp.data.result?.[0];
|
|
474
591
|
if (found?.sys_id) {
|
|
475
592
|
defId = found.sys_id;
|
|
476
593
|
defName = found.name || logicType;
|
|
477
594
|
defType = found.type || logicType;
|
|
595
|
+
defRecord = found;
|
|
478
596
|
steps.def_lookup = { id: found.sys_id, type: found.type, name: found.name, matched: field + '=' + logicType };
|
|
479
597
|
}
|
|
480
598
|
} catch (_) {}
|
|
@@ -485,7 +603,7 @@ async function addFlowLogicViaGraphQL(
|
|
|
485
603
|
const resp = await client.get('/api/now/table/sys_hub_flow_logic_definition', {
|
|
486
604
|
params: {
|
|
487
605
|
sysparm_query: 'typeLIKE' + logicType + '^ORnameLIKE' + logicType,
|
|
488
|
-
sysparm_fields:
|
|
606
|
+
sysparm_fields: defFields, sysparm_limit: 5
|
|
489
607
|
}
|
|
490
608
|
});
|
|
491
609
|
const results = resp.data.result || [];
|
|
@@ -494,12 +612,25 @@ async function addFlowLogicViaGraphQL(
|
|
|
494
612
|
defId = results[0].sys_id;
|
|
495
613
|
defName = results[0].name || logicType;
|
|
496
614
|
defType = results[0].type || logicType;
|
|
615
|
+
defRecord = results[0];
|
|
497
616
|
steps.def_lookup = { id: results[0].sys_id, type: results[0].type, name: results[0].name, matched: 'LIKE ' + logicType };
|
|
498
617
|
}
|
|
499
618
|
} catch (_) {}
|
|
500
619
|
}
|
|
501
620
|
if (!defId) return { success: false, error: 'Flow logic definition not found for: ' + logicType, steps };
|
|
502
621
|
|
|
622
|
+
// ELSE/ELSEIF blocks MUST be connected to an If block via connectedTo (the If block's uiUniqueIdentifier)
|
|
623
|
+
// Unlike other flow logic, Else is NOT a child (parent="") — it uses connectedTo to link to the If block.
|
|
624
|
+
const upperType = defType.toUpperCase();
|
|
625
|
+
if ((upperType === 'ELSE' || upperType === 'ELSEIF') && !connectedTo) {
|
|
626
|
+
return { success: false, error: upperType + ' blocks require connected_to set to the If block\'s uiUniqueIdentifier (returned from the add_flow_logic response for the If block). Else is NOT a child of If — it uses connectedTo to link to it.', steps };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Build full input objects with parameter definitions (matching UI format)
|
|
630
|
+
const inputResult = await buildFlowLogicInputsForInsert(client, defId, defRecord, inputs);
|
|
631
|
+
steps.available_inputs = inputResult.inputs.map((i: any) => ({ name: i.name, label: i.parameter?.label }));
|
|
632
|
+
steps.resolved_inputs = inputResult.resolvedInputs;
|
|
633
|
+
|
|
503
634
|
// Calculate insertion order
|
|
504
635
|
const resolvedOrder = await calculateInsertOrder(client, flowId, parentUiId, order);
|
|
505
636
|
steps.insert_order = resolvedOrder;
|
|
@@ -509,36 +640,33 @@ async function addFlowLogicViaGraphQL(
|
|
|
509
640
|
' actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
510
641
|
' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
511
642
|
|
|
512
|
-
// Build
|
|
513
|
-
var
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
643
|
+
// Build the insert object — include connectedTo when linking Else to an If block
|
|
644
|
+
var insertObj: any = {
|
|
645
|
+
order: String(resolvedOrder),
|
|
646
|
+
uiUniqueIdentifier: uuid,
|
|
647
|
+
parent: parentUiId || '',
|
|
648
|
+
metadata: '{"predicates":[]}',
|
|
649
|
+
flowSysId: flowId,
|
|
650
|
+
generationSource: '',
|
|
651
|
+
definitionId: defId,
|
|
652
|
+
type: 'flowlogic',
|
|
653
|
+
parentUiId: parentUiId || '',
|
|
654
|
+
inputs: inputResult.inputs,
|
|
655
|
+
outputsToAssign: [],
|
|
656
|
+
flowLogicDefinition: inputResult.flowLogicDefinition
|
|
657
|
+
};
|
|
658
|
+
if (connectedTo) {
|
|
659
|
+
insertObj.connectedTo = connectedTo;
|
|
521
660
|
}
|
|
522
661
|
|
|
523
662
|
var flowPatch: any = {
|
|
524
663
|
flowId: flowId,
|
|
525
664
|
flowLogics: {
|
|
526
|
-
insert: [
|
|
527
|
-
order: String(resolvedOrder),
|
|
528
|
-
uiUniqueIdentifier: uuid,
|
|
529
|
-
parent: parentUiId || '',
|
|
530
|
-
metadata: '{"predicates":[]}',
|
|
531
|
-
flowSysId: flowId,
|
|
532
|
-
generationSource: '',
|
|
533
|
-
definitionId: defId,
|
|
534
|
-
type: 'flowlogic',
|
|
535
|
-
parentUiId: parentUiId || '',
|
|
536
|
-
inputs: logicInputObjects
|
|
537
|
-
}]
|
|
665
|
+
insert: [insertObj]
|
|
538
666
|
}
|
|
539
667
|
};
|
|
540
668
|
|
|
541
|
-
// Add parent flow logic update signal
|
|
669
|
+
// Add parent flow logic update signal (tells GraphQL the parent was modified)
|
|
542
670
|
if (parentUiId) {
|
|
543
671
|
flowPatch.flowLogics.update = [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }];
|
|
544
672
|
}
|
|
@@ -546,10 +674,11 @@ async function addFlowLogicViaGraphQL(
|
|
|
546
674
|
try {
|
|
547
675
|
const result = await executeFlowPatchMutation(client, flowPatch, logicResponseFields);
|
|
548
676
|
const logicId = result?.flowLogics?.inserts?.[0]?.sysId;
|
|
549
|
-
|
|
677
|
+
const returnedUuid = result?.flowLogics?.inserts?.[0]?.uiUniqueIdentifier || uuid;
|
|
678
|
+
steps.insert = { success: !!logicId, logicId, uuid: returnedUuid };
|
|
550
679
|
if (!logicId) return { success: false, steps, error: 'GraphQL flow logic INSERT returned no ID' };
|
|
551
680
|
|
|
552
|
-
return { success: true, logicId, steps };
|
|
681
|
+
return { success: true, logicId, uiUniqueIdentifier: returnedUuid, steps };
|
|
553
682
|
} catch (e: any) {
|
|
554
683
|
steps.insert = { success: false, error: e.message };
|
|
555
684
|
return { success: false, steps, error: 'GraphQL flow logic INSERT failed: ' + e.message };
|
|
@@ -925,7 +1054,7 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
925
1054
|
},
|
|
926
1055
|
logic_type: {
|
|
927
1056
|
type: 'string',
|
|
928
|
-
description: 'Flow logic type for add_flow_logic. Looked up dynamically in sys_hub_flow_logic_definition. Common values: IF, FOR_EACH, DO_UNTIL, SWITCH.
|
|
1057
|
+
description: 'Flow logic type for add_flow_logic. Looked up dynamically in sys_hub_flow_logic_definition. Common values: IF, ELSE, FOR_EACH, DO_UNTIL, SWITCH. IMPORTANT: ELSE blocks require connected_to set to the If block\'s uiUniqueIdentifier. IF does NOT require an Else block — only add Else if explicitly requested.'
|
|
929
1058
|
},
|
|
930
1059
|
logic_inputs: {
|
|
931
1060
|
type: 'object',
|
|
@@ -933,7 +1062,11 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
933
1062
|
},
|
|
934
1063
|
parent_ui_id: {
|
|
935
1064
|
type: 'string',
|
|
936
|
-
description: 'Parent UI unique identifier for nesting elements inside flow logic blocks
|
|
1065
|
+
description: 'Parent UI unique identifier for nesting elements inside flow logic blocks. REQUIRED for placing actions/subflows inside an If/Else block — set to the If/Else block\'s uiUniqueIdentifier from its add_flow_logic response.'
|
|
1066
|
+
},
|
|
1067
|
+
connected_to: {
|
|
1068
|
+
type: 'string',
|
|
1069
|
+
description: 'REQUIRED for ELSE blocks: the uiUniqueIdentifier of the If block this Else is connected to. Unlike parent_ui_id (which nests elements inside a block), connected_to links sibling blocks like Else to their If. Get this value from the add_flow_logic response for the If block.'
|
|
937
1070
|
},
|
|
938
1071
|
subflow_id: {
|
|
939
1072
|
type: 'string',
|
|
@@ -1900,8 +2033,9 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1900
2033
|
var addLogicInputs = args.logic_inputs || {};
|
|
1901
2034
|
var addLogicOrder = args.order;
|
|
1902
2035
|
var addLogicParentUiId = args.parent_ui_id || '';
|
|
2036
|
+
var addLogicConnectedTo = args.connected_to || '';
|
|
1903
2037
|
|
|
1904
|
-
var addLogicResult = await addFlowLogicViaGraphQL(client, addLogicFlowId, addLogicType, addLogicInputs, addLogicOrder, addLogicParentUiId);
|
|
2038
|
+
var addLogicResult = await addFlowLogicViaGraphQL(client, addLogicFlowId, addLogicType, addLogicInputs, addLogicOrder, addLogicParentUiId, addLogicConnectedTo);
|
|
1905
2039
|
|
|
1906
2040
|
var addLogicSummary = summary();
|
|
1907
2041
|
if (addLogicResult.success) {
|
|
@@ -1909,7 +2043,8 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1909
2043
|
.success('Flow logic added via GraphQL')
|
|
1910
2044
|
.field('Flow', addLogicFlowId)
|
|
1911
2045
|
.field('Type', addLogicType)
|
|
1912
|
-
.field('Logic ID', addLogicResult.logicId || 'unknown')
|
|
2046
|
+
.field('Logic ID', addLogicResult.logicId || 'unknown')
|
|
2047
|
+
.field('uiUniqueIdentifier', addLogicResult.uiUniqueIdentifier || 'unknown');
|
|
1913
2048
|
} else {
|
|
1914
2049
|
addLogicSummary.error('Failed to add flow logic: ' + (addLogicResult.error || 'unknown'));
|
|
1915
2050
|
}
|