snow-flow 1.2.4 ā 1.3.0
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/dist/mcp/servicenow-deployment-mcp.js +378 -0
- package/dist/mcp/servicenow-flow-composer-mcp.js +22 -2
- package/dist/test-iphone-flow.js +189 -0
- package/dist/utils/flow-examples.js +720 -0
- package/dist/utils/flow-structure-builder.js +677 -0
- package/dist/utils/servicenow-client.js +156 -7
- package/dist/version.js +11 -1
- package/package.json +1 -1
- package/test-flow-fix.js +92 -0
|
@@ -2258,6 +2258,384 @@ Run snow_deployment_debug for basic session info or check the logs for more deta
|
|
|
2258
2258
|
}
|
|
2259
2259
|
return result;
|
|
2260
2260
|
}
|
|
2261
|
+
/**
|
|
2262
|
+
* Generate Update Set XML based on artifact type
|
|
2263
|
+
*/
|
|
2264
|
+
async generateUpdateSetXML(type, config, updateSetSession) {
|
|
2265
|
+
switch (type) {
|
|
2266
|
+
case 'widget':
|
|
2267
|
+
return this.generateWidgetUpdateSetXML(config);
|
|
2268
|
+
case 'flow':
|
|
2269
|
+
return this.generateFlowUpdateSetXML(config);
|
|
2270
|
+
case 'application':
|
|
2271
|
+
return this.generateApplicationUpdateSetXML(config);
|
|
2272
|
+
default:
|
|
2273
|
+
throw new Error(`Update Set XML generation not implemented for type: ${type}`);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
/**
|
|
2277
|
+
* Generate Update Set XML for Flow Designer flows
|
|
2278
|
+
*/
|
|
2279
|
+
generateFlowUpdateSetXML(args) {
|
|
2280
|
+
const timestamp = new Date().toISOString();
|
|
2281
|
+
const updateSetName = `Flow_${args.name}_${Date.now()}`;
|
|
2282
|
+
// Generate unique identifiers
|
|
2283
|
+
const updateSetId = this.generateGUID();
|
|
2284
|
+
const flowId = this.generateGUID();
|
|
2285
|
+
const triggerId = this.generateGUID();
|
|
2286
|
+
const updateXmlId = this.generateGUID();
|
|
2287
|
+
// Parse flow definition if it's a string
|
|
2288
|
+
let flowDefinition = args.flow_definition;
|
|
2289
|
+
if (typeof flowDefinition === 'string') {
|
|
2290
|
+
try {
|
|
2291
|
+
flowDefinition = JSON.parse(flowDefinition);
|
|
2292
|
+
}
|
|
2293
|
+
catch (e) {
|
|
2294
|
+
flowDefinition = { activities: [] };
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
// Ensure flow definition has proper structure
|
|
2298
|
+
if (!flowDefinition || typeof flowDefinition !== 'object') {
|
|
2299
|
+
flowDefinition = { activities: [] };
|
|
2300
|
+
}
|
|
2301
|
+
// Generate action instances and flow logic
|
|
2302
|
+
const { actionInstances, flowLogics, completeFlowDefinition } = this.generateFlowComponents(flowDefinition, flowId, triggerId, timestamp);
|
|
2303
|
+
// Calculate total update count
|
|
2304
|
+
const totalUpdates = 1 + actionInstances.length + flowLogics.length + 1; // flow + actions + logics + trigger
|
|
2305
|
+
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
2306
|
+
<unload unload_date="${timestamp}">
|
|
2307
|
+
<sys_update_set action="INSERT_OR_UPDATE">
|
|
2308
|
+
<application display_value="Global">global</application>
|
|
2309
|
+
<category>customer</category>
|
|
2310
|
+
<description>Auto-generated Update Set for Flow Designer: ${args.name}</description>
|
|
2311
|
+
<is_default>false</is_default>
|
|
2312
|
+
<name>${updateSetName}</name>
|
|
2313
|
+
<origin_sys_id/>
|
|
2314
|
+
<release_date/>
|
|
2315
|
+
<state>complete</state>
|
|
2316
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2317
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2318
|
+
<sys_id>${updateSetId}</sys_id>
|
|
2319
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2320
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2321
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2322
|
+
<update_count>${totalUpdates}</update_count>
|
|
2323
|
+
</sys_update_set>
|
|
2324
|
+
|
|
2325
|
+
<!-- Main Flow Record -->
|
|
2326
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
2327
|
+
<action>INSERT_OR_UPDATE</action>
|
|
2328
|
+
<application display_value="Global">global</application>
|
|
2329
|
+
<category>customer</category>
|
|
2330
|
+
<comments/>
|
|
2331
|
+
<name>sys_hub_flow_${flowId}</name>
|
|
2332
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
|
2333
|
+
<record_update table="sys_hub_flow">
|
|
2334
|
+
<sys_hub_flow action="INSERT_OR_UPDATE">
|
|
2335
|
+
<access>public</access>
|
|
2336
|
+
<acls/>
|
|
2337
|
+
<active>${args.active !== false ? 'true' : 'false'}</active>
|
|
2338
|
+
<annotation/>
|
|
2339
|
+
<callable_by_client_api>false</callable_by_client_api>
|
|
2340
|
+
<category>${args.category || 'automation'}</category>
|
|
2341
|
+
<checked_out_by/>
|
|
2342
|
+
<compiler_build/>
|
|
2343
|
+
<copied_from/>
|
|
2344
|
+
<copied_from_name/>
|
|
2345
|
+
<description>${args.description || ''}</description>
|
|
2346
|
+
<internal_name>global.${args.name}</internal_name>
|
|
2347
|
+
<label_cache>[{"name":"${args.name}","internal_name":"${args.name}","label":"${args.name}","description":"${args.description || ''}","language":"en","source":""}]</label_cache>
|
|
2348
|
+
<latest_snapshot/>
|
|
2349
|
+
<master_snapshot><![CDATA[${JSON.stringify(completeFlowDefinition)}]]></master_snapshot>
|
|
2350
|
+
<name>${args.name}</name>
|
|
2351
|
+
<natlang/>
|
|
2352
|
+
<outputs_cache>[]</outputs_cache>
|
|
2353
|
+
<remote_trigger_id/>
|
|
2354
|
+
<run_as>user</run_as>
|
|
2355
|
+
<run_as_tz/>
|
|
2356
|
+
<sc_callable>false</sc_callable>
|
|
2357
|
+
<show_draft_actions>false</show_draft_actions>
|
|
2358
|
+
<show_triggered_flows>false</show_triggered_flows>
|
|
2359
|
+
<status>published</status>
|
|
2360
|
+
<sys_class_name>sys_hub_flow</sys_class_name>
|
|
2361
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2362
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2363
|
+
<sys_domain>global</sys_domain>
|
|
2364
|
+
<sys_domain_path>/</sys_domain_path>
|
|
2365
|
+
<sys_id>${flowId}</sys_id>
|
|
2366
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2367
|
+
<sys_name>${args.name}</sys_name>
|
|
2368
|
+
<sys_overrides/>
|
|
2369
|
+
<sys_package display_value="Global" source="global">global</sys_package>
|
|
2370
|
+
<sys_policy/>
|
|
2371
|
+
<sys_scope display_value="Global">global</sys_scope>
|
|
2372
|
+
<sys_update_name>sys_hub_flow_${flowId}</sys_update_name>
|
|
2373
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2374
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2375
|
+
<type>${args.flow_type || 'flow'}</type>
|
|
2376
|
+
</sys_hub_flow>
|
|
2377
|
+
</record_update>]]></payload>
|
|
2378
|
+
<payload_hash>-1</payload_hash>
|
|
2379
|
+
<record_name>${args.name}</record_name>
|
|
2380
|
+
<reverted_from/>
|
|
2381
|
+
<source_table>sys_hub_flow</source_table>
|
|
2382
|
+
<state>current</state>
|
|
2383
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2384
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2385
|
+
<sys_id>${updateXmlId}</sys_id>
|
|
2386
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2387
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2388
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2389
|
+
<table>sys_hub_flow</table>
|
|
2390
|
+
<target_name>${args.name}</target_name>
|
|
2391
|
+
<type>Flow Designer</type>
|
|
2392
|
+
<update_domain>global</update_domain>
|
|
2393
|
+
<update_set display_value="${updateSetName}">${updateSetId}</update_set>
|
|
2394
|
+
<view/>
|
|
2395
|
+
</sys_update_xml>
|
|
2396
|
+
|
|
2397
|
+
<!-- Trigger Instance -->
|
|
2398
|
+
${this.generateTriggerInstanceXML(args, flowId, triggerId, timestamp, updateSetId, updateSetName)}
|
|
2399
|
+
|
|
2400
|
+
<!-- Action Instances -->
|
|
2401
|
+
${actionInstances.map(instance => instance.xml).join('\n\n ')}
|
|
2402
|
+
|
|
2403
|
+
<!-- Flow Logic (Connections) -->
|
|
2404
|
+
${flowLogics.map(logic => logic.xml).join('\n\n ')}
|
|
2405
|
+
</unload>`;
|
|
2406
|
+
return xmlContent;
|
|
2407
|
+
}
|
|
2408
|
+
/**
|
|
2409
|
+
* Generate flow components (actions, logic, complete definition)
|
|
2410
|
+
*/
|
|
2411
|
+
generateFlowComponents(flowDefinition, flowId, triggerId, timestamp) {
|
|
2412
|
+
const actionInstances = [];
|
|
2413
|
+
const flowLogics = [];
|
|
2414
|
+
// Process activities/actions from flow definition
|
|
2415
|
+
const activities = flowDefinition.activities || flowDefinition.steps || [];
|
|
2416
|
+
const actionIds = [];
|
|
2417
|
+
// Generate action instances
|
|
2418
|
+
activities.forEach((activity, index) => {
|
|
2419
|
+
const actionId = this.generateGUID();
|
|
2420
|
+
actionIds.push(actionId);
|
|
2421
|
+
const actionXml = this.generateActionInstanceXML(activity, actionId, flowId, index, timestamp);
|
|
2422
|
+
actionInstances.push({ id: actionId, xml: actionXml });
|
|
2423
|
+
});
|
|
2424
|
+
// Generate flow logic (connections between actions)
|
|
2425
|
+
for (let i = 0; i < actionIds.length; i++) {
|
|
2426
|
+
const logicId = this.generateGUID();
|
|
2427
|
+
let fromId = i === 0 ? triggerId : actionIds[i - 1];
|
|
2428
|
+
let toId = actionIds[i];
|
|
2429
|
+
const logicXml = this.generateFlowLogicXML(logicId, flowId, fromId, toId, i, timestamp);
|
|
2430
|
+
flowLogics.push({ id: logicId, xml: logicXml });
|
|
2431
|
+
}
|
|
2432
|
+
// Create complete flow definition with all components
|
|
2433
|
+
const completeFlowDefinition = {
|
|
2434
|
+
trigger: {
|
|
2435
|
+
type: flowDefinition.trigger_type || "manual",
|
|
2436
|
+
table: flowDefinition.table || "incident",
|
|
2437
|
+
condition: flowDefinition.condition || "",
|
|
2438
|
+
sys_id: triggerId
|
|
2439
|
+
},
|
|
2440
|
+
actions: activities.map((activity, index) => ({
|
|
2441
|
+
...activity,
|
|
2442
|
+
sys_id: actionIds[index],
|
|
2443
|
+
sequence: index + 1
|
|
2444
|
+
})),
|
|
2445
|
+
logic: flowLogics.map(logic => ({
|
|
2446
|
+
sys_id: logic.id,
|
|
2447
|
+
from: logic.id === flowLogics[0]?.id ? triggerId : actionIds[flowLogics.indexOf(logic) - 1],
|
|
2448
|
+
to: actionIds[flowLogics.indexOf(logic)]
|
|
2449
|
+
})),
|
|
2450
|
+
inputs: flowDefinition.inputs || [],
|
|
2451
|
+
outputs: flowDefinition.outputs || [],
|
|
2452
|
+
steps: activities
|
|
2453
|
+
};
|
|
2454
|
+
return { actionInstances, flowLogics, completeFlowDefinition };
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Generate trigger instance XML
|
|
2458
|
+
*/
|
|
2459
|
+
generateTriggerInstanceXML(args, flowId, triggerId, timestamp, updateSetId, updateSetName) {
|
|
2460
|
+
const updateXmlId = this.generateGUID();
|
|
2461
|
+
return `<sys_update_xml action="INSERT_OR_UPDATE">
|
|
2462
|
+
<action>INSERT_OR_UPDATE</action>
|
|
2463
|
+
<application display_value="Global">global</application>
|
|
2464
|
+
<category>customer</category>
|
|
2465
|
+
<comments/>
|
|
2466
|
+
<name>sys_hub_trigger_instance_${triggerId}</name>
|
|
2467
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
|
2468
|
+
<record_update table="sys_hub_trigger_instance">
|
|
2469
|
+
<sys_hub_trigger_instance action="INSERT_OR_UPDATE">
|
|
2470
|
+
<active>true</active>
|
|
2471
|
+
<condition>${args.condition || ''}</condition>
|
|
2472
|
+
<dynamic_ref_qual>false</dynamic_ref_qual>
|
|
2473
|
+
<flow display_value="${args.name}">${flowId}</flow>
|
|
2474
|
+
<order>100</order>
|
|
2475
|
+
<sys_class_name>sys_hub_trigger_instance</sys_class_name>
|
|
2476
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2477
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2478
|
+
<sys_domain>global</sys_domain>
|
|
2479
|
+
<sys_domain_path>/</sys_domain_path>
|
|
2480
|
+
<sys_id>${triggerId}</sys_id>
|
|
2481
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2482
|
+
<sys_name>${args.name} Trigger</sys_name>
|
|
2483
|
+
<sys_overrides/>
|
|
2484
|
+
<sys_package display_value="Global" source="global">global</sys_package>
|
|
2485
|
+
<sys_policy/>
|
|
2486
|
+
<sys_scope display_value="Global">global</sys_scope>
|
|
2487
|
+
<sys_update_name>sys_hub_trigger_instance_${triggerId}</sys_update_name>
|
|
2488
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2489
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2490
|
+
<table>${args.table || 'incident'}</table>
|
|
2491
|
+
<trigger_type>${args.trigger_type || 'manual'}</trigger_type>
|
|
2492
|
+
</sys_hub_trigger_instance>
|
|
2493
|
+
</record_update>]]></payload>
|
|
2494
|
+
<payload_hash>-1</payload_hash>
|
|
2495
|
+
<record_name>${args.name} Trigger</record_name>
|
|
2496
|
+
<reverted_from/>
|
|
2497
|
+
<source_table>sys_hub_trigger_instance</source_table>
|
|
2498
|
+
<state>current</state>
|
|
2499
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2500
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2501
|
+
<sys_id>${updateXmlId}</sys_id>
|
|
2502
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2503
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2504
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2505
|
+
<table>sys_hub_trigger_instance</table>
|
|
2506
|
+
<target_name>${args.name} Trigger</target_name>
|
|
2507
|
+
<type>Flow Designer</type>
|
|
2508
|
+
<update_domain>global</update_domain>
|
|
2509
|
+
<update_set display_value="${updateSetName}">${updateSetId}</update_set>
|
|
2510
|
+
<view/>
|
|
2511
|
+
</sys_update_xml>`;
|
|
2512
|
+
}
|
|
2513
|
+
/**
|
|
2514
|
+
* Generate action instance XML
|
|
2515
|
+
*/
|
|
2516
|
+
generateActionInstanceXML(activity, actionId, flowId, sequence, timestamp) {
|
|
2517
|
+
const updateXmlId = this.generateGUID();
|
|
2518
|
+
return `<sys_update_xml action="INSERT_OR_UPDATE">
|
|
2519
|
+
<action>INSERT_OR_UPDATE</action>
|
|
2520
|
+
<application display_value="Global">global</application>
|
|
2521
|
+
<category>customer</category>
|
|
2522
|
+
<comments/>
|
|
2523
|
+
<name>sys_hub_action_instance_${actionId}</name>
|
|
2524
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
|
2525
|
+
<record_update table="sys_hub_action_instance">
|
|
2526
|
+
<sys_hub_action_instance action="INSERT_OR_UPDATE">
|
|
2527
|
+
<action>${activity.action || activity.type || 'script'}</action>
|
|
2528
|
+
<action_name>${activity.name || `Step ${sequence + 1}`}</action_name>
|
|
2529
|
+
<active>true</active>
|
|
2530
|
+
<anchor_x>${activity.x || (200 + sequence * 150)}</anchor_x>
|
|
2531
|
+
<anchor_y>${activity.y || 200}</anchor_y>
|
|
2532
|
+
<flow display_value="Flow">${flowId}</flow>
|
|
2533
|
+
<inputs>${JSON.stringify(activity.inputs || {})}</inputs>
|
|
2534
|
+
<order>${(sequence + 1) * 100}</order>
|
|
2535
|
+
<outputs>${JSON.stringify(activity.outputs || {})}</outputs>
|
|
2536
|
+
<script>${activity.script || activity.code || ''}</script>
|
|
2537
|
+
<sys_class_name>sys_hub_action_instance</sys_class_name>
|
|
2538
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2539
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2540
|
+
<sys_domain>global</sys_domain>
|
|
2541
|
+
<sys_domain_path>/</sys_domain_path>
|
|
2542
|
+
<sys_id>${actionId}</sys_id>
|
|
2543
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2544
|
+
<sys_name>${activity.name || `Step ${sequence + 1}`}</sys_name>
|
|
2545
|
+
<sys_overrides/>
|
|
2546
|
+
<sys_package display_value="Global" source="global">global</sys_package>
|
|
2547
|
+
<sys_policy/>
|
|
2548
|
+
<sys_scope display_value="Global">global</sys_scope>
|
|
2549
|
+
<sys_update_name>sys_hub_action_instance_${actionId}</sys_update_name>
|
|
2550
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2551
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2552
|
+
</sys_hub_action_instance>
|
|
2553
|
+
</record_update>]]></payload>
|
|
2554
|
+
<payload_hash>-1</payload_hash>
|
|
2555
|
+
<record_name>${activity.name || `Step ${sequence + 1}`}</record_name>
|
|
2556
|
+
<reverted_from/>
|
|
2557
|
+
<source_table>sys_hub_action_instance</source_table>
|
|
2558
|
+
<state>current</state>
|
|
2559
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2560
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2561
|
+
<sys_id>${updateXmlId}</sys_id>
|
|
2562
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2563
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2564
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2565
|
+
<table>sys_hub_action_instance</table>
|
|
2566
|
+
<target_name>${activity.name || `Step ${sequence + 1}`}</target_name>
|
|
2567
|
+
<type>Flow Designer</type>
|
|
2568
|
+
<update_domain>global</update_domain>
|
|
2569
|
+
<update_set display_value="Flow Update Set">${flowId.substring(0, 8)}</update_set>
|
|
2570
|
+
<view/>
|
|
2571
|
+
</sys_update_xml>`;
|
|
2572
|
+
}
|
|
2573
|
+
/**
|
|
2574
|
+
* Generate flow logic XML (connections between actions)
|
|
2575
|
+
*/
|
|
2576
|
+
generateFlowLogicXML(logicId, flowId, fromId, toId, sequence, timestamp) {
|
|
2577
|
+
const updateXmlId = this.generateGUID();
|
|
2578
|
+
return `<sys_update_xml action="INSERT_OR_UPDATE">
|
|
2579
|
+
<action>INSERT_OR_UPDATE</action>
|
|
2580
|
+
<application display_value="Global">global</application>
|
|
2581
|
+
<category>customer</category>
|
|
2582
|
+
<comments/>
|
|
2583
|
+
<name>sys_hub_flow_logic_${logicId}</name>
|
|
2584
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
|
2585
|
+
<record_update table="sys_hub_flow_logic">
|
|
2586
|
+
<sys_hub_flow_logic action="INSERT_OR_UPDATE">
|
|
2587
|
+
<condition>true</condition>
|
|
2588
|
+
<flow display_value="Flow">${flowId}</flow>
|
|
2589
|
+
<from_step>${fromId}</from_step>
|
|
2590
|
+
<order>${(sequence + 1) * 10}</order>
|
|
2591
|
+
<relationship_type>success</relationship_type>
|
|
2592
|
+
<sys_class_name>sys_hub_flow_logic</sys_class_name>
|
|
2593
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2594
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2595
|
+
<sys_domain>global</sys_domain>
|
|
2596
|
+
<sys_domain_path>/</sys_domain_path>
|
|
2597
|
+
<sys_id>${logicId}</sys_id>
|
|
2598
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2599
|
+
<sys_name>Connection ${sequence + 1}</sys_name>
|
|
2600
|
+
<sys_overrides/>
|
|
2601
|
+
<sys_package display_value="Global" source="global">global</sys_package>
|
|
2602
|
+
<sys_policy/>
|
|
2603
|
+
<sys_scope display_value="Global">global</sys_scope>
|
|
2604
|
+
<sys_update_name>sys_hub_flow_logic_${logicId}</sys_update_name>
|
|
2605
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2606
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2607
|
+
<to_step>${toId}</to_step>
|
|
2608
|
+
</sys_hub_flow_logic>
|
|
2609
|
+
</record_update>]]></payload>
|
|
2610
|
+
<payload_hash>-1</payload_hash>
|
|
2611
|
+
<record_name>Connection ${sequence + 1}</record_name>
|
|
2612
|
+
<reverted_from/>
|
|
2613
|
+
<source_table>sys_hub_flow_logic</source_table>
|
|
2614
|
+
<state>current</state>
|
|
2615
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2616
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2617
|
+
<sys_id>${updateXmlId}</sys_id>
|
|
2618
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2619
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2620
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2621
|
+
<table>sys_hub_flow_logic</table>
|
|
2622
|
+
<target_name>Connection ${sequence + 1}</target_name>
|
|
2623
|
+
<type>Flow Designer</type>
|
|
2624
|
+
<update_domain>global</update_domain>
|
|
2625
|
+
<update_set display_value="Flow Update Set">${flowId.substring(0, 8)}</update_set>
|
|
2626
|
+
<view/>
|
|
2627
|
+
</sys_update_xml>`;
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Generate Update Set XML for applications (stub for now)
|
|
2631
|
+
*/
|
|
2632
|
+
generateApplicationUpdateSetXML(args) {
|
|
2633
|
+
// TODO: Implement application XML generation
|
|
2634
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2635
|
+
<unload>
|
|
2636
|
+
<!-- Application Update Set XML not yet implemented -->
|
|
2637
|
+
</unload>`;
|
|
2638
|
+
}
|
|
2261
2639
|
/**
|
|
2262
2640
|
* Preview widget with test data
|
|
2263
2641
|
*/
|
|
@@ -11,6 +11,7 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
|
11
11
|
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
12
12
|
const snow_oauth_js_1 = require("../utils/snow-oauth.js");
|
|
13
13
|
const logger_js_1 = require("../utils/logger.js");
|
|
14
|
+
const flow_structure_builder_js_1 = require("../utils/flow-structure-builder.js");
|
|
14
15
|
class ServiceNowFlowComposerMCP {
|
|
15
16
|
constructor() {
|
|
16
17
|
this.server = new index_js_1.Server({
|
|
@@ -300,8 +301,27 @@ class ServiceNowFlowComposerMCP {
|
|
|
300
301
|
let deploymentResult = null;
|
|
301
302
|
if (args.deploy_immediately !== false) {
|
|
302
303
|
console.log('š DEPLOYING intelligent flow to ServiceNow...');
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
// Use the conversion utility to ensure proper format
|
|
305
|
+
const enhancedFlowDefinition = (0, flow_structure_builder_js_1.convertToFlowDefinition)({
|
|
306
|
+
name: parsedIntent.flowName,
|
|
307
|
+
description: parsedIntent.description,
|
|
308
|
+
table: parsedIntent.table,
|
|
309
|
+
trigger: parsedIntent.trigger,
|
|
310
|
+
activities: flowDefinition.activities || [],
|
|
311
|
+
variables: flowDefinition.variables || [],
|
|
312
|
+
connections: flowDefinition.connections || [],
|
|
313
|
+
error_handling: flowDefinition.error_handling || []
|
|
314
|
+
});
|
|
315
|
+
// Try enhanced method first, fallback to original if needed
|
|
316
|
+
try {
|
|
317
|
+
deploymentResult = await this.client.createFlowWithStructureBuilder(enhancedFlowDefinition);
|
|
318
|
+
console.log('š Enhanced deployment result:', deploymentResult);
|
|
319
|
+
}
|
|
320
|
+
catch (enhancedError) {
|
|
321
|
+
console.warn('ā ļø Enhanced deployment failed, falling back to original method:', enhancedError);
|
|
322
|
+
deploymentResult = await this.client.createFlow(flowDefinition);
|
|
323
|
+
console.log('š Fallback deployment result:', deploymentResult);
|
|
324
|
+
}
|
|
305
325
|
}
|
|
306
326
|
const credentials = await this.oauth.loadCredentials();
|
|
307
327
|
const flowUrl = `https://${credentials?.instance}/flow-designer/flow/${parsedIntent.flowName}`;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Test script for iPhone Request Approval Flow
|
|
5
|
+
* Demonstrates the complete integrated solution with all fixes
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.testIPhoneRequestApprovalFlow = testIPhoneRequestApprovalFlow;
|
|
9
|
+
const servicenow_client_1 = require("./utils/servicenow-client");
|
|
10
|
+
const flow_structure_builder_1 = require("./utils/flow-structure-builder");
|
|
11
|
+
async function testIPhoneRequestApprovalFlow() {
|
|
12
|
+
console.log('š§Ŗ Testing iPhone Request Approval Flow Integration');
|
|
13
|
+
console.log('================================================');
|
|
14
|
+
try {
|
|
15
|
+
// Initialize ServiceNow client
|
|
16
|
+
const client = new servicenow_client_1.ServiceNowClient();
|
|
17
|
+
// Define the iPhone Request Approval flow
|
|
18
|
+
const iPhoneFlowDefinition = {
|
|
19
|
+
name: 'iPhone Request Approval',
|
|
20
|
+
description: 'Approval workflow for iPhone equipment requests with admin approval and requester notification',
|
|
21
|
+
table: 'sc_request',
|
|
22
|
+
trigger: {
|
|
23
|
+
type: 'record_created',
|
|
24
|
+
table: 'sc_request',
|
|
25
|
+
condition: 'request_itemCONTAINSiPhone^ORcatalogue_itemLIKEiPhone'
|
|
26
|
+
},
|
|
27
|
+
activities: [
|
|
28
|
+
{
|
|
29
|
+
id: 'approval_admin',
|
|
30
|
+
name: 'Admin Approval',
|
|
31
|
+
type: 'approval',
|
|
32
|
+
inputs: {
|
|
33
|
+
approver: 'admin',
|
|
34
|
+
approval_question: 'Approve iPhone request for ${trigger.requested_for.name}?',
|
|
35
|
+
instructions: 'Please review the iPhone request and approve or reject based on company policy.',
|
|
36
|
+
timeout: '7 days'
|
|
37
|
+
},
|
|
38
|
+
outputs: {
|
|
39
|
+
approval_decision: 'string',
|
|
40
|
+
approved_by: 'string',
|
|
41
|
+
approval_comments: 'string'
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'notify_requester',
|
|
46
|
+
name: 'Notify Requester',
|
|
47
|
+
type: 'notification',
|
|
48
|
+
inputs: {
|
|
49
|
+
recipient: '${trigger.requested_for.email}',
|
|
50
|
+
subject: 'iPhone Request ${approval_admin.approval_decision}',
|
|
51
|
+
message: `Dear ${trigger.requested_for.name},
|
|
52
|
+
|
|
53
|
+
Your iPhone request has been ${approval_admin.approval_decision}.
|
|
54
|
+
|
|
55
|
+
Request Details:
|
|
56
|
+
- Item: ${trigger.request_item.short_description}
|
|
57
|
+
- Requested: ${trigger.sys_created_on}
|
|
58
|
+
- Decision by: ${approval_admin.approved_by}
|
|
59
|
+
- Comments: ${approval_admin.approval_comments}
|
|
60
|
+
|
|
61
|
+
${approval_admin.approval_decision === 'approved' ?
|
|
62
|
+
'Your iPhone will be processed and delivered according to standard procedures.' :
|
|
63
|
+
'Please contact your manager if you have questions about this decision.'}
|
|
64
|
+
|
|
65
|
+
Best regards,
|
|
66
|
+
IT Support Team`
|
|
67
|
+
},
|
|
68
|
+
condition: 'approval_admin.approval_decision != null'
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
variables: [
|
|
72
|
+
{
|
|
73
|
+
name: 'approval_decision',
|
|
74
|
+
label: 'Approval Decision',
|
|
75
|
+
type: 'string',
|
|
76
|
+
required: false,
|
|
77
|
+
default: ''
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'requester_email',
|
|
81
|
+
label: 'Requester Email',
|
|
82
|
+
type: 'string',
|
|
83
|
+
required: true,
|
|
84
|
+
default: '${trigger.requested_for.email}'
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
connections: [
|
|
88
|
+
{
|
|
89
|
+
from: 'trigger',
|
|
90
|
+
to: 'approval_admin',
|
|
91
|
+
condition: ''
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
from: 'approval_admin',
|
|
95
|
+
to: 'notify_requester',
|
|
96
|
+
condition: 'approval_admin.approval_decision != null'
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
error_handling: []
|
|
100
|
+
};
|
|
101
|
+
console.log('ā
Step 1: Flow definition created');
|
|
102
|
+
console.log(` - Name: ${iPhoneFlowDefinition.name}`);
|
|
103
|
+
console.log(` - Trigger: ${iPhoneFlowDefinition.trigger.type} on ${iPhoneFlowDefinition.trigger.table}`);
|
|
104
|
+
console.log(` - Activities: ${iPhoneFlowDefinition.activities.length}`);
|
|
105
|
+
console.log(` - Variables: ${iPhoneFlowDefinition.variables.length}`);
|
|
106
|
+
// Test 1: Create complete flow components using enhanced utilities
|
|
107
|
+
console.log('\nš§ Step 2: Creating complete flow components...');
|
|
108
|
+
const flowComponents = (0, flow_structure_builder_1.createCompleteFlowComponents)(iPhoneFlowDefinition);
|
|
109
|
+
console.log('ā
Flow components generated:');
|
|
110
|
+
console.log(` - Flow Record: ${flowComponents.flowRecord.sys_id}`);
|
|
111
|
+
console.log(` - Trigger Instance: ${flowComponents.triggerInstance.sys_id}`);
|
|
112
|
+
console.log(` - Action Instances: ${flowComponents.actionInstances.length}`);
|
|
113
|
+
console.log(` - Logic Chain: ${flowComponents.logicChain.length}`);
|
|
114
|
+
console.log(` - Variables: ${flowComponents.variables.length}`);
|
|
115
|
+
// Test 2: Generate enhanced XML
|
|
116
|
+
console.log('\nš Step 3: Generating enhanced XML...');
|
|
117
|
+
const flowXML = (0, flow_structure_builder_1.generateFlowXML)(flowComponents, {
|
|
118
|
+
includeMetadata: true,
|
|
119
|
+
validateBeforeExport: true,
|
|
120
|
+
compactFormat: false
|
|
121
|
+
});
|
|
122
|
+
console.log('ā
XML generated successfully');
|
|
123
|
+
console.log(` - Size: ${flowXML.length} characters`);
|
|
124
|
+
console.log(` - Contains flow record: ${flowXML.includes('<sys_hub_flow>')}`);
|
|
125
|
+
console.log(` - Contains trigger: ${flowXML.includes('<sys_hub_trigger_instance>')}`);
|
|
126
|
+
console.log(` - Contains actions: ${flowXML.includes('<sys_hub_action_instance>')}`);
|
|
127
|
+
// Test 3: Deploy to ServiceNow using integrated client
|
|
128
|
+
console.log('\nš Step 4: Deploying to ServiceNow...');
|
|
129
|
+
const flowData = {
|
|
130
|
+
name: iPhoneFlowDefinition.name,
|
|
131
|
+
description: iPhoneFlowDefinition.description,
|
|
132
|
+
trigger_type: iPhoneFlowDefinition.trigger.type,
|
|
133
|
+
table: iPhoneFlowDefinition.trigger.table,
|
|
134
|
+
condition: iPhoneFlowDefinition.trigger.condition,
|
|
135
|
+
activities: iPhoneFlowDefinition.activities,
|
|
136
|
+
variables: iPhoneFlowDefinition.variables
|
|
137
|
+
};
|
|
138
|
+
const deploymentResult = await client.createFlow(flowData);
|
|
139
|
+
if (deploymentResult.success) {
|
|
140
|
+
console.log('ā
Flow deployed successfully!');
|
|
141
|
+
console.log(` - Flow ID: ${deploymentResult.data.sys_id}`);
|
|
142
|
+
console.log(` - Flow Name: ${deploymentResult.data.name}`);
|
|
143
|
+
console.log(` - Status: ${deploymentResult.data.status || 'published'}`);
|
|
144
|
+
console.log(` - Activities Created: ${deploymentResult.data.activities_created || 'N/A'}`);
|
|
145
|
+
console.log(` - Flow Designer URL: ${deploymentResult.data.flow_designer_url || 'N/A'}`);
|
|
146
|
+
// Test 4: Verify deployment
|
|
147
|
+
console.log('\nš Step 5: Verifying deployment...');
|
|
148
|
+
const flowContent = await client.checkFlowContent(deploymentResult.data.sys_id);
|
|
149
|
+
if (flowContent.hasContent) {
|
|
150
|
+
console.log('ā
Flow verification successful!');
|
|
151
|
+
console.log(` - Has Content: ${flowContent.hasContent}`);
|
|
152
|
+
console.log(` - Activities Count: ${flowContent.details.activities_count}`);
|
|
153
|
+
console.log(` - Has Trigger: ${flowContent.details.has_trigger}`);
|
|
154
|
+
console.log(` - Active: ${flowContent.details.active}`);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
console.log('ā ļø Flow deployed but verification shows no content');
|
|
158
|
+
console.log(` - Details: ${flowContent.details}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.error('ā Flow deployment failed:', deploymentResult.error);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
console.log('\nš Integration test completed successfully!');
|
|
166
|
+
console.log('All components are working together properly:');
|
|
167
|
+
console.log('ā
Flow structure builder - generates complete components');
|
|
168
|
+
console.log('ā
Enhanced XML generation - produces valid update sets');
|
|
169
|
+
console.log('ā
ServiceNow client - creates flow with activities');
|
|
170
|
+
console.log('ā
Activity creation integration - flow and action instances');
|
|
171
|
+
console.log('ā
Verification system - confirms deployment success');
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
console.error('ā Integration test failed:', error);
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Run the test if this file is executed directly
|
|
180
|
+
if (require.main === module) {
|
|
181
|
+
testIPhoneRequestApprovalFlow()
|
|
182
|
+
.then(success => {
|
|
183
|
+
process.exit(success ? 0 : 1);
|
|
184
|
+
})
|
|
185
|
+
.catch(error => {
|
|
186
|
+
console.error('Fatal error:', error);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
});
|
|
189
|
+
}
|