snow-flow 10.0.1-dev.462 → 10.0.1-dev.464
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
|
@@ -384,15 +384,238 @@ function flattenAttributes(attrs: any): string {
|
|
|
384
384
|
* Fallback: if the triggerpicker API fails, queries sys_hub_trigger_input / sys_hub_trigger_output
|
|
385
385
|
* via the Table API (same approach as buildActionInputsForInsert / buildFlowLogicInputsForInsert).
|
|
386
386
|
*/
|
|
387
|
+
/**
|
|
388
|
+
* Parse XML string from triggerpicker API to extract input/output elements.
|
|
389
|
+
* The triggerpicker endpoint may return XML instead of JSON on some instances.
|
|
390
|
+
*/
|
|
391
|
+
function parseTriggerpickerXml(xmlStr: string): { inputs: any[]; outputs: any[] } {
|
|
392
|
+
var inputs: any[] = [];
|
|
393
|
+
var outputs: any[] = [];
|
|
394
|
+
|
|
395
|
+
// Helper: extract text content of an XML element by tag name
|
|
396
|
+
var getTag = function (xml: string, tag: string): string {
|
|
397
|
+
var m = xml.match(new RegExp('<' + tag + '>([\\s\\S]*?)</' + tag + '>'));
|
|
398
|
+
return m ? m[1].trim() : '';
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// Helper: extract all occurrences of a repeated element
|
|
402
|
+
var getAll = function (xml: string, tag: string): string[] {
|
|
403
|
+
var re = new RegExp('<' + tag + '>([\\s\\S]*?)</' + tag + '>', 'g');
|
|
404
|
+
var results: string[] = [];
|
|
405
|
+
var m;
|
|
406
|
+
while ((m = re.exec(xml)) !== null) results.push(m[1]);
|
|
407
|
+
return results;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// Try to find input elements — XML may wrap them in <inputs><element>...</element></inputs>
|
|
411
|
+
// or <trigger_inputs><input>...</input></trigger_inputs> etc.
|
|
412
|
+
var inputsSection = getTag(xmlStr, 'inputs') || getTag(xmlStr, 'trigger_inputs') || xmlStr;
|
|
413
|
+
var inputElements = getAll(inputsSection, 'element');
|
|
414
|
+
if (inputElements.length === 0) inputElements = getAll(inputsSection, 'input');
|
|
415
|
+
if (inputElements.length === 0) inputElements = getAll(inputsSection, 'trigger_input');
|
|
416
|
+
|
|
417
|
+
for (var ii = 0; ii < inputElements.length; ii++) {
|
|
418
|
+
var el = inputElements[ii];
|
|
419
|
+
var name = getTag(el, 'name') || getTag(el, 'element');
|
|
420
|
+
if (!name) continue;
|
|
421
|
+
inputs.push({
|
|
422
|
+
id: getTag(el, 'sys_id') || getTag(el, 'id'),
|
|
423
|
+
name: name,
|
|
424
|
+
label: getTag(el, 'label') || name,
|
|
425
|
+
type: getTag(el, 'type') || getTag(el, 'internal_type') || 'string',
|
|
426
|
+
type_label: getTag(el, 'type_label') || '',
|
|
427
|
+
mandatory: getTag(el, 'mandatory') === 'true',
|
|
428
|
+
order: parseInt(getTag(el, 'order') || '0', 10),
|
|
429
|
+
maxsize: parseInt(getTag(el, 'maxsize') || getTag(el, 'max_length') || '4000', 10),
|
|
430
|
+
hint: getTag(el, 'hint'),
|
|
431
|
+
defaultValue: getTag(el, 'defaultValue') || getTag(el, 'default_value'),
|
|
432
|
+
defaultDisplayValue: getTag(el, 'defaultDisplayValue') || getTag(el, 'default_display_value'),
|
|
433
|
+
choiceOption: getTag(el, 'choiceOption') || getTag(el, 'choice_option'),
|
|
434
|
+
reference: getTag(el, 'reference'),
|
|
435
|
+
reference_display: getTag(el, 'reference_display'),
|
|
436
|
+
use_dependent: getTag(el, 'use_dependent') === 'true',
|
|
437
|
+
dependent_on: getTag(el, 'dependent_on'),
|
|
438
|
+
internal_link: getTag(el, 'internal_link'),
|
|
439
|
+
attributes: getTag(el, 'attributes')
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
var outputsSection = getTag(xmlStr, 'outputs') || getTag(xmlStr, 'trigger_outputs') || '';
|
|
444
|
+
var outputElements = getAll(outputsSection, 'element');
|
|
445
|
+
if (outputElements.length === 0) outputElements = getAll(outputsSection, 'output');
|
|
446
|
+
if (outputElements.length === 0) outputElements = getAll(outputsSection, 'trigger_output');
|
|
447
|
+
|
|
448
|
+
for (var oi = 0; oi < outputElements.length; oi++) {
|
|
449
|
+
var oel = outputElements[oi];
|
|
450
|
+
var oname = getTag(oel, 'name') || getTag(oel, 'element');
|
|
451
|
+
if (!oname) continue;
|
|
452
|
+
outputs.push({
|
|
453
|
+
id: getTag(oel, 'sys_id') || getTag(oel, 'id'),
|
|
454
|
+
name: oname,
|
|
455
|
+
label: getTag(oel, 'label') || oname,
|
|
456
|
+
type: getTag(oel, 'type') || getTag(oel, 'internal_type') || 'string',
|
|
457
|
+
type_label: getTag(oel, 'type_label') || '',
|
|
458
|
+
mandatory: getTag(oel, 'mandatory') === 'true',
|
|
459
|
+
order: parseInt(getTag(oel, 'order') || '0', 10),
|
|
460
|
+
maxsize: parseInt(getTag(oel, 'maxsize') || getTag(oel, 'max_length') || '200', 10),
|
|
461
|
+
hint: getTag(oel, 'hint'),
|
|
462
|
+
reference: getTag(oel, 'reference'),
|
|
463
|
+
reference_display: getTag(oel, 'reference_display'),
|
|
464
|
+
use_dependent: getTag(oel, 'use_dependent') === 'true',
|
|
465
|
+
dependent_on: getTag(oel, 'dependent_on'),
|
|
466
|
+
internal_link: getTag(oel, 'internal_link'),
|
|
467
|
+
attributes: getTag(oel, 'attributes')
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return { inputs, outputs };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Build a single trigger input object in GraphQL mutation format.
|
|
476
|
+
* Used by buildTriggerInputsForInsert and the hardcoded fallback.
|
|
477
|
+
*/
|
|
478
|
+
function buildTriggerInputObj(inp: any, userTable?: string, userCondition?: string): any {
|
|
479
|
+
var paramType = inp.type || 'string';
|
|
480
|
+
var name = inp.name || '';
|
|
481
|
+
var label = inp.label || name;
|
|
482
|
+
var attrs = typeof inp.attributes === 'object' ? flattenAttributes(inp.attributes) : (inp.attributes || '');
|
|
483
|
+
|
|
484
|
+
// Determine value: user-provided > default
|
|
485
|
+
var value = '';
|
|
486
|
+
if (name === 'table' && userTable) value = userTable;
|
|
487
|
+
else if (name === 'condition') value = userCondition || '^EQ';
|
|
488
|
+
else if (inp.defaultValue) value = inp.defaultValue;
|
|
489
|
+
|
|
490
|
+
var parameter: any = {
|
|
491
|
+
id: inp.id || '', label: label, name: name, type: paramType,
|
|
492
|
+
type_label: inp.type_label || TYPE_LABELS[paramType] || paramType,
|
|
493
|
+
order: inp.order || 0, extended: inp.extended || false,
|
|
494
|
+
mandatory: inp.mandatory || false, readonly: inp.readonly || false,
|
|
495
|
+
maxsize: inp.maxsize || 4000, data_structure: '',
|
|
496
|
+
reference: inp.reference || '', reference_display: inp.reference_display || '',
|
|
497
|
+
ref_qual: inp.ref_qual || '', choiceOption: inp.choiceOption || '',
|
|
498
|
+
table: '', columnName: '', defaultValue: inp.defaultValue || '',
|
|
499
|
+
use_dependent: inp.use_dependent || false, dependent_on: inp.dependent_on || '',
|
|
500
|
+
internal_link: inp.internal_link || '', show_ref_finder: inp.show_ref_finder || false,
|
|
501
|
+
local: inp.local || false, attributes: attrs, sys_class_name: '', children: []
|
|
502
|
+
};
|
|
503
|
+
if (inp.hint) parameter.hint = inp.hint;
|
|
504
|
+
if (inp.defaultDisplayValue) parameter.defaultDisplayValue = inp.defaultDisplayValue;
|
|
505
|
+
if (inp.choices) parameter.choices = inp.choices;
|
|
506
|
+
if (inp.defaultChoices) parameter.defaultChoices = inp.defaultChoices;
|
|
507
|
+
|
|
508
|
+
var inputObj: any = {
|
|
509
|
+
name: name, label: label, internalType: paramType,
|
|
510
|
+
mandatory: inp.mandatory || false, order: inp.order || 0,
|
|
511
|
+
valueSysId: '', field_name: name, type: paramType, children: [],
|
|
512
|
+
displayValue: { value: '' },
|
|
513
|
+
value: value ? { schemaless: false, schemalessValue: '', value: value } : { value: '' },
|
|
514
|
+
parameter: parameter
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
if (inp.choices && Array.isArray(inp.choices)) {
|
|
518
|
+
inputObj.choiceList = inp.choices.map(function (c: any) {
|
|
519
|
+
return { label: c.label, value: c.value };
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return inputObj;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Build a single trigger output object in GraphQL mutation format.
|
|
528
|
+
*/
|
|
529
|
+
function buildTriggerOutputObj(out: any): any {
|
|
530
|
+
var paramType = out.type || 'string';
|
|
531
|
+
var name = out.name || '';
|
|
532
|
+
var label = out.label || name;
|
|
533
|
+
var attrs = typeof out.attributes === 'object' ? flattenAttributes(out.attributes) : (out.attributes || '');
|
|
534
|
+
|
|
535
|
+
var parameter: any = {
|
|
536
|
+
id: out.id || '', label: label, name: name, type: paramType,
|
|
537
|
+
type_label: out.type_label || TYPE_LABELS[paramType] || paramType,
|
|
538
|
+
hint: out.hint || '', order: out.order || 0, extended: out.extended || false,
|
|
539
|
+
mandatory: out.mandatory || false, readonly: out.readonly || false,
|
|
540
|
+
maxsize: out.maxsize || 200, data_structure: '',
|
|
541
|
+
reference: out.reference || '', reference_display: out.reference_display || '',
|
|
542
|
+
ref_qual: '', choiceOption: '', table: '', columnName: '', defaultValue: '',
|
|
543
|
+
use_dependent: out.use_dependent || false, dependent_on: out.dependent_on || '',
|
|
544
|
+
internal_link: out.internal_link || '', show_ref_finder: false, local: false,
|
|
545
|
+
attributes: attrs, sys_class_name: ''
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
var children: any[] = [];
|
|
549
|
+
var paramChildren: any[] = [];
|
|
550
|
+
if (out.children && Array.isArray(out.children)) {
|
|
551
|
+
children = out.children.map(function (child: any) {
|
|
552
|
+
return { id: '', name: child.name || '', scriptActive: false, children: [], value: { value: '' }, script: null };
|
|
553
|
+
});
|
|
554
|
+
paramChildren = out.children.map(function (child: any) {
|
|
555
|
+
return {
|
|
556
|
+
id: '', label: child.label || child.name || '', name: child.name || '',
|
|
557
|
+
type: child.type || 'string', type_label: child.type_label || TYPE_LABELS[child.type || 'string'] || 'String',
|
|
558
|
+
hint: '', order: child.order || 0, extended: false, mandatory: false, readonly: false, maxsize: 0,
|
|
559
|
+
data_structure: '', reference: '', reference_display: '', ref_qual: '', choiceOption: '',
|
|
560
|
+
table: '', columnName: '', defaultValue: '', defaultDisplayValue: '',
|
|
561
|
+
use_dependent: false, dependent_on: false, show_ref_finder: false, local: false,
|
|
562
|
+
attributes: '', sys_class_name: '',
|
|
563
|
+
uiDisplayType: child.uiDisplayType || child.type || 'string',
|
|
564
|
+
uiDisplayTypeLabel: child.type_label || 'String',
|
|
565
|
+
internal_link: '', value: '', display_value: '', scriptActive: false,
|
|
566
|
+
parent: out.id || '',
|
|
567
|
+
fieldFacetMap: 'uiTypeLabel=' + (child.type_label || 'String') + ',',
|
|
568
|
+
children: [], script: null
|
|
569
|
+
};
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
parameter.children = paramChildren;
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
name: name, value: '', displayValue: '', type: paramType,
|
|
576
|
+
order: out.order || 0, label: label, children: children, parameter: parameter
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Hardcoded record trigger inputs — used as ultimate fallback when API and Table lookups fail.
|
|
582
|
+
* These definitions match the exact format captured from the Flow Designer UI for record-based triggers
|
|
583
|
+
* (record_create, record_update, record_create_or_update). Field names and types are consistent across instances.
|
|
584
|
+
*/
|
|
585
|
+
function getRecordTriggerFallbackInputs(): any[] {
|
|
586
|
+
return [
|
|
587
|
+
{ name: 'table', label: 'Table', type: 'table_name', type_label: 'Table Name', mandatory: true, order: 1, maxsize: 80, attributes: 'filter_table_source=RECORD_WATCHER_RESTRICTED,' },
|
|
588
|
+
{ name: 'condition', label: 'Condition', type: 'conditions', type_label: 'Conditions', mandatory: false, order: 100, maxsize: 4000, use_dependent: true, dependent_on: 'table', attributes: 'extended_operators=VALCHANGES;CHANGESFROM;CHANGESTO,wants_to_add_conditions=true,modelDependent=trigger_inputs,' },
|
|
589
|
+
{ name: 'run_on_extended', label: 'run_on_extended', type: 'choice', type_label: 'Choice', mandatory: false, order: 100, maxsize: 40, defaultValue: 'false', defaultDisplayValue: 'Run only on current table', choiceOption: '3', attributes: 'advanced=true,', choices: [{ label: 'Run only on current table', value: 'false', order: 0 }, { label: 'Run on current and extended tables', value: 'true', order: 1 }], defaultChoices: [{ label: 'Run only on current table', value: 'false', order: 1 }, { label: 'Run on current and extended tables', value: 'true', order: 2 }] },
|
|
590
|
+
{ name: 'run_flow_in', label: 'run_flow_in', type: 'choice', type_label: 'Choice', mandatory: false, order: 100, maxsize: 40, defaultValue: 'any', defaultDisplayValue: 'any', choiceOption: '3', attributes: 'advanced=true,', choices: [{ label: 'Run flow in background (default)', value: 'background', order: 0 }, { label: 'Run flow in foreground', value: 'foreground', order: 1 }], defaultChoices: [{ label: 'Run flow in background (default)', value: 'background', order: 1 }, { label: 'Run flow in foreground', value: 'foreground', order: 2 }] },
|
|
591
|
+
{ name: 'run_when_user_list', label: 'run_when_user_list', type: 'glide_list', type_label: 'List', mandatory: false, order: 100, maxsize: 4000, reference: 'sys_user', reference_display: 'User', attributes: 'advanced=true,' },
|
|
592
|
+
{ name: 'run_when_setting', label: 'run_when_setting', type: 'choice', type_label: 'Choice', mandatory: false, order: 100, maxsize: 40, defaultValue: 'both', defaultDisplayValue: 'Run for Both Interactive and Non-Interactive Sessions', choiceOption: '3', attributes: 'advanced=true,', choices: [{ label: 'Only Run for Non-Interactive Session', value: 'non_interactive', order: 0 }, { label: 'Only Run for User Interactive Session', value: 'interactive', order: 1 }, { label: 'Run for Both Interactive and Non-Interactive Sessions', value: 'both', order: 2 }], defaultChoices: [{ label: 'Only Run for Non-Interactive Session', value: 'non_interactive', order: 1 }, { label: 'Only Run for User Interactive Session', value: 'interactive', order: 2 }, { label: 'Run for Both Interactive and Non-Interactive Sessions', value: 'both', order: 3 }] },
|
|
593
|
+
{ name: 'run_when_user_setting', label: 'run_when_user_setting', type: 'choice', type_label: 'Choice', mandatory: false, order: 100, maxsize: 40, defaultValue: 'any', defaultDisplayValue: 'Run for any user', choiceOption: '3', attributes: 'advanced=true,', choices: [{ label: 'Do not run if triggered by the following users', value: 'not_one_of', order: 0 }, { label: 'Only Run if triggered by the following users', value: 'one_of', order: 1 }, { label: 'Run for any user', value: 'any', order: 2 }], defaultChoices: [{ label: 'Do not run if triggered by the following users', value: 'not_one_of', order: 1 }, { label: 'Only Run if triggered by the following users', value: 'one_of', order: 2 }, { label: 'Run for any user', value: 'any', order: 3 }] },
|
|
594
|
+
{ name: 'trigger_strategy', label: 'Run Trigger', type: 'choice', type_label: 'Choice', mandatory: false, order: 200, maxsize: 40, defaultValue: 'once', defaultDisplayValue: 'Once', choiceOption: '3', hint: 'Run Trigger every time the condition matches, or only the first time.', choices: [{ label: 'Once', value: 'once', order: 0 }, { label: 'For each unique change', value: 'unique_changes', order: 1 }, { label: 'Only if not currently running', value: 'always', order: 2 }, { label: 'For every update', value: 'every', order: 3 }], defaultChoices: [{ label: 'Once', value: 'once', order: 1 }, { label: 'For each unique change', value: 'unique_changes', order: 2 }, { label: 'Only if not currently running', value: 'always', order: 3 }, { label: 'For every update', value: 'every', order: 4 }] }
|
|
595
|
+
];
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function getRecordTriggerFallbackOutputs(): any[] {
|
|
599
|
+
return [
|
|
600
|
+
{ name: 'current', label: 'Record', type: 'document_id', type_label: 'Document ID', mandatory: true, order: 100, maxsize: 200, use_dependent: true, dependent_on: 'table_name', internal_link: 'table' },
|
|
601
|
+
{ name: 'changed_fields', label: 'Changed Fields', type: 'array.object', type_label: 'Array.Object', mandatory: false, order: 100, maxsize: 4000, attributes: 'uiTypeLabel=Array.Object,co_type_name=FDCollection,child_label=FDChangeDetails,child_type_label=Object,element_mapping_provider=com.glide.flow_design.action.data.FlowDesignVariableMapper,pwd2droppable=true,uiType=array.object,child_type=object,child_name=FDChangeDetails,', children: [{ name: 'field_name', label: 'Field Name', type: 'string', type_label: 'String', order: 1 }, { name: 'previous_value', label: 'Previous Value', type: 'string', type_label: 'String', order: 2 }, { name: 'current_value', label: 'Current Value', type: 'string', type_label: 'String', order: 3 }, { name: 'previous_display_value', label: 'Previous Display Value', type: 'string', type_label: 'String', order: 4 }, { name: 'current_display_value', label: 'Current Display Value', type: 'string', type_label: 'String', order: 5 }] },
|
|
602
|
+
{ name: 'table_name', label: 'Table Name', type: 'table_name', type_label: 'Table Name', mandatory: false, order: 101, maxsize: 200, internal_link: 'table', attributes: 'test_input_hidden=true,' },
|
|
603
|
+
{ name: 'run_start_time', label: 'Run Start Time UTC', type: 'glide_date_time', type_label: 'Date/Time', mandatory: false, order: 110, maxsize: 200, attributes: 'test_input_hidden=true,' },
|
|
604
|
+
{ name: 'run_start_date_time', label: 'Run Start Date/Time', type: 'glide_date_time', type_label: 'Date/Time', mandatory: false, order: 110, maxsize: 200, attributes: 'test_input_hidden=true,' }
|
|
605
|
+
];
|
|
606
|
+
}
|
|
607
|
+
|
|
387
608
|
async function buildTriggerInputsForInsert(
|
|
388
609
|
client: any,
|
|
389
610
|
trigDefId: string,
|
|
611
|
+
trigType: string,
|
|
390
612
|
userTable?: string,
|
|
391
613
|
userCondition?: string
|
|
392
|
-
): Promise<{ inputs: any[]; outputs: any[]; error?: string }> {
|
|
614
|
+
): Promise<{ inputs: any[]; outputs: any[]; source: string; error?: string }> {
|
|
393
615
|
var apiInputs: any[] = [];
|
|
394
616
|
var apiOutputs: any[] = [];
|
|
395
617
|
var fetchError = '';
|
|
618
|
+
var source = '';
|
|
396
619
|
|
|
397
620
|
// Strategy 1: triggerpicker API (primary — same as Flow Designer UI)
|
|
398
621
|
try {
|
|
@@ -400,10 +623,50 @@ async function buildTriggerInputsForInsert(
|
|
|
400
623
|
params: { sysparm_transaction_scope: 'global' },
|
|
401
624
|
headers: { Accept: 'application/json' }
|
|
402
625
|
});
|
|
403
|
-
var
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
626
|
+
var tpRaw = tpResp.data;
|
|
627
|
+
var tpData = tpRaw?.result || tpRaw;
|
|
628
|
+
|
|
629
|
+
// Handle JSON object response
|
|
630
|
+
if (tpData && typeof tpData === 'object' && !Array.isArray(tpData)) {
|
|
631
|
+
// Try common field name variations
|
|
632
|
+
var foundInputs = Array.isArray(tpData.inputs) ? tpData.inputs
|
|
633
|
+
: Array.isArray(tpData.trigger_inputs) ? tpData.trigger_inputs
|
|
634
|
+
: Array.isArray(tpData.input) ? tpData.input : null;
|
|
635
|
+
var foundOutputs = Array.isArray(tpData.outputs) ? tpData.outputs
|
|
636
|
+
: Array.isArray(tpData.trigger_outputs) ? tpData.trigger_outputs
|
|
637
|
+
: Array.isArray(tpData.output) ? tpData.output : null;
|
|
638
|
+
if (foundInputs) { apiInputs = foundInputs; source = 'triggerpicker_json'; }
|
|
639
|
+
if (foundOutputs) apiOutputs = foundOutputs;
|
|
640
|
+
|
|
641
|
+
// If no arrays found, try to explore nested structure
|
|
642
|
+
if (!foundInputs && !foundOutputs) {
|
|
643
|
+
for (var key of Object.keys(tpData)) {
|
|
644
|
+
var val = tpData[key];
|
|
645
|
+
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
646
|
+
if (Array.isArray(val.inputs)) { apiInputs = val.inputs; source = 'triggerpicker_json.' + key; }
|
|
647
|
+
if (Array.isArray(val.outputs)) apiOutputs = val.outputs;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Handle XML string response
|
|
654
|
+
if (apiInputs.length === 0 && typeof tpData === 'string' && tpData.includes('<')) {
|
|
655
|
+
var xmlResult = parseTriggerpickerXml(tpData);
|
|
656
|
+
if (xmlResult.inputs.length > 0) {
|
|
657
|
+
apiInputs = xmlResult.inputs;
|
|
658
|
+
apiOutputs = xmlResult.outputs;
|
|
659
|
+
source = 'triggerpicker_xml';
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// Also check if the raw response itself is XML (not wrapped in result)
|
|
663
|
+
if (apiInputs.length === 0 && typeof tpRaw === 'string' && tpRaw.includes('<')) {
|
|
664
|
+
var xmlResult2 = parseTriggerpickerXml(tpRaw);
|
|
665
|
+
if (xmlResult2.inputs.length > 0) {
|
|
666
|
+
apiInputs = xmlResult2.inputs;
|
|
667
|
+
apiOutputs = xmlResult2.outputs;
|
|
668
|
+
source = 'triggerpicker_xml_raw';
|
|
669
|
+
}
|
|
407
670
|
}
|
|
408
671
|
} catch (tpErr: any) {
|
|
409
672
|
fetchError = 'triggerpicker: ' + (tpErr.message || 'unknown');
|
|
@@ -421,22 +684,25 @@ async function buildTriggerInputsForInsert(
|
|
|
421
684
|
}
|
|
422
685
|
});
|
|
423
686
|
var tableInputs = tiResp.data.result || [];
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
687
|
+
if (tableInputs.length > 0) {
|
|
688
|
+
apiInputs = tableInputs.map(function (rec: any) {
|
|
689
|
+
return {
|
|
690
|
+
id: str(rec.sys_id), name: str(rec.element), label: str(rec.label) || str(rec.element),
|
|
691
|
+
type: str(rec.internal_type) || 'string',
|
|
692
|
+
type_label: TYPE_LABELS[str(rec.internal_type) || 'string'] || str(rec.internal_type),
|
|
693
|
+
mandatory: str(rec.mandatory) === 'true',
|
|
694
|
+
order: parseInt(str(rec.order) || '0', 10),
|
|
695
|
+
maxsize: parseInt(str(rec.max_length) || '4000', 10),
|
|
696
|
+
hint: str(rec.hint), defaultValue: str(rec.default_value),
|
|
697
|
+
reference: str(rec.reference), reference_display: str(rec.reference_display),
|
|
698
|
+
use_dependent: str(rec.use_dependent_field) === 'true',
|
|
699
|
+
dependent_on: str(rec.dependent_on_field),
|
|
700
|
+
attributes: str(rec.attributes)
|
|
701
|
+
};
|
|
702
|
+
});
|
|
703
|
+
source = 'table_api';
|
|
704
|
+
fetchError = '';
|
|
705
|
+
}
|
|
440
706
|
} catch (tiErr: any) {
|
|
441
707
|
fetchError += '; table_api_inputs: ' + (tiErr.message || 'unknown');
|
|
442
708
|
}
|
|
@@ -452,127 +718,62 @@ async function buildTriggerInputsForInsert(
|
|
|
452
718
|
}
|
|
453
719
|
});
|
|
454
720
|
var tableOutputs = toResp.data.result || [];
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
721
|
+
if (tableOutputs.length > 0) {
|
|
722
|
+
apiOutputs = tableOutputs.map(function (rec: any) {
|
|
723
|
+
return {
|
|
724
|
+
id: str(rec.sys_id), name: str(rec.element), label: str(rec.label) || str(rec.element),
|
|
725
|
+
type: str(rec.internal_type) || 'string',
|
|
726
|
+
type_label: TYPE_LABELS[str(rec.internal_type) || 'string'] || str(rec.internal_type),
|
|
727
|
+
mandatory: str(rec.mandatory) === 'true',
|
|
728
|
+
order: parseInt(str(rec.order) || '0', 10),
|
|
729
|
+
maxsize: parseInt(str(rec.max_length) || '200', 10),
|
|
730
|
+
hint: str(rec.hint), reference: str(rec.reference), reference_display: str(rec.reference_display),
|
|
731
|
+
use_dependent: str(rec.use_dependent_field) === 'true',
|
|
732
|
+
dependent_on: str(rec.dependent_on_field),
|
|
733
|
+
attributes: str(rec.attributes)
|
|
734
|
+
};
|
|
735
|
+
});
|
|
736
|
+
}
|
|
469
737
|
} catch (_) {}
|
|
470
738
|
}
|
|
471
739
|
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
};
|
|
740
|
+
// Strategy 3: Hardcoded fallback for record-based triggers (ultimate safety net)
|
|
741
|
+
// Uses exact definitions captured from the Flow Designer UI
|
|
742
|
+
var isRecordTrigger = /record/.test(trigType.toLowerCase());
|
|
743
|
+
if (apiInputs.length === 0 && isRecordTrigger) {
|
|
744
|
+
apiInputs = getRecordTriggerFallbackInputs();
|
|
745
|
+
source = 'hardcoded_fallback';
|
|
746
|
+
}
|
|
747
|
+
if (apiOutputs.length === 0 && isRecordTrigger) {
|
|
748
|
+
apiOutputs = getRecordTriggerFallbackOutputs();
|
|
749
|
+
}
|
|
511
750
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
751
|
+
// Transform to GraphQL mutation format
|
|
752
|
+
var inputs = apiInputs.map(function (inp: any) { return buildTriggerInputObj(inp, userTable, userCondition); });
|
|
753
|
+
var outputs = apiOutputs.map(function (out: any) { return buildTriggerOutputObj(out); });
|
|
754
|
+
|
|
755
|
+
// Final safety net: ensure table and condition inputs are ALWAYS present for record triggers
|
|
756
|
+
if (isRecordTrigger) {
|
|
757
|
+
var hasTable = inputs.some(function (i: any) { return i.name === 'table'; });
|
|
758
|
+
var hasCondition = inputs.some(function (i: any) { return i.name === 'condition'; });
|
|
759
|
+
if (!hasTable) {
|
|
760
|
+
inputs.unshift(buildTriggerInputObj(
|
|
761
|
+
{ name: 'table', label: 'Table', type: 'table_name', type_label: 'Table Name', mandatory: true, order: 1, maxsize: 80, attributes: 'filter_table_source=RECORD_WATCHER_RESTRICTED,' },
|
|
762
|
+
userTable, userCondition
|
|
763
|
+
));
|
|
764
|
+
source += '+table_injected';
|
|
517
765
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
});
|
|
766
|
+
if (!hasCondition) {
|
|
767
|
+
var condIdx = inputs.findIndex(function (i: any) { return i.name === 'table'; });
|
|
768
|
+
inputs.splice(condIdx + 1, 0, buildTriggerInputObj(
|
|
769
|
+
{ name: 'condition', label: 'Condition', type: 'conditions', type_label: 'Conditions', mandatory: false, order: 100, maxsize: 4000, use_dependent: true, dependent_on: 'table', attributes: 'extended_operators=VALCHANGES;CHANGESFROM;CHANGESTO,wants_to_add_conditions=true,modelDependent=trigger_inputs,' },
|
|
770
|
+
userTable, userCondition
|
|
771
|
+
));
|
|
772
|
+
source += '+condition_injected';
|
|
566
773
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
return {
|
|
570
|
-
name: name, value: '', displayValue: '', type: paramType,
|
|
571
|
-
order: out.order || 0, label: label, children: children, parameter: parameter
|
|
572
|
-
};
|
|
573
|
-
});
|
|
774
|
+
}
|
|
574
775
|
|
|
575
|
-
return { inputs, outputs, error: fetchError || undefined };
|
|
776
|
+
return { inputs, outputs, source: source || 'none', error: fetchError || undefined };
|
|
576
777
|
}
|
|
577
778
|
|
|
578
779
|
async function addTriggerViaGraphQL(
|
|
@@ -643,8 +844,8 @@ async function addTriggerViaGraphQL(
|
|
|
643
844
|
if (!trigDefId) return { success: false, error: 'Trigger definition not found for: ' + triggerType, steps };
|
|
644
845
|
|
|
645
846
|
// 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 };
|
|
847
|
+
var triggerData = await buildTriggerInputsForInsert(client, trigDefId!, trigType, table, condition);
|
|
848
|
+
steps.trigger_data = { inputCount: triggerData.inputs.length, outputCount: triggerData.outputs.length, source: triggerData.source, error: triggerData.error };
|
|
648
849
|
|
|
649
850
|
const triggerResponseFields = 'triggerInstances { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
650
851
|
try {
|
|
@@ -839,6 +1040,252 @@ async function addActionViaGraphQL(
|
|
|
839
1040
|
}
|
|
840
1041
|
}
|
|
841
1042
|
|
|
1043
|
+
// ── DATA PILL CONDITION HELPERS ────────────────────────────────────────
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Get trigger info from a flow for constructing data pill references.
|
|
1047
|
+
* Reads the flow version payload to find the trigger name, table, and outputs.
|
|
1048
|
+
*
|
|
1049
|
+
* Returns the data pill base (e.g., "Created or Updated_1") and table (e.g., "incident").
|
|
1050
|
+
* The data pill base is used as: {{dataPillBase.fieldName}} in condition values.
|
|
1051
|
+
*/
|
|
1052
|
+
async function getFlowTriggerInfo(
|
|
1053
|
+
client: any,
|
|
1054
|
+
flowId: string
|
|
1055
|
+
): Promise<{ dataPillBase: string; triggerName: string; table: string; tableLabel: string; tableRef: string; error?: string }> {
|
|
1056
|
+
var triggerName = '';
|
|
1057
|
+
var table = '';
|
|
1058
|
+
var tableLabel = '';
|
|
1059
|
+
|
|
1060
|
+
try {
|
|
1061
|
+
// Read the flow version payload which contains all flow elements
|
|
1062
|
+
var resp = await client.get('/api/now/table/sys_hub_flow_version', {
|
|
1063
|
+
params: {
|
|
1064
|
+
sysparm_query: 'flow=' + flowId + '^ORDERBYDESCsys_created_on',
|
|
1065
|
+
sysparm_fields: 'sys_id,payload',
|
|
1066
|
+
sysparm_limit: 1
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
var payload = resp.data.result?.[0]?.payload;
|
|
1070
|
+
if (payload) {
|
|
1071
|
+
var parsed = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
|
1072
|
+
// The payload contains triggerInstances array with trigger data
|
|
1073
|
+
var triggerInstances = parsed.triggerInstances || parsed.trigger_instances || [];
|
|
1074
|
+
if (!Array.isArray(triggerInstances)) {
|
|
1075
|
+
// Some payloads nest elements differently
|
|
1076
|
+
if (parsed.elements) {
|
|
1077
|
+
triggerInstances = (Array.isArray(parsed.elements) ? parsed.elements : []).filter(
|
|
1078
|
+
(e: any) => e.type === 'trigger' || e.elementType === 'trigger'
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
if (triggerInstances.length > 0) {
|
|
1083
|
+
var trigger = triggerInstances[0];
|
|
1084
|
+
triggerName = trigger.name || trigger.triggerName || '';
|
|
1085
|
+
// Look for table in trigger inputs
|
|
1086
|
+
if (trigger.inputs && Array.isArray(trigger.inputs)) {
|
|
1087
|
+
for (var ti = 0; ti < trigger.inputs.length; ti++) {
|
|
1088
|
+
if (trigger.inputs[ti].name === 'table') {
|
|
1089
|
+
table = trigger.inputs[ti].value?.value || trigger.inputs[ti].value || '';
|
|
1090
|
+
break;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
} catch (_) {}
|
|
1097
|
+
|
|
1098
|
+
// Look up table label for display in label cache
|
|
1099
|
+
if (table) {
|
|
1100
|
+
try {
|
|
1101
|
+
var labelResp = await client.get('/api/now/table/sys_db_object', {
|
|
1102
|
+
params: {
|
|
1103
|
+
sysparm_query: 'name=' + table,
|
|
1104
|
+
sysparm_fields: 'label',
|
|
1105
|
+
sysparm_display_value: 'true',
|
|
1106
|
+
sysparm_limit: 1
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
tableLabel = str(labelResp.data.result?.[0]?.label) || '';
|
|
1110
|
+
} catch (_) {}
|
|
1111
|
+
if (!tableLabel) {
|
|
1112
|
+
// Fallback: capitalize the table name
|
|
1113
|
+
tableLabel = table.charAt(0).toUpperCase() + table.slice(1).replace(/_/g, ' ');
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
if (!triggerName) {
|
|
1118
|
+
return { dataPillBase: '', triggerName: '', table: table, tableLabel: tableLabel, tableRef: table, error: 'Could not determine trigger name from flow version payload' };
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
var dataPillBase = triggerName + '_1.current';
|
|
1122
|
+
return { dataPillBase, triggerName, table, tableLabel, tableRef: table };
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Parse a ServiceNow encoded query into individual condition clauses.
|
|
1127
|
+
* Each clause has: prefix (^ or ^OR), field, operator, value.
|
|
1128
|
+
*
|
|
1129
|
+
* Example: "category=inquiry^priority!=1^ORshort_descriptionLIKEtest"
|
|
1130
|
+
* → [
|
|
1131
|
+
* { prefix: '', field: 'category', operator: '=', value: 'inquiry' },
|
|
1132
|
+
* { prefix: '^', field: 'priority', operator: '!=', value: '1' },
|
|
1133
|
+
* { prefix: '^OR', field: 'short_description', operator: 'LIKE', value: 'test' }
|
|
1134
|
+
* ]
|
|
1135
|
+
*/
|
|
1136
|
+
function parseEncodedQuery(query: string): { prefix: string; field: string; operator: string; value: string }[] {
|
|
1137
|
+
if (!query || query === '^EQ') return [];
|
|
1138
|
+
|
|
1139
|
+
// Remove trailing ^EQ
|
|
1140
|
+
var q = query.replace(/\^EQ$/, '');
|
|
1141
|
+
if (!q) return [];
|
|
1142
|
+
|
|
1143
|
+
// Split on ^OR and ^ while keeping the separators
|
|
1144
|
+
var clauses: { prefix: string; raw: string }[] = [];
|
|
1145
|
+
var parts = q.split(/(\^OR|\^NQ|\^)/);
|
|
1146
|
+
var currentPrefix = '';
|
|
1147
|
+
|
|
1148
|
+
for (var i = 0; i < parts.length; i++) {
|
|
1149
|
+
var part = parts[i];
|
|
1150
|
+
if (part === '^' || part === '^OR' || part === '^NQ') {
|
|
1151
|
+
currentPrefix = part;
|
|
1152
|
+
} else if (part.length > 0) {
|
|
1153
|
+
clauses.push({ prefix: currentPrefix, raw: part });
|
|
1154
|
+
currentPrefix = '';
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// Operators sorted by length descending to match longest first
|
|
1159
|
+
var operators = [
|
|
1160
|
+
'VALCHANGES', 'CHANGESFROM', 'CHANGESTO',
|
|
1161
|
+
'ISNOTEMPTY', 'ISEMPTY', 'EMPTYSTRING', 'ANYTHING',
|
|
1162
|
+
'NOT LIKE', 'NOT IN', 'NSAMEAS',
|
|
1163
|
+
'STARTSWITH', 'ENDSWITH', 'BETWEEN', 'INSTANCEOF',
|
|
1164
|
+
'DYNAMIC', 'SAMEAS',
|
|
1165
|
+
'LIKE', 'IN',
|
|
1166
|
+
'!=', '>=', '<=', '>', '<', '='
|
|
1167
|
+
];
|
|
1168
|
+
|
|
1169
|
+
var result: { prefix: string; field: string; operator: string; value: string }[] = [];
|
|
1170
|
+
for (var j = 0; j < clauses.length; j++) {
|
|
1171
|
+
var clause = clauses[j];
|
|
1172
|
+
var raw = clause.raw;
|
|
1173
|
+
var matched = false;
|
|
1174
|
+
|
|
1175
|
+
for (var k = 0; k < operators.length; k++) {
|
|
1176
|
+
var op = operators[k];
|
|
1177
|
+
var opIdx = raw.indexOf(op);
|
|
1178
|
+
if (opIdx > 0) {
|
|
1179
|
+
result.push({
|
|
1180
|
+
prefix: clause.prefix,
|
|
1181
|
+
field: raw.substring(0, opIdx),
|
|
1182
|
+
operator: op,
|
|
1183
|
+
value: raw.substring(opIdx + op.length)
|
|
1184
|
+
});
|
|
1185
|
+
matched = true;
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
if (!matched) {
|
|
1190
|
+
// Unrecognized format — keep as-is
|
|
1191
|
+
result.push({ prefix: clause.prefix, field: raw, operator: '', value: '' });
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
return result;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* Transform an encoded query condition value to use data pill references.
|
|
1200
|
+
* Replaces field names with {{dataPillBase.fieldName}} syntax.
|
|
1201
|
+
*
|
|
1202
|
+
* Example: "category=inquiry" → "{{Created or Updated_1.current.category}}=inquiry"
|
|
1203
|
+
*/
|
|
1204
|
+
function transformConditionToDataPills(conditionValue: string, dataPillBase: string): string {
|
|
1205
|
+
if (!conditionValue || !dataPillBase) return conditionValue;
|
|
1206
|
+
|
|
1207
|
+
var clauses = parseEncodedQuery(conditionValue);
|
|
1208
|
+
if (clauses.length === 0) return conditionValue;
|
|
1209
|
+
|
|
1210
|
+
var result = '';
|
|
1211
|
+
for (var i = 0; i < clauses.length; i++) {
|
|
1212
|
+
var clause = clauses[i];
|
|
1213
|
+
result += clause.prefix;
|
|
1214
|
+
result += '{{' + dataPillBase + '.' + clause.field + '}}';
|
|
1215
|
+
result += clause.operator;
|
|
1216
|
+
result += clause.value;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
return result;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* Build labelCache entries for data pills used in flow logic conditions.
|
|
1224
|
+
* Each unique data pill reference needs a labelCache entry so the Flow Designer UI
|
|
1225
|
+
* can display the data pill label correctly.
|
|
1226
|
+
*
|
|
1227
|
+
* The entry format matches the captured UI mutation format.
|
|
1228
|
+
*/
|
|
1229
|
+
function buildConditionLabelCache(
|
|
1230
|
+
conditionValue: string,
|
|
1231
|
+
dataPillBase: string,
|
|
1232
|
+
triggerName: string,
|
|
1233
|
+
table: string,
|
|
1234
|
+
tableLabel: string,
|
|
1235
|
+
logicUiId: string
|
|
1236
|
+
): any[] {
|
|
1237
|
+
if (!conditionValue || !dataPillBase) return [];
|
|
1238
|
+
|
|
1239
|
+
var clauses = parseEncodedQuery(conditionValue);
|
|
1240
|
+
if (clauses.length === 0) return [];
|
|
1241
|
+
|
|
1242
|
+
// Build a label cache entry for each unique field-level data pill
|
|
1243
|
+
var seen: Record<string, boolean> = {};
|
|
1244
|
+
var entries: any[] = [];
|
|
1245
|
+
|
|
1246
|
+
// Also register the record-level data pill (the base: TriggerName_1.current)
|
|
1247
|
+
// This is needed for the condition builder to understand the context
|
|
1248
|
+
if (!seen[dataPillBase]) {
|
|
1249
|
+
seen[dataPillBase] = true;
|
|
1250
|
+
entries.push({
|
|
1251
|
+
name: dataPillBase,
|
|
1252
|
+
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record',
|
|
1253
|
+
reference: table,
|
|
1254
|
+
reference_display: tableLabel,
|
|
1255
|
+
type: 'reference',
|
|
1256
|
+
base_type: 'reference',
|
|
1257
|
+
attributes: '',
|
|
1258
|
+
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1259
|
+
choices: {}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
for (var i = 0; i < clauses.length; i++) {
|
|
1264
|
+
var field = clauses[i].field;
|
|
1265
|
+
if (!field) continue;
|
|
1266
|
+
var pillName = dataPillBase + '.' + field;
|
|
1267
|
+
if (seen[pillName]) continue;
|
|
1268
|
+
seen[pillName] = true;
|
|
1269
|
+
|
|
1270
|
+
// Capitalize field name for label
|
|
1271
|
+
var fieldLabel = field.replace(/_/g, ' ').replace(/\b\w/g, function (c: string) { return c.toUpperCase(); });
|
|
1272
|
+
|
|
1273
|
+
entries.push({
|
|
1274
|
+
name: pillName,
|
|
1275
|
+
label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record\u27a1' + fieldLabel,
|
|
1276
|
+
reference: '',
|
|
1277
|
+
reference_display: '',
|
|
1278
|
+
type: 'string',
|
|
1279
|
+
base_type: 'string',
|
|
1280
|
+
attributes: '',
|
|
1281
|
+
usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
|
|
1282
|
+
choices: {}
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
return entries;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
842
1289
|
// ── FLOW LOGIC (If/Else, For Each, etc.) ─────────────────────────────
|
|
843
1290
|
|
|
844
1291
|
async function addFlowLogicViaGraphQL(
|
|
@@ -911,11 +1358,37 @@ async function addFlowLogicViaGraphQL(
|
|
|
911
1358
|
steps.resolved_inputs = inputResult.resolvedInputs;
|
|
912
1359
|
steps.input_query_stats = { defParamsFound: inputResult.defParamsCount, inputsBuilt: inputResult.inputs.length, error: inputResult.inputQueryError };
|
|
913
1360
|
|
|
1361
|
+
// ── Data pill transformation for condition inputs ───────────────────
|
|
1362
|
+
// Flow Designer conditions require data pill references ({{TriggerName_1.current.field}})
|
|
1363
|
+
// instead of bare field names. Transform encoded query conditions and build labelCache.
|
|
1364
|
+
const uuid = generateUUID();
|
|
1365
|
+
var labelCacheEntries: any[] = [];
|
|
1366
|
+
|
|
1367
|
+
var conditionInput = inputResult.inputs.find(function (inp: any) { return inp.name === 'condition'; });
|
|
1368
|
+
var rawCondition = conditionInput?.value?.value || '';
|
|
1369
|
+
if (rawCondition && rawCondition !== '^EQ') {
|
|
1370
|
+
var triggerInfo = await getFlowTriggerInfo(client, flowId);
|
|
1371
|
+
steps.trigger_info = { dataPillBase: triggerInfo.dataPillBase, triggerName: triggerInfo.triggerName, table: triggerInfo.table, tableLabel: triggerInfo.tableLabel, error: triggerInfo.error };
|
|
1372
|
+
|
|
1373
|
+
if (triggerInfo.dataPillBase) {
|
|
1374
|
+
// Transform condition value: "category=inquiry" → "{{Created or Updated_1.current.category}}=inquiry"
|
|
1375
|
+
var transformedCondition = transformConditionToDataPills(rawCondition, triggerInfo.dataPillBase);
|
|
1376
|
+
conditionInput.value = { schemaless: false, schemalessValue: '', value: transformedCondition };
|
|
1377
|
+
steps.condition_transform = { original: rawCondition, transformed: transformedCondition };
|
|
1378
|
+
|
|
1379
|
+
// Build labelCache entries for each data pill used in the condition
|
|
1380
|
+
labelCacheEntries = buildConditionLabelCache(
|
|
1381
|
+
rawCondition, triggerInfo.dataPillBase, triggerInfo.triggerName,
|
|
1382
|
+
triggerInfo.tableRef, triggerInfo.tableLabel, uuid
|
|
1383
|
+
);
|
|
1384
|
+
steps.label_cache = { count: labelCacheEntries.length, pills: labelCacheEntries.map(function (e: any) { return e.name; }) };
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
914
1388
|
// Calculate insertion order
|
|
915
1389
|
const resolvedOrder = await calculateInsertOrder(client, flowId, parentUiId, order);
|
|
916
1390
|
steps.insert_order = resolvedOrder;
|
|
917
1391
|
|
|
918
|
-
const uuid = generateUUID();
|
|
919
1392
|
const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
920
1393
|
' actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
|
|
921
1394
|
' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
@@ -946,6 +1419,11 @@ async function addFlowLogicViaGraphQL(
|
|
|
946
1419
|
}
|
|
947
1420
|
};
|
|
948
1421
|
|
|
1422
|
+
// Add labelCache entries for data pill references in conditions
|
|
1423
|
+
if (labelCacheEntries.length > 0) {
|
|
1424
|
+
flowPatch.labelCache = { insert: labelCacheEntries };
|
|
1425
|
+
}
|
|
1426
|
+
|
|
949
1427
|
// Add parent flow logic update signal (tells GraphQL the parent was modified)
|
|
950
1428
|
if (parentUiId) {
|
|
951
1429
|
flowPatch.flowLogics.update = [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }];
|