snow-flow 10.0.1-dev.401 → 10.0.1-dev.403
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
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Create, list, get, update, activate, deactivate, delete and publish
|
|
5
5
|
* Flow Designer flows and subflows.
|
|
6
6
|
*
|
|
7
|
-
* For create/create_subflow: uses a
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Falls back to Table API if the
|
|
7
|
+
* For create/create_subflow: uses a Scheduled Job (sysauto_script) that
|
|
8
|
+
* runs GlideRecord server-side, ensuring sys_hub_flow_version records are
|
|
9
|
+
* created, flow_definition is set, and sn_fd engine registration is
|
|
10
|
+
* attempted. Falls back to Table API if the scheduled job fails.
|
|
11
11
|
*
|
|
12
12
|
* All other actions (list, get, update, activate, etc.) use the Table API.
|
|
13
13
|
*/
|
|
@@ -59,6 +59,13 @@ async function createFlowViaScheduledJob(
|
|
|
59
59
|
category: string;
|
|
60
60
|
runAs: string;
|
|
61
61
|
shouldActivate: boolean;
|
|
62
|
+
triggerType?: string;
|
|
63
|
+
triggerTable?: string;
|
|
64
|
+
triggerCondition?: string;
|
|
65
|
+
activities?: Array<{ name: string; type?: string; inputs?: any }>;
|
|
66
|
+
inputs?: Array<{ name: string; label?: string; type?: string; mandatory?: boolean; default_value?: string }>;
|
|
67
|
+
outputs?: Array<{ name: string; label?: string; type?: string }>;
|
|
68
|
+
flowDefinition?: any;
|
|
62
69
|
}
|
|
63
70
|
): Promise<{
|
|
64
71
|
success: boolean;
|
|
@@ -71,6 +78,7 @@ async function createFlowViaScheduledJob(
|
|
|
71
78
|
error?: string;
|
|
72
79
|
}> {
|
|
73
80
|
var resultPropName = 'snow_flow.factory_result.' + Date.now();
|
|
81
|
+
var flowDefStr = params.flowDefinition ? JSON.stringify(params.flowDefinition) : '';
|
|
74
82
|
|
|
75
83
|
// Build the server-side ES5 script
|
|
76
84
|
var script = [
|
|
@@ -85,9 +93,20 @@ async function createFlowViaScheduledJob(
|
|
|
85
93
|
" var flowCat = '" + escForScript(params.category) + "';",
|
|
86
94
|
" var runAs = '" + escForScript(params.runAs) + "';",
|
|
87
95
|
" var activate = " + (params.shouldActivate ? "true" : "false") + ";",
|
|
96
|
+
" var trigType = '" + escForScript(params.triggerType || 'manual') + "';",
|
|
97
|
+
" var trigTable = '" + escForScript(params.triggerTable || '') + "';",
|
|
98
|
+
" var trigCondition = '" + escForScript(params.triggerCondition || '') + "';",
|
|
99
|
+
" var flowDefStr = '" + escForScript(flowDefStr) + "';",
|
|
100
|
+
" var activitiesJson = '" + escForScript(JSON.stringify(params.activities || [])) + "';",
|
|
101
|
+
" var inputsJson = '" + escForScript(JSON.stringify(params.inputs || [])) + "';",
|
|
102
|
+
" var outputsJson = '" + escForScript(JSON.stringify(params.outputs || [])) + "';",
|
|
103
|
+
" var activities = []; try { activities = JSON.parse(activitiesJson); } catch(e) {}",
|
|
104
|
+
" var inputs = []; try { inputs = JSON.parse(inputsJson); } catch(e) {}",
|
|
105
|
+
" var outputs = []; try { outputs = JSON.parse(outputsJson); } catch(e) {}",
|
|
88
106
|
" var flowSysId = null;",
|
|
89
107
|
" var verSysId = null;",
|
|
90
108
|
"",
|
|
109
|
+
// ── TIER 1: sn_fd.FlowDesigner API ──
|
|
91
110
|
" try {",
|
|
92
111
|
" if (typeof sn_fd !== 'undefined' && sn_fd.FlowDesigner && typeof sn_fd.FlowDesigner.createFlow === 'function') {",
|
|
93
112
|
" var fdR = sn_fd.FlowDesigner.createFlow({ name: flowName, description: flowDesc, type: isSubflow ? 'subflow' : 'flow', category: flowCat, run_as: runAs });",
|
|
@@ -104,6 +123,7 @@ async function createFlowViaScheduledJob(
|
|
|
104
123
|
" }",
|
|
105
124
|
" } catch(t1e) { r.steps.tier1 = { success: false, error: t1e.getMessage ? t1e.getMessage() : t1e + '' }; }",
|
|
106
125
|
"",
|
|
126
|
+
// ── TIER 2: GlideRecord with flow_definition ──
|
|
107
127
|
" if (!flowSysId) {",
|
|
108
128
|
" try {",
|
|
109
129
|
" var f = new GlideRecord('sys_hub_flow');",
|
|
@@ -113,6 +133,7 @@ async function createFlowViaScheduledJob(
|
|
|
113
133
|
" f.setValue('run_as', runAs); f.setValue('active', false);",
|
|
114
134
|
" f.setValue('status', 'draft'); f.setValue('validated', true);",
|
|
115
135
|
" f.setValue('type', isSubflow ? 'subflow' : 'flow');",
|
|
136
|
+
" if (flowDefStr) { f.setValue('flow_definition', flowDefStr); f.setValue('latest_snapshot', flowDefStr); }",
|
|
116
137
|
" flowSysId = f.insert();",
|
|
117
138
|
" r.steps.flow_insert = { success: !!flowSysId, sys_id: flowSysId + '' };",
|
|
118
139
|
" if (flowSysId) {",
|
|
@@ -122,6 +143,7 @@ async function createFlowViaScheduledJob(
|
|
|
122
143
|
" v.setValue('version', '1.0'); v.setValue('state', 'draft');",
|
|
123
144
|
" v.setValue('active', true); v.setValue('compile_state', 'draft');",
|
|
124
145
|
" v.setValue('is_current', true);",
|
|
146
|
+
" if (flowDefStr) v.setValue('flow_definition', flowDefStr);",
|
|
125
147
|
" verSysId = v.insert();",
|
|
126
148
|
" r.steps.version_insert = { success: !!verSysId, sys_id: verSysId + '' };",
|
|
127
149
|
" if (verSysId) {",
|
|
@@ -141,8 +163,117 @@ async function createFlowViaScheduledJob(
|
|
|
141
163
|
" } catch(t2e) { r.steps.tier2 = { success: false, error: t2e.getMessage ? t2e.getMessage() : t2e + '' }; }",
|
|
142
164
|
" }",
|
|
143
165
|
"",
|
|
166
|
+
// ── Trigger, action, variable creation (runs for any tier) ──
|
|
167
|
+
" if (flowSysId) {",
|
|
168
|
+
// Trigger
|
|
169
|
+
" if (!isSubflow && trigType !== 'manual') {",
|
|
170
|
+
" try {",
|
|
171
|
+
" var triggerMap = { 'record_created': 'sn_fd.trigger.record_created', 'record_updated': 'sn_fd.trigger.record_updated', 'scheduled': 'sn_fd.trigger.scheduled' };",
|
|
172
|
+
" var trigIntName = triggerMap[trigType] || '';",
|
|
173
|
+
" if (trigIntName) {",
|
|
174
|
+
" var trigDef = new GlideRecord('sys_hub_action_type_definition');",
|
|
175
|
+
" trigDef.addQuery('internal_name', trigIntName); trigDef.query();",
|
|
176
|
+
" if (trigDef.next()) {",
|
|
177
|
+
" var trigInst = new GlideRecord('sys_hub_trigger_instance');",
|
|
178
|
+
" trigInst.initialize();",
|
|
179
|
+
" trigInst.setValue('flow', flowSysId); trigInst.setValue('action_type', trigDef.getUniqueValue());",
|
|
180
|
+
" trigInst.setValue('name', trigType); trigInst.setValue('order', 0); trigInst.setValue('active', true);",
|
|
181
|
+
" if (trigTable) trigInst.setValue('table', trigTable);",
|
|
182
|
+
" if (trigCondition) trigInst.setValue('condition', trigCondition);",
|
|
183
|
+
" var trigId = trigInst.insert();",
|
|
184
|
+
" r.steps.trigger = { success: !!trigId, sys_id: trigId + '' };",
|
|
185
|
+
" } else { r.steps.trigger = { success: false, error: 'Trigger def not found: ' + trigIntName }; }",
|
|
186
|
+
" }",
|
|
187
|
+
" } catch(te) { r.steps.trigger = { success: false, error: te.getMessage ? te.getMessage() : te + '' }; }",
|
|
188
|
+
" }",
|
|
189
|
+
// Actions
|
|
190
|
+
" var actionsCreated = 0;",
|
|
191
|
+
" for (var ai = 0; ai < activities.length; ai++) {",
|
|
192
|
+
" try {",
|
|
193
|
+
" var act = activities[ai];",
|
|
194
|
+
" var actDef = new GlideRecord('sys_hub_action_type_definition');",
|
|
195
|
+
" actDef.addQuery('internal_name', 'CONTAINS', act.type || 'script');",
|
|
196
|
+
" actDef.addOrCondition('name', 'CONTAINS', act.type || 'script'); actDef.query();",
|
|
197
|
+
" var actInst = new GlideRecord('sys_hub_action_instance');",
|
|
198
|
+
" actInst.initialize();",
|
|
199
|
+
" actInst.setValue('flow', flowSysId); actInst.setValue('name', act.name || 'Action ' + (ai + 1));",
|
|
200
|
+
" actInst.setValue('order', (ai + 1) * 100); actInst.setValue('active', true);",
|
|
201
|
+
" if (actDef.next()) actInst.setValue('action_type', actDef.getUniqueValue());",
|
|
202
|
+
" if (actInst.insert()) actionsCreated++;",
|
|
203
|
+
" } catch(ae) {}",
|
|
204
|
+
" }",
|
|
205
|
+
" r.steps.actions = { success: true, created: actionsCreated, requested: activities.length };",
|
|
206
|
+
// Variables (subflows)
|
|
207
|
+
" var varsCreated = 0;",
|
|
208
|
+
" if (isSubflow) {",
|
|
209
|
+
" for (var vi = 0; vi < inputs.length; vi++) {",
|
|
210
|
+
" try { var inp = inputs[vi]; var fv = new GlideRecord('sys_hub_flow_variable'); fv.initialize();",
|
|
211
|
+
" fv.setValue('flow', flowSysId); fv.setValue('name', inp.name); fv.setValue('label', inp.label || inp.name);",
|
|
212
|
+
" fv.setValue('type', inp.type || 'string'); fv.setValue('mandatory', inp.mandatory || false);",
|
|
213
|
+
" fv.setValue('default_value', inp.default_value || ''); fv.setValue('variable_type', 'input');",
|
|
214
|
+
" if (fv.insert()) varsCreated++;",
|
|
215
|
+
" } catch(ve) {}",
|
|
216
|
+
" }",
|
|
217
|
+
" for (var vo = 0; vo < outputs.length; vo++) {",
|
|
218
|
+
" try { var ot = outputs[vo]; var ov = new GlideRecord('sys_hub_flow_variable'); ov.initialize();",
|
|
219
|
+
" ov.setValue('flow', flowSysId); ov.setValue('name', ot.name); ov.setValue('label', ot.label || ot.name);",
|
|
220
|
+
" ov.setValue('type', ot.type || 'string'); ov.setValue('variable_type', 'output');",
|
|
221
|
+
" if (ov.insert()) varsCreated++;",
|
|
222
|
+
" } catch(ve) {}",
|
|
223
|
+
" }",
|
|
224
|
+
" }",
|
|
225
|
+
" r.steps.variables = { success: true, created: varsCreated };",
|
|
226
|
+
"",
|
|
227
|
+
// ── Engine registration (server-side sn_fd APIs) ──
|
|
228
|
+
" r.steps.engine = { apis_found: [] };",
|
|
229
|
+
" try {",
|
|
230
|
+
" if (typeof sn_fd !== 'undefined') {",
|
|
231
|
+
" r.steps.engine.sn_fd = 'available';",
|
|
232
|
+
" if (sn_fd.FlowDesigner) {",
|
|
233
|
+
" r.steps.engine.apis_found.push('FlowDesigner');",
|
|
234
|
+
" if (typeof sn_fd.FlowDesigner.publishFlow === 'function') {",
|
|
235
|
+
" try {",
|
|
236
|
+
" sn_fd.FlowDesigner.publishFlow(flowSysId);",
|
|
237
|
+
" r.steps.engine.publish = 'success';",
|
|
238
|
+
" } catch(pe) { r.steps.engine.publish = pe.getMessage ? pe.getMessage() : pe + ''; }",
|
|
239
|
+
" }",
|
|
240
|
+
" }",
|
|
241
|
+
" if (sn_fd.FlowCompiler) {",
|
|
242
|
+
" r.steps.engine.apis_found.push('FlowCompiler');",
|
|
243
|
+
" if (typeof sn_fd.FlowCompiler.compile === 'function') {",
|
|
244
|
+
" try {",
|
|
245
|
+
" sn_fd.FlowCompiler.compile(flowSysId);",
|
|
246
|
+
" r.steps.engine.compile = 'success';",
|
|
247
|
+
" } catch(ce) { r.steps.engine.compile = ce.getMessage ? ce.getMessage() : ce + ''; }",
|
|
248
|
+
" }",
|
|
249
|
+
" }",
|
|
250
|
+
" var otherApis = ['FlowAPI', 'FlowPublisher'];",
|
|
251
|
+
" for (var oa = 0; oa < otherApis.length; oa++) {",
|
|
252
|
+
" if (sn_fd[otherApis[oa]]) {",
|
|
253
|
+
" r.steps.engine.apis_found.push(otherApis[oa]);",
|
|
254
|
+
" var apiObj = sn_fd[otherApis[oa]];",
|
|
255
|
+
" if (typeof apiObj.publish === 'function') {",
|
|
256
|
+
" try { apiObj.publish(flowSysId); r.steps.engine[otherApis[oa] + '_publish'] = 'success'; }",
|
|
257
|
+
" catch(e) { r.steps.engine[otherApis[oa] + '_publish'] = e + ''; }",
|
|
258
|
+
" }",
|
|
259
|
+
" }",
|
|
260
|
+
" }",
|
|
261
|
+
" } else {",
|
|
262
|
+
" r.steps.engine.sn_fd = 'unavailable';",
|
|
263
|
+
" }",
|
|
264
|
+
" try {",
|
|
265
|
+
" if (typeof GlideFlowDesigner !== 'undefined') {",
|
|
266
|
+
" r.steps.engine.apis_found.push('GlideFlowDesigner');",
|
|
267
|
+
" }",
|
|
268
|
+
" } catch(e) {}",
|
|
269
|
+
" } catch(engineErr) {",
|
|
270
|
+
" r.steps.engine.error = engineErr.getMessage ? engineErr.getMessage() : engineErr + '';",
|
|
271
|
+
" }",
|
|
272
|
+
" }",
|
|
273
|
+
"",
|
|
144
274
|
" r.flow_sys_id = flowSysId ? flowSysId + '' : null;",
|
|
145
275
|
" r.version_sys_id = verSysId ? verSysId + '' : null;",
|
|
276
|
+
// ── Post-engine check: re-read latest_version ──
|
|
146
277
|
" if (flowSysId) {",
|
|
147
278
|
" var cf = new GlideRecord('sys_hub_flow');",
|
|
148
279
|
" if (cf.get(flowSysId)) {",
|
|
@@ -175,9 +306,10 @@ async function createFlowViaScheduledJob(
|
|
|
175
306
|
});
|
|
176
307
|
var jobSysId = jobResp.data.result?.sys_id;
|
|
177
308
|
|
|
178
|
-
// 3. Poll for result (
|
|
309
|
+
// 3. Poll for result (adaptive: 5×1s + 5×2s + 5×3s = 30s max)
|
|
179
310
|
for (var i = 0; i < 15; i++) {
|
|
180
|
-
|
|
311
|
+
var delay = i < 5 ? 1000 : i < 10 ? 2000 : 3000;
|
|
312
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
181
313
|
var propResp = await client.get('/api/now/table/sys_properties', {
|
|
182
314
|
params: {
|
|
183
315
|
sysparm_query: 'name=' + resultPropName,
|
|
@@ -1157,7 +1289,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1157
1289
|
delete flowDefinition.trigger;
|
|
1158
1290
|
}
|
|
1159
1291
|
|
|
1160
|
-
// ──
|
|
1292
|
+
// ── Pipeline: Scheduled Job (primary) → Table API (fallback) ──
|
|
1161
1293
|
var flowSysId: string | null = null;
|
|
1162
1294
|
var usedMethod = 'table_api';
|
|
1163
1295
|
var versionCreated = false;
|
|
@@ -1168,7 +1300,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1168
1300
|
|
|
1169
1301
|
// Diagnostics: track every step for debugging "flow cannot be found" issues
|
|
1170
1302
|
var diagnostics: any = {
|
|
1171
|
-
factory_bootstrap:
|
|
1303
|
+
factory_bootstrap: 'skipped (direct scheduled job)',
|
|
1172
1304
|
factory_namespace: null,
|
|
1173
1305
|
factory_call: null,
|
|
1174
1306
|
table_api_used: false,
|
|
@@ -1179,133 +1311,65 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1179
1311
|
post_verify: null
|
|
1180
1312
|
};
|
|
1181
1313
|
|
|
1314
|
+
// ── Scheduled Job (primary — server-side GlideRecord) ───────
|
|
1315
|
+
// This runs inside ServiceNow as a system job, so it CAN set
|
|
1316
|
+
// computed fields like latest_version that Table API cannot.
|
|
1317
|
+
// Also attempts sn_fd engine registration server-side.
|
|
1182
1318
|
try {
|
|
1183
|
-
var
|
|
1184
|
-
diagnostics.factory_bootstrap = 'success';
|
|
1185
|
-
diagnostics.factory_namespace = factory.namespace;
|
|
1186
|
-
|
|
1187
|
-
// Call /discover to learn what sn_fd APIs exist on this instance
|
|
1188
|
-
var discovery = await discoverInstanceCapabilities(client, factory.namespace);
|
|
1189
|
-
if (discovery) {
|
|
1190
|
-
diagnostics.instance_build = discovery.build_tag || discovery.build_name || 'unknown';
|
|
1191
|
-
diagnostics.available_apis = discovery.apis || {};
|
|
1192
|
-
diagnostics.available_methods = discovery.methods || {};
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
var factoryPayload = {
|
|
1319
|
+
var scheduledResult = await createFlowViaScheduledJob(client, {
|
|
1196
1320
|
name: flowName,
|
|
1197
1321
|
description: flowDescription,
|
|
1198
|
-
|
|
1322
|
+
internalName: sanitizeInternalName(flowName),
|
|
1323
|
+
isSubflow: isSubflow,
|
|
1199
1324
|
category: flowCategory,
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1325
|
+
runAs: flowRunAs,
|
|
1326
|
+
shouldActivate: shouldActivate,
|
|
1327
|
+
triggerType: triggerType,
|
|
1328
|
+
triggerTable: flowTable,
|
|
1329
|
+
triggerCondition: triggerCondition,
|
|
1205
1330
|
activities: activitiesArg.map(function (act: any, idx: number) {
|
|
1206
|
-
return { name: act.name, type: act.type || 'script', inputs: act.inputs || {}
|
|
1331
|
+
return { name: act.name, type: act.type || 'script', inputs: act.inputs || {} };
|
|
1207
1332
|
}),
|
|
1208
1333
|
inputs: inputsArg,
|
|
1209
1334
|
outputs: outputsArg,
|
|
1210
|
-
|
|
1335
|
+
flowDefinition: flowDefinition
|
|
1336
|
+
});
|
|
1337
|
+
diagnostics.scheduled_job = {
|
|
1338
|
+
success: scheduledResult.success,
|
|
1339
|
+
tierUsed: scheduledResult.tierUsed,
|
|
1340
|
+
latestVersionSet: scheduledResult.latestVersionSet,
|
|
1341
|
+
latestVersionValue: scheduledResult.latestVersionValue,
|
|
1342
|
+
steps: scheduledResult.steps,
|
|
1343
|
+
error: scheduledResult.error
|
|
1211
1344
|
};
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
try {
|
|
1217
|
-
factoryResp = await client.post(factoryEndpoint, factoryPayload);
|
|
1218
|
-
} catch (callError: any) {
|
|
1219
|
-
// 404 = API was deleted externally → invalidate cache, retry once
|
|
1220
|
-
if (callError.response?.status === 404) {
|
|
1221
|
-
invalidateFlowFactoryCache();
|
|
1222
|
-
var retryFactory = await ensureFlowFactoryAPI(client, context.instanceUrl);
|
|
1223
|
-
diagnostics.factory_namespace = retryFactory.namespace;
|
|
1224
|
-
var retryEndpoint = '/api/' + retryFactory.namespace + '/' + FLOW_FACTORY_API_ID + '/create';
|
|
1225
|
-
factoryResp = await client.post(retryEndpoint, factoryPayload);
|
|
1226
|
-
} else {
|
|
1227
|
-
throw callError;
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
var factoryResult = factoryResp.data?.result || factoryResp.data;
|
|
1232
|
-
if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
|
|
1233
|
-
var tierUsed = factoryResult.tier_used || 'unknown';
|
|
1234
|
-
diagnostics.factory_call = 'success';
|
|
1235
|
-
diagnostics.factory_tier = tierUsed;
|
|
1236
|
-
diagnostics.factory_steps = factoryResult.steps || {};
|
|
1237
|
-
flowSysId = factoryResult.flow_sys_id;
|
|
1238
|
-
usedMethod = 'scripted_rest_api';
|
|
1239
|
-
versionCreated = !!factoryResult.version_created;
|
|
1345
|
+
if (scheduledResult.success && scheduledResult.flowSysId) {
|
|
1346
|
+
flowSysId = scheduledResult.flowSysId;
|
|
1347
|
+
usedMethod = 'scheduled_job (' + (scheduledResult.tierUsed || 'unknown') + ')';
|
|
1348
|
+
versionCreated = !!scheduledResult.versionSysId;
|
|
1240
1349
|
diagnostics.version_created = versionCreated;
|
|
1241
|
-
diagnostics.version_method = '
|
|
1242
|
-
diagnostics.
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
if (steps.trigger) {
|
|
1247
|
-
triggerCreated = !!steps.trigger.success;
|
|
1248
|
-
if (!steps.trigger.success && steps.trigger.error) {
|
|
1249
|
-
factoryWarnings.push('Trigger: ' + steps.trigger.error);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
if (steps.actions) {
|
|
1253
|
-
actionsCreated = steps.actions.created || 0;
|
|
1350
|
+
diagnostics.version_method = 'scheduled_job';
|
|
1351
|
+
diagnostics.latest_version_auto_set = scheduledResult.latestVersionSet;
|
|
1352
|
+
// Extract engine registration results from scheduled job
|
|
1353
|
+
if (scheduledResult.steps?.engine) {
|
|
1354
|
+
diagnostics.engine_registration = scheduledResult.steps.engine;
|
|
1254
1355
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1356
|
+
// Extract trigger/action/variable results from scheduled job
|
|
1357
|
+
if (scheduledResult.steps?.trigger) {
|
|
1358
|
+
triggerCreated = !!scheduledResult.steps.trigger.success;
|
|
1359
|
+
if (!scheduledResult.steps.trigger.success && scheduledResult.steps.trigger.error) {
|
|
1360
|
+
factoryWarnings.push('Trigger: ' + scheduledResult.steps.trigger.error);
|
|
1361
|
+
}
|
|
1257
1362
|
}
|
|
1258
|
-
if (
|
|
1259
|
-
|
|
1363
|
+
if (scheduledResult.steps?.actions) {
|
|
1364
|
+
actionsCreated = scheduledResult.steps.actions.created || 0;
|
|
1260
1365
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
// Flow Factory unavailable — fall through to Table API
|
|
1264
|
-
var statusCode = factoryError.response?.status;
|
|
1265
|
-
var factoryErrMsg = statusCode
|
|
1266
|
-
? 'HTTP ' + statusCode + ': ' + (factoryError.response?.data?.error?.message || factoryError.message || 'unknown')
|
|
1267
|
-
: (factoryError.message || 'unknown');
|
|
1268
|
-
diagnostics.factory_bootstrap = diagnostics.factory_bootstrap || ('error: ' + factoryErrMsg);
|
|
1269
|
-
diagnostics.factory_call = diagnostics.factory_call || ('error: ' + factoryErrMsg);
|
|
1270
|
-
if (statusCode !== 403) {
|
|
1271
|
-
factoryWarnings.push('Flow Factory unavailable (' + factoryErrMsg + '), using Table API fallback');
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
// ── Scheduled Job fallback (server-side GlideRecord) ────────
|
|
1276
|
-
// This runs inside ServiceNow as a system job, so it CAN set
|
|
1277
|
-
// computed fields like latest_version that Table API cannot.
|
|
1278
|
-
if (!flowSysId) {
|
|
1279
|
-
try {
|
|
1280
|
-
var scheduledResult = await createFlowViaScheduledJob(client, {
|
|
1281
|
-
name: flowName,
|
|
1282
|
-
description: flowDescription,
|
|
1283
|
-
internalName: sanitizeInternalName(flowName),
|
|
1284
|
-
isSubflow: isSubflow,
|
|
1285
|
-
category: flowCategory,
|
|
1286
|
-
runAs: flowRunAs,
|
|
1287
|
-
shouldActivate: shouldActivate
|
|
1288
|
-
});
|
|
1289
|
-
diagnostics.scheduled_job = {
|
|
1290
|
-
success: scheduledResult.success,
|
|
1291
|
-
tierUsed: scheduledResult.tierUsed,
|
|
1292
|
-
latestVersionSet: scheduledResult.latestVersionSet,
|
|
1293
|
-
latestVersionValue: scheduledResult.latestVersionValue,
|
|
1294
|
-
steps: scheduledResult.steps,
|
|
1295
|
-
error: scheduledResult.error
|
|
1296
|
-
};
|
|
1297
|
-
if (scheduledResult.success && scheduledResult.flowSysId) {
|
|
1298
|
-
flowSysId = scheduledResult.flowSysId;
|
|
1299
|
-
usedMethod = 'scheduled_job (' + (scheduledResult.tierUsed || 'unknown') + ')';
|
|
1300
|
-
versionCreated = !!scheduledResult.versionSysId;
|
|
1301
|
-
diagnostics.version_created = versionCreated;
|
|
1302
|
-
diagnostics.version_method = 'scheduled_job';
|
|
1303
|
-
diagnostics.latest_version_auto_set = scheduledResult.latestVersionSet;
|
|
1366
|
+
if (scheduledResult.steps?.variables) {
|
|
1367
|
+
varsCreated = scheduledResult.steps.variables.created || 0;
|
|
1304
1368
|
}
|
|
1305
|
-
} catch (schedErr: any) {
|
|
1306
|
-
diagnostics.scheduled_job = { error: schedErr.message || 'unknown' };
|
|
1307
|
-
factoryWarnings.push('Scheduled job fallback failed: ' + (schedErr.message || schedErr));
|
|
1308
1369
|
}
|
|
1370
|
+
} catch (schedErr: any) {
|
|
1371
|
+
diagnostics.scheduled_job = { error: schedErr.message || 'unknown' };
|
|
1372
|
+
factoryWarnings.push('Scheduled job failed: ' + (schedErr.message || schedErr));
|
|
1309
1373
|
}
|
|
1310
1374
|
|
|
1311
1375
|
// ── Table API fallback (last resort) ─────────────────────────
|
|
@@ -1506,10 +1570,11 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1506
1570
|
|
|
1507
1571
|
}
|
|
1508
1572
|
|
|
1509
|
-
// ── Register flow with Flow Designer engine
|
|
1510
|
-
//
|
|
1511
|
-
//
|
|
1512
|
-
|
|
1573
|
+
// ── Register flow with Flow Designer engine (Table API only) ──
|
|
1574
|
+
// For scheduled job path, engine registration is done server-side
|
|
1575
|
+
// inside the job script (via sn_fd APIs). Only call the external
|
|
1576
|
+
// REST-based registration for the Table API fallback path.
|
|
1577
|
+
if (flowSysId && usedMethod.startsWith('table_api')) {
|
|
1513
1578
|
var engineResult = await registerFlowWithEngine(client, flowSysId, shouldActivate);
|
|
1514
1579
|
diagnostics.engine_registration = {
|
|
1515
1580
|
success: engineResult.success,
|
|
@@ -1624,29 +1689,38 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1624
1689
|
|
|
1625
1690
|
// Diagnostics section
|
|
1626
1691
|
createSummary.blank().line('Diagnostics:');
|
|
1627
|
-
if (diagnostics.
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
|
|
1632
|
-
createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
|
|
1633
|
-
if (diagnostics.factory_tier) {
|
|
1634
|
-
createSummary.indented('Factory tier used: ' + diagnostics.factory_tier);
|
|
1635
|
-
}
|
|
1636
|
-
if (diagnostics.available_apis && Object.keys(diagnostics.available_apis).length > 0) {
|
|
1637
|
-
var apiStr = Object.keys(diagnostics.available_apis).map(function (k: string) { return k + '=' + diagnostics.available_apis[k]; }).join(', ');
|
|
1638
|
-
createSummary.indented('Available APIs: ' + apiStr);
|
|
1692
|
+
if (diagnostics.scheduled_job) {
|
|
1693
|
+
var sj = diagnostics.scheduled_job;
|
|
1694
|
+
createSummary.indented('Scheduled job: ' + (sj.success ? 'success' : 'failed') + (sj.tierUsed ? ' (' + sj.tierUsed + ')' : ''));
|
|
1695
|
+
if (sj.error) createSummary.indented(' Error: ' + sj.error);
|
|
1639
1696
|
}
|
|
1640
1697
|
createSummary.indented('Table API used: ' + diagnostics.table_api_used);
|
|
1641
1698
|
createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
|
|
1642
1699
|
if (diagnostics.engine_registration) {
|
|
1643
|
-
|
|
1644
|
-
if (
|
|
1645
|
-
|
|
1646
|
-
|
|
1700
|
+
var eng = diagnostics.engine_registration;
|
|
1701
|
+
if (eng.sn_fd) {
|
|
1702
|
+
// Server-side engine registration (from scheduled job)
|
|
1703
|
+
var engineLabel = 'sn_fd=' + eng.sn_fd;
|
|
1704
|
+
if (eng.apis_found && eng.apis_found.length > 0) {
|
|
1705
|
+
engineLabel += ', APIs=[' + eng.apis_found.join(', ') + ']';
|
|
1706
|
+
}
|
|
1707
|
+
if (eng.publish) engineLabel += ', publishFlow=' + eng.publish;
|
|
1708
|
+
if (eng.compile) engineLabel += ', compile=' + eng.compile;
|
|
1709
|
+
if (eng.error) engineLabel += ', error=' + eng.error;
|
|
1710
|
+
createSummary.indented('Engine (server-side): ' + engineLabel);
|
|
1711
|
+
} else if (eng.success !== undefined) {
|
|
1712
|
+
// REST-based engine registration (Table API path)
|
|
1713
|
+
createSummary.indented('Engine registration: ' + (eng.success ? eng.method : 'FAILED'));
|
|
1714
|
+
if (eng.attempts) {
|
|
1715
|
+
for (var ea = 0; ea < eng.attempts.length; ea++) {
|
|
1716
|
+
createSummary.indented(' ' + eng.attempts[ea]);
|
|
1717
|
+
}
|
|
1647
1718
|
}
|
|
1648
1719
|
}
|
|
1649
1720
|
}
|
|
1721
|
+
if (diagnostics.latest_version_auto_set !== undefined) {
|
|
1722
|
+
createSummary.indented('latest_version: ' + (diagnostics.latest_version_auto_set ? 'set' : 'null'));
|
|
1723
|
+
}
|
|
1650
1724
|
if (diagnostics.post_verify) {
|
|
1651
1725
|
if (diagnostics.post_verify.error) {
|
|
1652
1726
|
createSummary.indented('Post-verify: error — ' + diagnostics.post_verify.error);
|