snow-flow 10.0.1-dev.460 → 10.0.1-dev.462
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
|
@@ -206,20 +206,39 @@ async function buildFlowLogicInputsForInsert(
|
|
|
206
206
|
defId: string,
|
|
207
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
208
|
userValues?: Record<string, string>
|
|
209
|
-
): Promise<{ inputs: any[]; flowLogicDefinition: any; resolvedInputs: Record<string, string
|
|
209
|
+
): Promise<{ inputs: any[]; flowLogicDefinition: any; resolvedInputs: Record<string, string>; inputQueryError?: string; defParamsCount: number }> {
|
|
210
210
|
// Query sys_hub_flow_logic_input for this definition's inputs (separate table from sys_hub_action_input)
|
|
211
|
+
// Field names verified from actual sys_hub_flow_logic_input XML schema
|
|
211
212
|
var defParams: any[] = [];
|
|
213
|
+
var inputQueryError = '';
|
|
212
214
|
try {
|
|
213
215
|
var resp = await client.get('/api/now/table/sys_hub_flow_logic_input', {
|
|
214
216
|
params: {
|
|
215
217
|
sysparm_query: 'model=' + defId,
|
|
216
|
-
sysparm_fields: 'sys_id,element,label,internal_type,mandatory,default_value,order,max_length,hint,read_only,
|
|
218
|
+
sysparm_fields: 'sys_id,element,label,internal_type,mandatory,default_value,order,max_length,hint,read_only,attributes,sys_class_name,reference,choice,dependent,dependent_on_field,use_dependent_field,column_label',
|
|
217
219
|
sysparm_display_value: 'false',
|
|
218
220
|
sysparm_limit: 50
|
|
219
221
|
}
|
|
220
222
|
});
|
|
221
223
|
defParams = resp.data.result || [];
|
|
222
|
-
} catch (
|
|
224
|
+
} catch (e: any) {
|
|
225
|
+
inputQueryError = e.message || 'unknown error';
|
|
226
|
+
// Fallback: try with minimal fields
|
|
227
|
+
try {
|
|
228
|
+
var resp2 = await client.get('/api/now/table/sys_hub_flow_logic_input', {
|
|
229
|
+
params: {
|
|
230
|
+
sysparm_query: 'model=' + defId,
|
|
231
|
+
sysparm_fields: 'sys_id,element,label,internal_type,mandatory,order,max_length,attributes',
|
|
232
|
+
sysparm_display_value: 'false',
|
|
233
|
+
sysparm_limit: 50
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
defParams = resp2.data.result || [];
|
|
237
|
+
inputQueryError = '';
|
|
238
|
+
} catch (e2: any) {
|
|
239
|
+
inputQueryError += '; fallback also failed: ' + (e2.message || '');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
223
242
|
|
|
224
243
|
// Fuzzy-match user-provided values to actual field names
|
|
225
244
|
var resolvedInputs: Record<string, string> = {};
|
|
@@ -304,7 +323,7 @@ async function buildFlowLogicInputsForInsert(
|
|
|
304
323
|
variables: '[]'
|
|
305
324
|
};
|
|
306
325
|
|
|
307
|
-
return { inputs, flowLogicDefinition, resolvedInputs };
|
|
326
|
+
return { inputs, flowLogicDefinition, resolvedInputs, inputQueryError: inputQueryError || undefined, defParamsCount: defParams.length };
|
|
308
327
|
}
|
|
309
328
|
|
|
310
329
|
// Note: reordering of existing elements is NOT possible via Table API because
|
|
@@ -346,6 +365,216 @@ async function calculateInsertOrder(
|
|
|
346
365
|
return 1;
|
|
347
366
|
}
|
|
348
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Flatten an attributes object { key: "val" } into comma-separated "key=val," string (matching UI format).
|
|
370
|
+
* If already a string, returns as-is.
|
|
371
|
+
*/
|
|
372
|
+
function flattenAttributes(attrs: any): string {
|
|
373
|
+
if (!attrs || typeof attrs === 'string') return attrs || '';
|
|
374
|
+
return Object.entries(attrs).map(([k, v]) => k + '=' + v).join(',') + ',';
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Build full trigger input and output objects for the INSERT mutation by fetching from the
|
|
379
|
+
* triggerpicker API (/api/now/hub/triggerpicker/{id}) — the same endpoint Flow Designer UI uses.
|
|
380
|
+
*
|
|
381
|
+
* The UI sends ALL inputs with full parameter definitions (choices, defaults, attributes) and
|
|
382
|
+
* ALL outputs in a single INSERT mutation. This function replicates that format exactly.
|
|
383
|
+
*
|
|
384
|
+
* Fallback: if the triggerpicker API fails, queries sys_hub_trigger_input / sys_hub_trigger_output
|
|
385
|
+
* via the Table API (same approach as buildActionInputsForInsert / buildFlowLogicInputsForInsert).
|
|
386
|
+
*/
|
|
387
|
+
async function buildTriggerInputsForInsert(
|
|
388
|
+
client: any,
|
|
389
|
+
trigDefId: string,
|
|
390
|
+
userTable?: string,
|
|
391
|
+
userCondition?: string
|
|
392
|
+
): Promise<{ inputs: any[]; outputs: any[]; error?: string }> {
|
|
393
|
+
var apiInputs: any[] = [];
|
|
394
|
+
var apiOutputs: any[] = [];
|
|
395
|
+
var fetchError = '';
|
|
396
|
+
|
|
397
|
+
// Strategy 1: triggerpicker API (primary — same as Flow Designer UI)
|
|
398
|
+
try {
|
|
399
|
+
var tpResp = await client.get('/api/now/hub/triggerpicker/' + trigDefId, {
|
|
400
|
+
params: { sysparm_transaction_scope: 'global' },
|
|
401
|
+
headers: { Accept: 'application/json' }
|
|
402
|
+
});
|
|
403
|
+
var tpData = tpResp.data?.result || tpResp.data;
|
|
404
|
+
if (tpData && typeof tpData === 'object') {
|
|
405
|
+
apiInputs = tpData.inputs || tpData.trigger_inputs || [];
|
|
406
|
+
apiOutputs = tpData.outputs || tpData.trigger_outputs || [];
|
|
407
|
+
}
|
|
408
|
+
} catch (tpErr: any) {
|
|
409
|
+
fetchError = 'triggerpicker: ' + (tpErr.message || 'unknown');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Strategy 2: Table API fallback (query sys_hub_trigger_input / sys_hub_trigger_output)
|
|
413
|
+
if (apiInputs.length === 0) {
|
|
414
|
+
try {
|
|
415
|
+
var tiResp = await client.get('/api/now/table/sys_hub_trigger_input', {
|
|
416
|
+
params: {
|
|
417
|
+
sysparm_query: 'model=' + trigDefId,
|
|
418
|
+
sysparm_fields: 'sys_id,element,label,internal_type,mandatory,default_value,order,max_length,hint,read_only,attributes,reference,reference_display,choice,dependent_on_field,use_dependent_field',
|
|
419
|
+
sysparm_display_value: 'false',
|
|
420
|
+
sysparm_limit: 50
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
var tableInputs = tiResp.data.result || [];
|
|
424
|
+
apiInputs = tableInputs.map(function (rec: any) {
|
|
425
|
+
return {
|
|
426
|
+
id: str(rec.sys_id), name: str(rec.element), label: str(rec.label) || str(rec.element),
|
|
427
|
+
type: str(rec.internal_type) || 'string',
|
|
428
|
+
type_label: TYPE_LABELS[str(rec.internal_type) || 'string'] || str(rec.internal_type),
|
|
429
|
+
mandatory: str(rec.mandatory) === 'true',
|
|
430
|
+
order: parseInt(str(rec.order) || '0', 10),
|
|
431
|
+
maxsize: parseInt(str(rec.max_length) || '4000', 10),
|
|
432
|
+
hint: str(rec.hint), defaultValue: str(rec.default_value),
|
|
433
|
+
reference: str(rec.reference), reference_display: str(rec.reference_display),
|
|
434
|
+
use_dependent: str(rec.use_dependent_field) === 'true',
|
|
435
|
+
dependent_on: str(rec.dependent_on_field),
|
|
436
|
+
attributes: str(rec.attributes)
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
fetchError = '';
|
|
440
|
+
} catch (tiErr: any) {
|
|
441
|
+
fetchError += '; table_api_inputs: ' + (tiErr.message || 'unknown');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (apiOutputs.length === 0) {
|
|
445
|
+
try {
|
|
446
|
+
var toResp = await client.get('/api/now/table/sys_hub_trigger_output', {
|
|
447
|
+
params: {
|
|
448
|
+
sysparm_query: 'model=' + trigDefId,
|
|
449
|
+
sysparm_fields: 'sys_id,element,label,internal_type,mandatory,order,max_length,hint,attributes,reference,reference_display,use_dependent_field,dependent_on_field',
|
|
450
|
+
sysparm_display_value: 'false',
|
|
451
|
+
sysparm_limit: 50
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
var tableOutputs = toResp.data.result || [];
|
|
455
|
+
apiOutputs = tableOutputs.map(function (rec: any) {
|
|
456
|
+
return {
|
|
457
|
+
id: str(rec.sys_id), name: str(rec.element), label: str(rec.label) || str(rec.element),
|
|
458
|
+
type: str(rec.internal_type) || 'string',
|
|
459
|
+
type_label: TYPE_LABELS[str(rec.internal_type) || 'string'] || str(rec.internal_type),
|
|
460
|
+
mandatory: str(rec.mandatory) === 'true',
|
|
461
|
+
order: parseInt(str(rec.order) || '0', 10),
|
|
462
|
+
maxsize: parseInt(str(rec.max_length) || '200', 10),
|
|
463
|
+
hint: str(rec.hint), reference: str(rec.reference), reference_display: str(rec.reference_display),
|
|
464
|
+
use_dependent: str(rec.use_dependent_field) === 'true',
|
|
465
|
+
dependent_on: str(rec.dependent_on_field),
|
|
466
|
+
attributes: str(rec.attributes)
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
} catch (_) {}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Transform inputs into GraphQL mutation format (matching exact UI structure)
|
|
473
|
+
var inputs = apiInputs.map(function (inp: any) {
|
|
474
|
+
var paramType = inp.type || 'string';
|
|
475
|
+
var name = inp.name || '';
|
|
476
|
+
var label = inp.label || name;
|
|
477
|
+
var attrs = typeof inp.attributes === 'object' ? flattenAttributes(inp.attributes) : (inp.attributes || '');
|
|
478
|
+
|
|
479
|
+
// Determine value: user-provided > default
|
|
480
|
+
var value = '';
|
|
481
|
+
if (name === 'table' && userTable) value = userTable;
|
|
482
|
+
else if (name === 'condition') value = userCondition || '^EQ';
|
|
483
|
+
else if (inp.defaultValue) value = inp.defaultValue;
|
|
484
|
+
|
|
485
|
+
var parameter: any = {
|
|
486
|
+
id: inp.id || '', label: label, name: name, type: paramType,
|
|
487
|
+
type_label: inp.type_label || TYPE_LABELS[paramType] || paramType,
|
|
488
|
+
order: inp.order || 0, extended: inp.extended || false,
|
|
489
|
+
mandatory: inp.mandatory || false, readonly: inp.readonly || false,
|
|
490
|
+
maxsize: inp.maxsize || 4000, data_structure: '',
|
|
491
|
+
reference: inp.reference || '', reference_display: inp.reference_display || '',
|
|
492
|
+
ref_qual: inp.ref_qual || '', choiceOption: inp.choiceOption || '',
|
|
493
|
+
table: '', columnName: '', defaultValue: inp.defaultValue || '',
|
|
494
|
+
use_dependent: inp.use_dependent || false, dependent_on: inp.dependent_on || '',
|
|
495
|
+
internal_link: inp.internal_link || '', show_ref_finder: inp.show_ref_finder || false,
|
|
496
|
+
local: inp.local || false, attributes: attrs, sys_class_name: '', children: []
|
|
497
|
+
};
|
|
498
|
+
if (inp.hint) parameter.hint = inp.hint;
|
|
499
|
+
if (inp.defaultDisplayValue) parameter.defaultDisplayValue = inp.defaultDisplayValue;
|
|
500
|
+
if (inp.choices) parameter.choices = inp.choices;
|
|
501
|
+
if (inp.defaultChoices) parameter.defaultChoices = inp.defaultChoices;
|
|
502
|
+
|
|
503
|
+
var inputObj: any = {
|
|
504
|
+
name: name, label: label, internalType: paramType,
|
|
505
|
+
mandatory: inp.mandatory || false, order: inp.order || 0,
|
|
506
|
+
valueSysId: '', field_name: name, type: paramType, children: [],
|
|
507
|
+
displayValue: { value: '' },
|
|
508
|
+
value: value ? { schemaless: false, schemalessValue: '', value: value } : { value: '' },
|
|
509
|
+
parameter: parameter
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
// Add choiceList for choice-type inputs (top-level, matching UI format)
|
|
513
|
+
if (inp.choices && Array.isArray(inp.choices)) {
|
|
514
|
+
inputObj.choiceList = inp.choices.map(function (c: any) {
|
|
515
|
+
return { label: c.label, value: c.value };
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return inputObj;
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Transform outputs into GraphQL mutation format
|
|
523
|
+
var outputs = apiOutputs.map(function (out: any) {
|
|
524
|
+
var paramType = out.type || 'string';
|
|
525
|
+
var name = out.name || '';
|
|
526
|
+
var label = out.label || name;
|
|
527
|
+
var attrs = typeof out.attributes === 'object' ? flattenAttributes(out.attributes) : (out.attributes || '');
|
|
528
|
+
|
|
529
|
+
var parameter: any = {
|
|
530
|
+
id: out.id || '', label: label, name: name, type: paramType,
|
|
531
|
+
type_label: out.type_label || TYPE_LABELS[paramType] || paramType,
|
|
532
|
+
hint: out.hint || '', order: out.order || 0, extended: out.extended || false,
|
|
533
|
+
mandatory: out.mandatory || false, readonly: out.readonly || false,
|
|
534
|
+
maxsize: out.maxsize || 200, data_structure: '',
|
|
535
|
+
reference: out.reference || '', reference_display: out.reference_display || '',
|
|
536
|
+
ref_qual: '', choiceOption: '', table: '', columnName: '', defaultValue: '',
|
|
537
|
+
use_dependent: out.use_dependent || false, dependent_on: out.dependent_on || '',
|
|
538
|
+
internal_link: out.internal_link || '', show_ref_finder: false, local: false,
|
|
539
|
+
attributes: attrs, sys_class_name: ''
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// Build children for complex types (like array.object)
|
|
543
|
+
var children: any[] = [];
|
|
544
|
+
var paramChildren: any[] = [];
|
|
545
|
+
if (out.children && Array.isArray(out.children)) {
|
|
546
|
+
children = out.children.map(function (child: any) {
|
|
547
|
+
return { id: '', name: child.name || '', scriptActive: false, children: [], value: { value: '' }, script: null };
|
|
548
|
+
});
|
|
549
|
+
paramChildren = out.children.map(function (child: any) {
|
|
550
|
+
return {
|
|
551
|
+
id: '', label: child.label || child.name || '', name: child.name || '',
|
|
552
|
+
type: child.type || 'string', type_label: child.type_label || TYPE_LABELS[child.type || 'string'] || 'String',
|
|
553
|
+
hint: '', order: child.order || 0, extended: false, mandatory: false, readonly: false, maxsize: 0,
|
|
554
|
+
data_structure: '', reference: '', reference_display: '', ref_qual: '', choiceOption: '',
|
|
555
|
+
table: '', columnName: '', defaultValue: '', defaultDisplayValue: '',
|
|
556
|
+
use_dependent: false, dependent_on: false, show_ref_finder: false, local: false,
|
|
557
|
+
attributes: '', sys_class_name: '',
|
|
558
|
+
uiDisplayType: child.uiDisplayType || child.type || 'string',
|
|
559
|
+
uiDisplayTypeLabel: child.type_label || 'String',
|
|
560
|
+
internal_link: '', value: '', display_value: '', scriptActive: false,
|
|
561
|
+
parent: out.id || '',
|
|
562
|
+
fieldFacetMap: 'uiTypeLabel=' + (child.type_label || 'String') + ',',
|
|
563
|
+
children: [], script: null
|
|
564
|
+
};
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
parameter.children = paramChildren;
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
name: name, value: '', displayValue: '', type: paramType,
|
|
571
|
+
order: out.order || 0, label: label, children: children, parameter: parameter
|
|
572
|
+
};
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
return { inputs, outputs, error: fetchError || undefined };
|
|
576
|
+
}
|
|
577
|
+
|
|
349
578
|
async function addTriggerViaGraphQL(
|
|
350
579
|
client: any,
|
|
351
580
|
flowId: string,
|
|
@@ -413,8 +642,13 @@ async function addTriggerViaGraphQL(
|
|
|
413
642
|
}
|
|
414
643
|
if (!trigDefId) return { success: false, error: 'Trigger definition not found for: ' + triggerType, steps };
|
|
415
644
|
|
|
645
|
+
// Build full trigger inputs and outputs from triggerpicker API (matching UI format)
|
|
646
|
+
var triggerData = await buildTriggerInputsForInsert(client, trigDefId!, table, condition);
|
|
647
|
+
steps.trigger_data = { inputCount: triggerData.inputs.length, outputCount: triggerData.outputs.length, error: triggerData.error };
|
|
648
|
+
|
|
416
649
|
const triggerResponseFields = 'triggerInstances { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
417
650
|
try {
|
|
651
|
+
// Single INSERT with full inputs and outputs (matching UI behavior — no separate UPDATE needed)
|
|
418
652
|
const insertResult = await executeFlowPatchMutation(client, {
|
|
419
653
|
flowId: flowId,
|
|
420
654
|
triggerInstances: {
|
|
@@ -426,8 +660,8 @@ async function addTriggerViaGraphQL(
|
|
|
426
660
|
type: trigType,
|
|
427
661
|
hasDynamicOutputs: false,
|
|
428
662
|
metadata: '{"predicates":[]}',
|
|
429
|
-
inputs:
|
|
430
|
-
outputs:
|
|
663
|
+
inputs: triggerData.inputs,
|
|
664
|
+
outputs: triggerData.outputs
|
|
431
665
|
}]
|
|
432
666
|
}
|
|
433
667
|
}, triggerResponseFields);
|
|
@@ -436,30 +670,6 @@ async function addTriggerViaGraphQL(
|
|
|
436
670
|
steps.insert = { success: !!triggerId, triggerId };
|
|
437
671
|
if (!triggerId) return { success: false, steps, error: 'GraphQL trigger INSERT returned no trigger ID' };
|
|
438
672
|
|
|
439
|
-
if (table) {
|
|
440
|
-
const updateInputs: any[] = [
|
|
441
|
-
{
|
|
442
|
-
name: 'table',
|
|
443
|
-
displayField: 'number',
|
|
444
|
-
displayValue: { schemaless: false, schemalessValue: '', value: table.charAt(0).toUpperCase() + table.slice(1) },
|
|
445
|
-
value: { schemaless: false, schemalessValue: '', value: table }
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
name: 'condition',
|
|
449
|
-
displayValue: { schemaless: false, schemalessValue: '', value: condition || '^EQ' }
|
|
450
|
-
}
|
|
451
|
-
];
|
|
452
|
-
try {
|
|
453
|
-
await executeFlowPatchMutation(client, {
|
|
454
|
-
flowId: flowId,
|
|
455
|
-
triggerInstances: { update: [{ id: triggerId, inputs: updateInputs }] }
|
|
456
|
-
}, triggerResponseFields);
|
|
457
|
-
steps.update = { success: true, table, condition: condition || '^EQ' };
|
|
458
|
-
} catch (e: any) {
|
|
459
|
-
steps.update = { success: false, error: e.message };
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
673
|
return { success: true, triggerId, steps };
|
|
464
674
|
} catch (e: any) {
|
|
465
675
|
steps.insert = { success: false, error: e.message };
|
|
@@ -467,6 +677,18 @@ async function addTriggerViaGraphQL(
|
|
|
467
677
|
}
|
|
468
678
|
}
|
|
469
679
|
|
|
680
|
+
// Common short-name aliases for action types — maps user-friendly names to ServiceNow internal names
|
|
681
|
+
const ACTION_TYPE_ALIASES: Record<string, string[]> = {
|
|
682
|
+
script: ['script_step', 'run_script', 'Run Script'],
|
|
683
|
+
log: ['log_message', 'Log Message', 'Log'],
|
|
684
|
+
create_record: ['Create Record'],
|
|
685
|
+
update_record: ['Update Record'],
|
|
686
|
+
notification: ['send_notification', 'send_email', 'Send Notification', 'Send Email'],
|
|
687
|
+
field_update: ['set_field_values', 'Set Field Values'],
|
|
688
|
+
wait: ['wait_for', 'Wait For Duration', 'Wait'],
|
|
689
|
+
approval: ['ask_for_approval', 'create_approval', 'Ask for Approval'],
|
|
690
|
+
};
|
|
691
|
+
|
|
470
692
|
async function addActionViaGraphQL(
|
|
471
693
|
client: any,
|
|
472
694
|
flowId: string,
|
|
@@ -479,7 +701,7 @@ async function addActionViaGraphQL(
|
|
|
479
701
|
): Promise<{ success: boolean; actionId?: string; steps?: any; error?: string }> {
|
|
480
702
|
const steps: any = {};
|
|
481
703
|
|
|
482
|
-
// Dynamically look up action definition in sys_hub_action_type_snapshot
|
|
704
|
+
// Dynamically look up action definition in sys_hub_action_type_snapshot and sys_hub_action_type_definition
|
|
483
705
|
// Prefer global/core actions over spoke-specific ones (e.g. core "Update Record" vs spoke-specific "Update Record")
|
|
484
706
|
const snapshotFields = 'sys_id,internal_name,name,sys_scope,sys_package';
|
|
485
707
|
let actionDefId: string | null = null;
|
|
@@ -507,41 +729,66 @@ async function addActionViaGraphQL(
|
|
|
507
729
|
return candidates[0];
|
|
508
730
|
};
|
|
509
731
|
|
|
510
|
-
for
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
732
|
+
// Helper: search a table for action definitions by exact match and LIKE
|
|
733
|
+
const searchTable = async (tableName: string, searchTerms: string[]): Promise<void> => {
|
|
734
|
+
for (var si = 0; si < searchTerms.length && !actionDefId; si++) {
|
|
735
|
+
var term = searchTerms[si];
|
|
736
|
+
// Exact match on internal_name and name
|
|
737
|
+
for (const field of ['internal_name', 'name']) {
|
|
738
|
+
if (actionDefId) break;
|
|
739
|
+
try {
|
|
740
|
+
const resp = await client.get('/api/now/table/' + tableName, {
|
|
741
|
+
params: { sysparm_query: field + '=' + term, sysparm_fields: snapshotFields, sysparm_limit: 10 }
|
|
742
|
+
});
|
|
743
|
+
const results = resp.data.result || [];
|
|
744
|
+
if (results.length > 1) {
|
|
745
|
+
steps.def_lookup_candidates = results.map((r: any) => ({ sys_id: r.sys_id, internal_name: str(r.internal_name), name: str(r.name), scope: str(r.sys_scope), package: str(r.sys_package) }));
|
|
746
|
+
}
|
|
747
|
+
const found = pickBest(results);
|
|
748
|
+
if (found?.sys_id) {
|
|
749
|
+
actionDefId = found.sys_id;
|
|
750
|
+
steps.def_lookup = { id: found.sys_id, internal_name: str(found.internal_name), name: str(found.name), scope: str(found.sys_scope), package: str(found.sys_package), matched: tableName + ':' + field + '=' + term };
|
|
751
|
+
}
|
|
752
|
+
} catch (_) {}
|
|
519
753
|
}
|
|
520
|
-
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
|
|
754
|
+
// LIKE search
|
|
755
|
+
if (!actionDefId) {
|
|
756
|
+
try {
|
|
757
|
+
const resp = await client.get('/api/now/table/' + tableName, {
|
|
758
|
+
params: {
|
|
759
|
+
sysparm_query: 'internal_nameLIKE' + term + '^ORnameLIKE' + term,
|
|
760
|
+
sysparm_fields: snapshotFields, sysparm_limit: 10
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
const results = resp.data.result || [];
|
|
764
|
+
if (results.length > 0 && !steps.def_lookup_fallback_candidates) {
|
|
765
|
+
steps.def_lookup_fallback_candidates = results.map((r: any) => ({ sys_id: r.sys_id, internal_name: str(r.internal_name), name: str(r.name), scope: str(r.sys_scope), package: str(r.sys_package) }));
|
|
766
|
+
}
|
|
767
|
+
const found = pickBest(results);
|
|
768
|
+
if (found?.sys_id) {
|
|
769
|
+
actionDefId = found.sys_id;
|
|
770
|
+
steps.def_lookup = { id: found.sys_id, internal_name: str(found.internal_name), name: str(found.name), scope: str(found.sys_scope), package: str(found.sys_package), matched: tableName + ':LIKE ' + term };
|
|
771
|
+
}
|
|
772
|
+
} catch (_) {}
|
|
524
773
|
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
// Build search terms: original actionType + any alias variations
|
|
778
|
+
var searchTerms = [actionType];
|
|
779
|
+
var aliases = ACTION_TYPE_ALIASES[actionType.toLowerCase()];
|
|
780
|
+
if (aliases) searchTerms = searchTerms.concat(aliases);
|
|
781
|
+
|
|
782
|
+
// Search 1: sys_hub_action_type_snapshot (published action snapshots)
|
|
783
|
+
await searchTable('sys_hub_action_type_snapshot', searchTerms);
|
|
784
|
+
|
|
785
|
+
// Search 2: sys_hub_action_type_definition (action definitions — includes built-in/native actions)
|
|
527
786
|
if (!actionDefId) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
params: {
|
|
531
|
-
sysparm_query: 'internal_nameLIKE' + actionType + '^ORnameLIKE' + actionType,
|
|
532
|
-
sysparm_fields: snapshotFields, sysparm_limit: 10
|
|
533
|
-
}
|
|
534
|
-
});
|
|
535
|
-
const results = resp.data.result || [];
|
|
536
|
-
steps.def_lookup_fallback_candidates = results.map((r: any) => ({ sys_id: r.sys_id, internal_name: str(r.internal_name), name: str(r.name), scope: str(r.sys_scope), package: str(r.sys_package) }));
|
|
537
|
-
const found = pickBest(results);
|
|
538
|
-
if (found?.sys_id) {
|
|
539
|
-
actionDefId = found.sys_id;
|
|
540
|
-
steps.def_lookup = { id: found.sys_id, internal_name: str(found.internal_name), name: str(found.name), scope: str(found.sys_scope), package: str(found.sys_package), matched: 'LIKE ' + actionType };
|
|
541
|
-
}
|
|
542
|
-
} catch (_) {}
|
|
787
|
+
steps.snapshot_not_found = true;
|
|
788
|
+
await searchTable('sys_hub_action_type_definition', searchTerms);
|
|
543
789
|
}
|
|
544
|
-
|
|
790
|
+
|
|
791
|
+
if (!actionDefId) return { success: false, error: 'Action definition not found for: ' + actionType + ' (searched snapshot + definition tables with terms: ' + searchTerms.join(', ') + ')', steps };
|
|
545
792
|
|
|
546
793
|
// Build full input objects with parameter definitions (matching UI format)
|
|
547
794
|
const inputResult = await buildActionInputsForInsert(client, actionDefId, inputs);
|
|
@@ -662,6 +909,7 @@ async function addFlowLogicViaGraphQL(
|
|
|
662
909
|
const inputResult = await buildFlowLogicInputsForInsert(client, defId, defRecord, inputs);
|
|
663
910
|
steps.available_inputs = inputResult.inputs.map((i: any) => ({ name: i.name, label: i.parameter?.label }));
|
|
664
911
|
steps.resolved_inputs = inputResult.resolvedInputs;
|
|
912
|
+
steps.input_query_stats = { defParamsFound: inputResult.defParamsCount, inputsBuilt: inputResult.inputs.length, error: inputResult.inputQueryError };
|
|
665
913
|
|
|
666
914
|
// Calculate insertion order
|
|
667
915
|
const resolvedOrder = await calculateInsertOrder(client, flowId, parentUiId, order);
|
|
@@ -1130,8 +1378,7 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
1130
1378
|
},
|
|
1131
1379
|
action_type: {
|
|
1132
1380
|
type: 'string',
|
|
1133
|
-
|
|
1134
|
-
description: 'Action type to add (for add_action)',
|
|
1381
|
+
description: 'Action type to add (for add_action). Looked up dynamically by internal_name or name in sys_hub_action_type_snapshot and sys_hub_action_type_definition. Common short names: log, script, create_record, update_record, notification, field_update, wait, approval. You can also use the exact ServiceNow internal name (e.g. "sn_fd.script_step", "global.update_record") or display name (e.g. "Run Script", "Update Record").',
|
|
1135
1382
|
default: 'log'
|
|
1136
1383
|
},
|
|
1137
1384
|
action_name: {
|