snow-flow-test 10.0.1-test.110 → 10.0.1-test.112
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
|
@@ -45,17 +45,90 @@ var _bootstrapPromise: Promise<{ namespace: string; apiSysId: string }> | null =
|
|
|
45
45
|
* This runs server-side on ServiceNow and triggers all Business Rules,
|
|
46
46
|
* unlike direct Table API inserts which skip sys_hub_flow_version creation.
|
|
47
47
|
*/
|
|
48
|
+
/**
|
|
49
|
+
* Discover script — GET /discover endpoint.
|
|
50
|
+
* Probes which sn_fd APIs, methods and fields are available on this instance.
|
|
51
|
+
*/
|
|
52
|
+
var FLOW_FACTORY_DISCOVER_SCRIPT = [
|
|
53
|
+
'(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {',
|
|
54
|
+
' var r = {',
|
|
55
|
+
' build_tag: gs.getProperty("glide.buildtag") || "unknown",',
|
|
56
|
+
' build_name: gs.getProperty("glide.buildname") || "unknown",',
|
|
57
|
+
' apis: {}, methods: {}, fields: {}',
|
|
58
|
+
' };',
|
|
59
|
+
' try { r.apis.sn_fd = typeof sn_fd !== "undefined" ? "available" : "unavailable"; } catch(e) { r.apis.sn_fd = "unavailable"; }',
|
|
60
|
+
' var apiNames = ["FlowDesigner","FlowAPI","FlowPublisher","FlowCompiler"];',
|
|
61
|
+
' for (var i = 0; i < apiNames.length; i++) {',
|
|
62
|
+
' try {',
|
|
63
|
+
' if (typeof sn_fd !== "undefined") { r.apis[apiNames[i]] = typeof sn_fd[apiNames[i]]; }',
|
|
64
|
+
' else { r.apis[apiNames[i]] = "no_sn_fd"; }',
|
|
65
|
+
' } catch(e) { r.apis[apiNames[i]] = "error:" + e; }',
|
|
66
|
+
' }',
|
|
67
|
+
' var globalNames = ["GlideFlowDesigner","FlowDesignerInternalAPI","FlowDesignerAPI"];',
|
|
68
|
+
' for (var g = 0; g < globalNames.length; g++) {',
|
|
69
|
+
' try {',
|
|
70
|
+
' var gv = this[globalNames[g]];',
|
|
71
|
+
' r.apis[globalNames[g]] = gv ? typeof gv : "undefined";',
|
|
72
|
+
' } catch(e) { r.apis[globalNames[g]] = "error:" + e; }',
|
|
73
|
+
' }',
|
|
74
|
+
' try {',
|
|
75
|
+
' if (typeof sn_fd !== "undefined" && sn_fd.FlowDesigner) {',
|
|
76
|
+
' var fd = sn_fd.FlowDesigner;',
|
|
77
|
+
' var mns = ["createFlow","publishFlow","activateFlow","compileFlow","createDraftFlow","getFlow"];',
|
|
78
|
+
' for (var m = 0; m < mns.length; m++) { r.methods[mns[m]] = typeof fd[mns[m]]; }',
|
|
79
|
+
' }',
|
|
80
|
+
' } catch(e) { r.methods._error = e + ""; }',
|
|
81
|
+
' try {',
|
|
82
|
+
' var gr = new GlideRecord("sys_hub_flow_version");',
|
|
83
|
+
' var fns = ["compiled_definition","compile_state","is_current","published_flow","flow_definition"];',
|
|
84
|
+
' for (var f = 0; f < fns.length; f++) { r.fields[fns[f]] = gr.isValidField(fns[f]); }',
|
|
85
|
+
' } catch(e) { r.fields._error = e + ""; }',
|
|
86
|
+
' try {',
|
|
87
|
+
' var br = new GlideRecord("sys_script");',
|
|
88
|
+
' br.addQuery("collection","sys_hub_flow"); br.addQuery("active",true); br.query();',
|
|
89
|
+
' r.flow_br_count = br.getRowCount();',
|
|
90
|
+
' var brv = new GlideRecord("sys_script");',
|
|
91
|
+
' brv.addQuery("collection","sys_hub_flow_version"); brv.addQuery("active",true); brv.query();',
|
|
92
|
+
' r.version_br_count = brv.getRowCount();',
|
|
93
|
+
' } catch(e) { r.br_error = e + ""; }',
|
|
94
|
+
' response.setStatus(200);',
|
|
95
|
+
' response.setBody(r);',
|
|
96
|
+
'})(request, response);'
|
|
97
|
+
].join('\n');
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Create script — POST /create endpoint.
|
|
101
|
+
* Three-tier approach:
|
|
102
|
+
* Tier 1: sn_fd.FlowDesigner API (handles everything internally)
|
|
103
|
+
* Tier 2: GlideRecord INSERT as draft → UPDATE to published (triggers compilation BRs)
|
|
104
|
+
* Tier 3: Raw GlideRecord INSERT (last resort, may not register with engine)
|
|
105
|
+
*/
|
|
48
106
|
var FLOW_FACTORY_SCRIPT = [
|
|
49
107
|
'(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {',
|
|
50
|
-
|
|
51
|
-
'
|
|
52
|
-
'
|
|
108
|
+
// ── Body parsing (3-method cascade) ──
|
|
109
|
+
' var body = null;',
|
|
110
|
+
' var parseLog = [];',
|
|
111
|
+
' try {',
|
|
112
|
+
' var ds = request.body.dataString;',
|
|
113
|
+
' if (ds) { body = JSON.parse(ds + ""); parseLog.push("dataString:ok"); }',
|
|
114
|
+
' } catch(e1) { parseLog.push("dataString:" + e1); }',
|
|
115
|
+
' if (!body) {',
|
|
116
|
+
' try {',
|
|
117
|
+
' var d = request.body.data;',
|
|
118
|
+
' if (d && typeof d === "object") { body = d; parseLog.push("data:ok"); }',
|
|
119
|
+
' else if (d) { body = JSON.parse(d + ""); parseLog.push("data-parse:ok"); }',
|
|
120
|
+
' } catch(e2) { parseLog.push("data:" + e2); }',
|
|
121
|
+
' }',
|
|
122
|
+
' if (!body) {',
|
|
123
|
+
' try { body = JSON.parse(request.body + ""); parseLog.push("body-direct:ok"); }',
|
|
124
|
+
' catch(e3) { parseLog.push("body-direct:" + e3); }',
|
|
125
|
+
' }',
|
|
53
126
|
' if (!body || typeof body !== "object") {',
|
|
54
127
|
' response.setStatus(400);',
|
|
55
|
-
' response.setBody({ success: false, error: "
|
|
128
|
+
' response.setBody({ success: false, error: "No parseable body", parseLog: parseLog, bodyType: typeof request.body });',
|
|
56
129
|
' return;',
|
|
57
130
|
' }',
|
|
58
|
-
' var result = { success: false, steps: {} };',
|
|
131
|
+
' var result = { success: false, steps: {}, tier_used: null, parseLog: parseLog };',
|
|
59
132
|
'',
|
|
60
133
|
' try {',
|
|
61
134
|
' var flowName = body.name || "Unnamed Flow";',
|
|
@@ -70,170 +143,201 @@ var FLOW_FACTORY_SCRIPT = [
|
|
|
70
143
|
' var activities = body.activities || [];',
|
|
71
144
|
' var inputs = body.inputs || [];',
|
|
72
145
|
' var outputs = body.outputs || [];',
|
|
146
|
+
' var flowDef = body.flow_definition;',
|
|
147
|
+
' var flowDefStr = flowDef ? (typeof flowDef === "string" ? flowDef : JSON.stringify(flowDef)) : "";',
|
|
148
|
+
' var intName = flowName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");',
|
|
73
149
|
'',
|
|
74
|
-
'
|
|
75
|
-
' var
|
|
76
|
-
' flow.initialize();',
|
|
77
|
-
' flow.setValue("name", flowName);',
|
|
78
|
-
' flow.setValue("description", flowDesc);',
|
|
79
|
-
' flow.setValue("internal_name", flowName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, ""));',
|
|
80
|
-
' flow.setValue("category", flowCategory);',
|
|
81
|
-
' flow.setValue("run_as", runAs);',
|
|
82
|
-
' flow.setValue("active", shouldActivate);',
|
|
83
|
-
' flow.setValue("status", shouldActivate ? "published" : "draft");',
|
|
84
|
-
' flow.setValue("validated", true);',
|
|
85
|
-
' flow.setValue("type", isSubflow ? "subflow" : "flow");',
|
|
86
|
-
'',
|
|
87
|
-
' if (body.flow_definition) {',
|
|
88
|
-
' flow.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
|
|
89
|
-
' flow.setValue("latest_snapshot", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
|
|
90
|
-
' }',
|
|
91
|
-
'',
|
|
92
|
-
' var flowSysId = flow.insert();',
|
|
93
|
-
' if (!flowSysId) {',
|
|
94
|
-
' result.error = "Failed to insert sys_hub_flow record";',
|
|
95
|
-
' response.setStatus(500);',
|
|
96
|
-
' response.setBody(result);',
|
|
97
|
-
' return;',
|
|
98
|
-
' }',
|
|
99
|
-
' result.steps.flow = { success: true, sys_id: flowSysId + "" };',
|
|
150
|
+
' var flowSysId = null;',
|
|
151
|
+
' var verSysId = null;',
|
|
100
152
|
'',
|
|
101
|
-
|
|
153
|
+
// ── TIER 1: sn_fd.FlowDesigner API ──
|
|
102
154
|
' try {',
|
|
103
|
-
'
|
|
104
|
-
'
|
|
105
|
-
'
|
|
106
|
-
'
|
|
107
|
-
'
|
|
108
|
-
'
|
|
109
|
-
'
|
|
110
|
-
'
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
' if (body.flow_definition) {',
|
|
114
|
-
' ver.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
|
|
115
|
-
' }',
|
|
116
|
-
' var verSysId = ver.insert();',
|
|
117
|
-
' if (verSysId) {',
|
|
118
|
-
' result.steps.version = { success: true, sys_id: verSysId + "" };',
|
|
119
|
-
' // Update flow to point to latest version',
|
|
120
|
-
' var flowUpd = new GlideRecord("sys_hub_flow");',
|
|
121
|
-
' if (flowUpd.get(flowSysId)) {',
|
|
122
|
-
' flowUpd.setValue("latest_version", verSysId);',
|
|
123
|
-
' flowUpd.update();',
|
|
155
|
+
' if (typeof sn_fd !== "undefined" && sn_fd.FlowDesigner && typeof sn_fd.FlowDesigner.createFlow === "function") {',
|
|
156
|
+
' var fdResult = sn_fd.FlowDesigner.createFlow({ name: flowName, description: flowDesc, type: isSubflow ? "subflow" : "flow", category: flowCategory, run_as: runAs });',
|
|
157
|
+
' if (fdResult) {',
|
|
158
|
+
' flowSysId = (typeof fdResult === "object" ? fdResult.sys_id || fdResult.getValue("sys_id") : fdResult) + "";',
|
|
159
|
+
' result.tier_used = "sn_fd_api";',
|
|
160
|
+
' result.steps.tier1 = { success: true, api: "sn_fd.FlowDesigner.createFlow" };',
|
|
161
|
+
' if (shouldActivate && typeof sn_fd.FlowDesigner.publishFlow === "function") {',
|
|
162
|
+
' try { sn_fd.FlowDesigner.publishFlow(flowSysId); result.steps.tier1_publish = { success: true }; }',
|
|
163
|
+
' catch(pe) { result.steps.tier1_publish = { success: false, error: pe + "" }; }',
|
|
164
|
+
' }',
|
|
124
165
|
' }',
|
|
125
|
-
' } else {',
|
|
126
|
-
' result.steps.version = { success: false, error: "Insert returned empty sys_id" };',
|
|
127
166
|
' }',
|
|
128
|
-
' } catch (
|
|
129
|
-
' result.steps.version = { success: false, error: verErr.getMessage ? verErr.getMessage() : verErr + "" };',
|
|
130
|
-
' }',
|
|
167
|
+
' } catch(t1e) { result.steps.tier1 = { success: false, error: t1e.getMessage ? t1e.getMessage() : t1e + "" }; }',
|
|
131
168
|
'',
|
|
132
|
-
|
|
133
|
-
' if (!
|
|
169
|
+
// ── TIER 2: GlideRecord INSERT as draft → UPDATE to published ──
|
|
170
|
+
' if (!flowSysId) {',
|
|
134
171
|
' try {',
|
|
135
|
-
' var
|
|
136
|
-
'
|
|
137
|
-
'
|
|
138
|
-
'
|
|
139
|
-
'
|
|
140
|
-
'
|
|
141
|
-
'
|
|
142
|
-
'
|
|
143
|
-
'
|
|
144
|
-
'
|
|
145
|
-
'
|
|
146
|
-
'
|
|
147
|
-
'
|
|
148
|
-
'
|
|
149
|
-
'
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
158
|
-
'
|
|
172
|
+
' var flow = new GlideRecord("sys_hub_flow");',
|
|
173
|
+
' flow.initialize();',
|
|
174
|
+
' flow.setValue("name", flowName);',
|
|
175
|
+
' flow.setValue("description", flowDesc);',
|
|
176
|
+
' flow.setValue("internal_name", intName);',
|
|
177
|
+
' flow.setValue("category", flowCategory);',
|
|
178
|
+
' flow.setValue("run_as", runAs);',
|
|
179
|
+
' flow.setValue("active", false);',
|
|
180
|
+
' flow.setValue("status", "draft");',
|
|
181
|
+
' flow.setValue("validated", true);',
|
|
182
|
+
' flow.setValue("type", isSubflow ? "subflow" : "flow");',
|
|
183
|
+
' if (flowDefStr) { flow.setValue("flow_definition", flowDefStr); flow.setValue("latest_snapshot", flowDefStr); }',
|
|
184
|
+
' flowSysId = flow.insert();',
|
|
185
|
+
' result.steps.flow_insert = { success: !!flowSysId, sys_id: flowSysId + "", as_draft: true };',
|
|
186
|
+
'',
|
|
187
|
+
' if (flowSysId) {',
|
|
188
|
+
' var ver = new GlideRecord("sys_hub_flow_version");',
|
|
189
|
+
' ver.initialize();',
|
|
190
|
+
' ver.setValue("flow", flowSysId);',
|
|
191
|
+
' ver.setValue("name", "1.0");',
|
|
192
|
+
' ver.setValue("version", "1.0");',
|
|
193
|
+
' ver.setValue("state", "draft");',
|
|
194
|
+
' ver.setValue("active", true);',
|
|
195
|
+
' ver.setValue("compile_state", "draft");',
|
|
196
|
+
' ver.setValue("is_current", true);',
|
|
197
|
+
' if (flowDefStr) ver.setValue("flow_definition", flowDefStr);',
|
|
198
|
+
' verSysId = ver.insert();',
|
|
199
|
+
' result.steps.version_insert = { success: !!verSysId, sys_id: verSysId + "", as_draft: true };',
|
|
200
|
+
'',
|
|
201
|
+
' if (verSysId) {',
|
|
202
|
+
' var linkUpd = new GlideRecord("sys_hub_flow");',
|
|
203
|
+
' if (linkUpd.get(flowSysId)) { linkUpd.setValue("latest_version", verSysId); linkUpd.update(); }',
|
|
159
204
|
' }',
|
|
205
|
+
'',
|
|
206
|
+
' if (shouldActivate) {',
|
|
207
|
+
' var flowPub = new GlideRecord("sys_hub_flow");',
|
|
208
|
+
' if (flowPub.get(flowSysId)) {',
|
|
209
|
+
' flowPub.setValue("status", "published");',
|
|
210
|
+
' flowPub.setValue("active", true);',
|
|
211
|
+
' flowPub.update();',
|
|
212
|
+
' result.steps.flow_publish_update = { success: true };',
|
|
213
|
+
' }',
|
|
214
|
+
' if (verSysId) {',
|
|
215
|
+
' var verPub = new GlideRecord("sys_hub_flow_version");',
|
|
216
|
+
' if (verPub.get(verSysId)) {',
|
|
217
|
+
' verPub.setValue("state", "published");',
|
|
218
|
+
' verPub.setValue("compile_state", "compiled");',
|
|
219
|
+
' verPub.setValue("published_flow", flowSysId);',
|
|
220
|
+
' verPub.update();',
|
|
221
|
+
' result.steps.version_publish_update = { success: true };',
|
|
222
|
+
' }',
|
|
223
|
+
' }',
|
|
224
|
+
' }',
|
|
225
|
+
' result.tier_used = "gliderecord_draft_then_publish";',
|
|
226
|
+
' result.success = true;',
|
|
160
227
|
' }',
|
|
161
|
-
' } catch (
|
|
162
|
-
' result.steps.trigger = { success: false, error: trigErr.getMessage ? trigErr.getMessage() : trigErr + "" };',
|
|
163
|
-
' }',
|
|
228
|
+
' } catch(t2e) { result.steps.tier2 = { success: false, error: t2e.getMessage ? t2e.getMessage() : t2e + "" }; }',
|
|
164
229
|
' }',
|
|
165
230
|
'',
|
|
166
|
-
|
|
167
|
-
'
|
|
168
|
-
' for (var ai = 0; ai < activities.length; ai++) {',
|
|
231
|
+
// ── TIER 3: Raw GlideRecord INSERT (last resort) ──
|
|
232
|
+
' if (!flowSysId) {',
|
|
169
233
|
' try {',
|
|
170
|
-
' var
|
|
171
|
-
'
|
|
172
|
-
'
|
|
173
|
-
'
|
|
174
|
-
'
|
|
175
|
-
'
|
|
176
|
-
'
|
|
177
|
-
'
|
|
178
|
-
'
|
|
179
|
-
'
|
|
180
|
-
'
|
|
181
|
-
'
|
|
182
|
-
'
|
|
183
|
-
'
|
|
234
|
+
' var rawFlow = new GlideRecord("sys_hub_flow");',
|
|
235
|
+
' rawFlow.initialize();',
|
|
236
|
+
' rawFlow.setValue("name", flowName);',
|
|
237
|
+
' rawFlow.setValue("description", flowDesc);',
|
|
238
|
+
' rawFlow.setValue("internal_name", intName);',
|
|
239
|
+
' rawFlow.setValue("category", flowCategory);',
|
|
240
|
+
' rawFlow.setValue("run_as", runAs);',
|
|
241
|
+
' rawFlow.setValue("active", shouldActivate);',
|
|
242
|
+
' rawFlow.setValue("status", shouldActivate ? "published" : "draft");',
|
|
243
|
+
' rawFlow.setValue("validated", true);',
|
|
244
|
+
' rawFlow.setValue("type", isSubflow ? "subflow" : "flow");',
|
|
245
|
+
' if (flowDefStr) { rawFlow.setValue("flow_definition", flowDefStr); rawFlow.setValue("latest_snapshot", flowDefStr); }',
|
|
246
|
+
' flowSysId = rawFlow.insert();',
|
|
247
|
+
' if (flowSysId) {',
|
|
248
|
+
' var rawVer = new GlideRecord("sys_hub_flow_version");',
|
|
249
|
+
' rawVer.initialize();',
|
|
250
|
+
' rawVer.setValue("flow", flowSysId);',
|
|
251
|
+
' rawVer.setValue("name", "1.0"); rawVer.setValue("version", "1.0");',
|
|
252
|
+
' rawVer.setValue("state", shouldActivate ? "published" : "draft");',
|
|
253
|
+
' rawVer.setValue("active", true); rawVer.setValue("compile_state", "compiled");',
|
|
254
|
+
' rawVer.setValue("is_current", true);',
|
|
255
|
+
' if (shouldActivate) rawVer.setValue("published_flow", flowSysId);',
|
|
256
|
+
' if (flowDefStr) rawVer.setValue("flow_definition", flowDefStr);',
|
|
257
|
+
' verSysId = rawVer.insert();',
|
|
258
|
+
' if (verSysId) {',
|
|
259
|
+
' var rawLink = new GlideRecord("sys_hub_flow");',
|
|
260
|
+
' if (rawLink.get(flowSysId)) { rawLink.setValue("latest_version", verSysId); rawLink.update(); }',
|
|
261
|
+
' }',
|
|
262
|
+
' result.tier_used = "gliderecord_raw";',
|
|
263
|
+
' result.success = true;',
|
|
184
264
|
' }',
|
|
185
|
-
'
|
|
186
|
-
' } catch (actErr) {',
|
|
187
|
-
' // Best-effort per action',
|
|
188
|
-
' }',
|
|
265
|
+
' } catch(t3e) { result.steps.tier3 = { success: false, error: t3e.getMessage ? t3e.getMessage() : t3e + "" }; }',
|
|
189
266
|
' }',
|
|
190
|
-
' result.steps.actions = { success: true, created: actionsCreated, requested: activities.length };',
|
|
191
267
|
'',
|
|
192
|
-
|
|
193
|
-
'
|
|
194
|
-
'
|
|
195
|
-
'
|
|
268
|
+
// ── Common: trigger, actions, variables ──
|
|
269
|
+
' if (flowSysId) {',
|
|
270
|
+
' result.flow_sys_id = flowSysId + "";',
|
|
271
|
+
' result.version_sys_id = verSysId ? verSysId + "" : null;',
|
|
272
|
+
' result.version_created = !!verSysId;',
|
|
273
|
+
'',
|
|
274
|
+
' if (!isSubflow && triggerType !== "manual") {',
|
|
196
275
|
' try {',
|
|
197
|
-
' var
|
|
198
|
-
' var
|
|
199
|
-
'
|
|
200
|
-
'
|
|
201
|
-
'
|
|
202
|
-
'
|
|
203
|
-
'
|
|
204
|
-
'
|
|
205
|
-
'
|
|
206
|
-
'
|
|
207
|
-
'
|
|
208
|
-
'
|
|
276
|
+
' var triggerMap = { "record_created": "sn_fd.trigger.record_created", "record_updated": "sn_fd.trigger.record_updated", "scheduled": "sn_fd.trigger.scheduled" };',
|
|
277
|
+
' var trigIntName = triggerMap[triggerType] || "";',
|
|
278
|
+
' if (trigIntName) {',
|
|
279
|
+
' var trigDef = new GlideRecord("sys_hub_action_type_definition");',
|
|
280
|
+
' trigDef.addQuery("internal_name", trigIntName); trigDef.query();',
|
|
281
|
+
' if (trigDef.next()) {',
|
|
282
|
+
' var trigInst = new GlideRecord("sys_hub_trigger_instance");',
|
|
283
|
+
' trigInst.initialize();',
|
|
284
|
+
' trigInst.setValue("flow", flowSysId); trigInst.setValue("action_type", trigDef.getUniqueValue());',
|
|
285
|
+
' trigInst.setValue("name", triggerType); trigInst.setValue("order", 0); trigInst.setValue("active", true);',
|
|
286
|
+
' if (triggerTable) trigInst.setValue("table", triggerTable);',
|
|
287
|
+
' if (triggerCondition) trigInst.setValue("condition", triggerCondition);',
|
|
288
|
+
' var trigSysId = trigInst.insert();',
|
|
289
|
+
' result.steps.trigger = { success: !!trigSysId, sys_id: trigSysId + "" };',
|
|
290
|
+
' } else { result.steps.trigger = { success: false, error: "Trigger def not found: " + trigIntName }; }',
|
|
291
|
+
' }',
|
|
292
|
+
' } catch(te) { result.steps.trigger = { success: false, error: te.getMessage ? te.getMessage() : te + "" }; }',
|
|
209
293
|
' }',
|
|
210
|
-
'
|
|
294
|
+
'',
|
|
295
|
+
' var actionsCreated = 0;',
|
|
296
|
+
' for (var ai = 0; ai < activities.length; ai++) {',
|
|
211
297
|
' try {',
|
|
212
|
-
' var
|
|
213
|
-
' var
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
'
|
|
217
|
-
'
|
|
218
|
-
'
|
|
219
|
-
'
|
|
220
|
-
' if (
|
|
221
|
-
'
|
|
298
|
+
' var act = activities[ai];',
|
|
299
|
+
' var actDef = new GlideRecord("sys_hub_action_type_definition");',
|
|
300
|
+
' actDef.addQuery("internal_name", "CONTAINS", act.type || "script");',
|
|
301
|
+
' actDef.addOrCondition("name", "CONTAINS", act.type || "script"); actDef.query();',
|
|
302
|
+
' var actInst = new GlideRecord("sys_hub_action_instance");',
|
|
303
|
+
' actInst.initialize();',
|
|
304
|
+
' actInst.setValue("flow", flowSysId); actInst.setValue("name", act.name || "Action " + (ai + 1));',
|
|
305
|
+
' actInst.setValue("order", (ai + 1) * 100); actInst.setValue("active", true);',
|
|
306
|
+
' if (actDef.next()) actInst.setValue("action_type", actDef.getUniqueValue());',
|
|
307
|
+
' if (actInst.insert()) actionsCreated++;',
|
|
308
|
+
' } catch(ae) {}',
|
|
309
|
+
' }',
|
|
310
|
+
' result.steps.actions = { success: true, created: actionsCreated, requested: activities.length };',
|
|
311
|
+
'',
|
|
312
|
+
' var varsCreated = 0;',
|
|
313
|
+
' if (isSubflow) {',
|
|
314
|
+
' for (var vi = 0; vi < inputs.length; vi++) {',
|
|
315
|
+
' try { var inp = inputs[vi]; var fv = new GlideRecord("sys_hub_flow_variable"); fv.initialize();',
|
|
316
|
+
' fv.setValue("flow",flowSysId); fv.setValue("name",inp.name); fv.setValue("label",inp.label||inp.name);',
|
|
317
|
+
' fv.setValue("type",inp.type||"string"); fv.setValue("mandatory",inp.mandatory||false);',
|
|
318
|
+
' fv.setValue("default_value",inp.default_value||""); fv.setValue("variable_type","input");',
|
|
319
|
+
' if (fv.insert()) varsCreated++;',
|
|
320
|
+
' } catch(ve) {}',
|
|
321
|
+
' }',
|
|
322
|
+
' for (var vo = 0; vo < outputs.length; vo++) {',
|
|
323
|
+
' try { var out = outputs[vo]; var ov = new GlideRecord("sys_hub_flow_variable"); ov.initialize();',
|
|
324
|
+
' ov.setValue("flow",flowSysId); ov.setValue("name",out.name); ov.setValue("label",out.label||out.name);',
|
|
325
|
+
' ov.setValue("type",out.type||"string"); ov.setValue("variable_type","output");',
|
|
326
|
+
' if (ov.insert()) varsCreated++;',
|
|
327
|
+
' } catch(ve) {}',
|
|
328
|
+
' }',
|
|
222
329
|
' }',
|
|
330
|
+
' result.steps.variables = { success: true, created: varsCreated };',
|
|
223
331
|
' }',
|
|
224
|
-
' result.steps.variables = { success: true, created: varsCreated };',
|
|
225
332
|
'',
|
|
226
|
-
' result.success
|
|
227
|
-
'
|
|
228
|
-
' result.version_created = !!(result.steps.version && result.steps.version.success);',
|
|
229
|
-
' response.setStatus(201);',
|
|
333
|
+
' if (result.success) response.setStatus(201);',
|
|
334
|
+
' else response.setStatus(500);',
|
|
230
335
|
'',
|
|
231
336
|
' } catch (e) {',
|
|
232
337
|
' result.success = false;',
|
|
233
338
|
' result.error = e.getMessage ? e.getMessage() : e + "";',
|
|
234
339
|
' response.setStatus(500);',
|
|
235
340
|
' }',
|
|
236
|
-
'',
|
|
237
341
|
' response.setBody(result);',
|
|
238
342
|
'})(request, response);'
|
|
239
343
|
].join('\n');
|
|
@@ -384,7 +488,7 @@ async function ensureFlowFactoryAPI(
|
|
|
384
488
|
name: 'create',
|
|
385
489
|
active: true,
|
|
386
490
|
relative_path: '/create',
|
|
387
|
-
short_description: 'Create a flow
|
|
491
|
+
short_description: 'Create a flow/subflow via 3-tier approach: sn_fd API → draft+publish → raw GlideRecord',
|
|
388
492
|
operation_script: FLOW_FACTORY_SCRIPT,
|
|
389
493
|
requires_authentication: true,
|
|
390
494
|
enforce_acl: 'false'
|
|
@@ -395,6 +499,23 @@ async function ensureFlowFactoryAPI(
|
|
|
395
499
|
throw new Error('Failed to create Scripted REST operation: ' + (opError.message || opError));
|
|
396
500
|
}
|
|
397
501
|
|
|
502
|
+
// 5b. Deploy the GET /discover resource (API capability probing)
|
|
503
|
+
try {
|
|
504
|
+
await client.post('/api/now/table/sys_ws_operation', {
|
|
505
|
+
web_service_definition: apiSysId,
|
|
506
|
+
http_method: 'GET',
|
|
507
|
+
name: 'discover',
|
|
508
|
+
active: true,
|
|
509
|
+
relative_path: '/discover',
|
|
510
|
+
short_description: 'Probe available sn_fd APIs, methods and fields for this ServiceNow instance',
|
|
511
|
+
operation_script: FLOW_FACTORY_DISCOVER_SCRIPT,
|
|
512
|
+
requires_authentication: true,
|
|
513
|
+
enforce_acl: 'false'
|
|
514
|
+
});
|
|
515
|
+
} catch (_) {
|
|
516
|
+
// Non-fatal: create endpoint is more important than discover
|
|
517
|
+
}
|
|
518
|
+
|
|
398
519
|
// 6. Probe to discover the namespace ServiceNow assigned
|
|
399
520
|
var resolvedNs = await probeFlowFactoryNamespace(client, apiSysId, instanceUrl);
|
|
400
521
|
if (!resolvedNs) {
|
|
@@ -417,6 +538,111 @@ async function ensureFlowFactoryAPI(
|
|
|
417
538
|
*/
|
|
418
539
|
function invalidateFlowFactoryCache(): void {
|
|
419
540
|
_flowFactoryCache = null;
|
|
541
|
+
_discoveryCache = null;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Call the /discover endpoint on the Flow Factory API to learn which
|
|
546
|
+
* sn_fd APIs and methods are available on the target ServiceNow instance.
|
|
547
|
+
* Results are cached alongside the factory cache.
|
|
548
|
+
*/
|
|
549
|
+
var _discoveryCache: any = null;
|
|
550
|
+
|
|
551
|
+
async function discoverInstanceCapabilities(
|
|
552
|
+
client: any,
|
|
553
|
+
namespace: string
|
|
554
|
+
): Promise<any> {
|
|
555
|
+
if (_discoveryCache) return _discoveryCache;
|
|
556
|
+
try {
|
|
557
|
+
var discoverEndpoint = '/api/' + namespace + '/' + FLOW_FACTORY_API_ID + '/discover';
|
|
558
|
+
var resp = await client.get(discoverEndpoint);
|
|
559
|
+
_discoveryCache = resp.data?.result || resp.data || null;
|
|
560
|
+
return _discoveryCache;
|
|
561
|
+
} catch (_) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* After creating a flow (via any method), try to register it with the
|
|
568
|
+
* Flow Designer engine by calling its built-in compile / publish / activate
|
|
569
|
+
* REST endpoints. Without this step the flow record exists in the DB but
|
|
570
|
+
* Flow Designer cannot open it ("Your flow cannot be found").
|
|
571
|
+
*
|
|
572
|
+
* Tries multiple known endpoint patterns because the namespace/path changed
|
|
573
|
+
* across ServiceNow releases. Returns diagnostics on which attempts were
|
|
574
|
+
* made and what succeeded.
|
|
575
|
+
*/
|
|
576
|
+
async function registerFlowWithEngine(
|
|
577
|
+
client: any,
|
|
578
|
+
flowSysId: string,
|
|
579
|
+
shouldActivate: boolean
|
|
580
|
+
): Promise<{ success: boolean; method: string; attempts: string[] }> {
|
|
581
|
+
var attempts: string[] = [];
|
|
582
|
+
|
|
583
|
+
// Helper: try a POST and classify the result
|
|
584
|
+
async function tryPost(label: string, url: string, body?: any): Promise<boolean> {
|
|
585
|
+
try {
|
|
586
|
+
await client.post(url, body || {});
|
|
587
|
+
attempts.push(label + ': success');
|
|
588
|
+
return true;
|
|
589
|
+
} catch (e: any) {
|
|
590
|
+
var s = e.response?.status || 'err';
|
|
591
|
+
attempts.push(label + ': ' + s);
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// ── Strategy 1: Publish via Flow Designer REST API ───────────────
|
|
597
|
+
// This is the closest equivalent to clicking "Publish" in the UI.
|
|
598
|
+
var publishPaths = [
|
|
599
|
+
'/api/sn_fd/flow/' + flowSysId + '/publish',
|
|
600
|
+
'/api/sn_fd/designer/flow/' + flowSysId + '/publish',
|
|
601
|
+
'/api/sn_flow_designer/flow/' + flowSysId + '/publish',
|
|
602
|
+
];
|
|
603
|
+
for (var pi = 0; pi < publishPaths.length; pi++) {
|
|
604
|
+
if (await tryPost('publish[' + pi + ']', publishPaths[pi])) {
|
|
605
|
+
return { success: true, method: 'publish', attempts: attempts };
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// ── Strategy 2: Activate (registers the flow with the engine) ────
|
|
610
|
+
if (shouldActivate) {
|
|
611
|
+
var activatePaths = [
|
|
612
|
+
'/api/sn_fd/flow/' + flowSysId + '/activate',
|
|
613
|
+
'/api/sn_fd/designer/flow/' + flowSysId + '/activate',
|
|
614
|
+
'/api/sn_flow_designer/flow/' + flowSysId + '/activate',
|
|
615
|
+
];
|
|
616
|
+
for (var ai = 0; ai < activatePaths.length; ai++) {
|
|
617
|
+
if (await tryPost('activate[' + ai + ']', activatePaths[ai])) {
|
|
618
|
+
return { success: true, method: 'activate', attempts: attempts };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// ── Strategy 3: Checkout + checkin (triggers internal compilation) ─
|
|
624
|
+
var checkoutPaths = [
|
|
625
|
+
'/api/sn_fd/flow/' + flowSysId + '/checkout',
|
|
626
|
+
'/api/sn_fd/designer/flow/' + flowSysId + '/checkout',
|
|
627
|
+
];
|
|
628
|
+
for (var ci = 0; ci < checkoutPaths.length; ci++) {
|
|
629
|
+
if (await tryPost('checkout[' + ci + ']', checkoutPaths[ci])) {
|
|
630
|
+
var checkinPath = checkoutPaths[ci].replace('/checkout', '/checkin');
|
|
631
|
+
await tryPost('checkin[' + ci + ']', checkinPath);
|
|
632
|
+
return { success: true, method: 'checkout+checkin', attempts: attempts };
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// ── Strategy 4: Snapshot (already existed — triggers version snapshot) ─
|
|
637
|
+
if (await tryPost('snapshot', '/api/sn_flow_designer/flow/snapshot', { flow_id: flowSysId })) {
|
|
638
|
+
return { success: true, method: 'snapshot', attempts: attempts };
|
|
639
|
+
}
|
|
640
|
+
// Try alternate snapshot path
|
|
641
|
+
if (await tryPost('snapshot-alt', '/api/sn_fd/flow/' + flowSysId + '/snapshot')) {
|
|
642
|
+
return { success: true, method: 'snapshot-alt', attempts: attempts };
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return { success: false, method: 'none', attempts: attempts };
|
|
420
646
|
}
|
|
421
647
|
|
|
422
648
|
/**
|
|
@@ -675,7 +901,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
675
901
|
version_created: false,
|
|
676
902
|
version_method: null,
|
|
677
903
|
version_fields_set: [] as string[],
|
|
678
|
-
|
|
904
|
+
engine_registration: null,
|
|
679
905
|
post_verify: null
|
|
680
906
|
};
|
|
681
907
|
|
|
@@ -684,6 +910,14 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
684
910
|
diagnostics.factory_bootstrap = 'success';
|
|
685
911
|
diagnostics.factory_namespace = factory.namespace;
|
|
686
912
|
|
|
913
|
+
// Call /discover to learn what sn_fd APIs exist on this instance
|
|
914
|
+
var discovery = await discoverInstanceCapabilities(client, factory.namespace);
|
|
915
|
+
if (discovery) {
|
|
916
|
+
diagnostics.instance_build = discovery.build_tag || discovery.build_name || 'unknown';
|
|
917
|
+
diagnostics.available_apis = discovery.apis || {};
|
|
918
|
+
diagnostics.available_methods = discovery.methods || {};
|
|
919
|
+
}
|
|
920
|
+
|
|
687
921
|
var factoryPayload = {
|
|
688
922
|
name: flowName,
|
|
689
923
|
description: flowDescription,
|
|
@@ -722,12 +956,15 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
722
956
|
|
|
723
957
|
var factoryResult = factoryResp.data?.result || factoryResp.data;
|
|
724
958
|
if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
|
|
959
|
+
var tierUsed = factoryResult.tier_used || 'unknown';
|
|
725
960
|
diagnostics.factory_call = 'success';
|
|
961
|
+
diagnostics.factory_tier = tierUsed;
|
|
962
|
+
diagnostics.factory_steps = factoryResult.steps || {};
|
|
726
963
|
flowSysId = factoryResult.flow_sys_id;
|
|
727
964
|
usedMethod = 'scripted_rest_api';
|
|
728
965
|
versionCreated = !!factoryResult.version_created;
|
|
729
966
|
diagnostics.version_created = versionCreated;
|
|
730
|
-
diagnostics.version_method = 'factory';
|
|
967
|
+
diagnostics.version_method = 'factory (' + tierUsed + ')';
|
|
731
968
|
diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
|
|
732
969
|
|
|
733
970
|
// Extract step details
|
|
@@ -919,12 +1156,20 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
919
1156
|
}
|
|
920
1157
|
}
|
|
921
1158
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// ── Register flow with Flow Designer engine ─────────────────
|
|
1162
|
+
// This is the KEY step: without it, records exist but Flow Designer
|
|
1163
|
+
// shows "Your flow cannot be found" because the engine hasn't compiled it.
|
|
1164
|
+
if (flowSysId) {
|
|
1165
|
+
var engineResult = await registerFlowWithEngine(client, flowSysId, shouldActivate);
|
|
1166
|
+
diagnostics.engine_registration = {
|
|
1167
|
+
success: engineResult.success,
|
|
1168
|
+
method: engineResult.method,
|
|
1169
|
+
attempts: engineResult.attempts
|
|
1170
|
+
};
|
|
1171
|
+
if (!engineResult.success) {
|
|
1172
|
+
factoryWarnings.push('Flow Designer engine registration failed — flow may show "cannot be found". Attempts: ' + engineResult.attempts.join(', '));
|
|
928
1173
|
}
|
|
929
1174
|
}
|
|
930
1175
|
|
|
@@ -1034,12 +1279,29 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1034
1279
|
|
|
1035
1280
|
// Diagnostics section
|
|
1036
1281
|
createSummary.blank().line('Diagnostics:');
|
|
1282
|
+
if (diagnostics.instance_build) {
|
|
1283
|
+
createSummary.indented('Instance build: ' + diagnostics.instance_build);
|
|
1284
|
+
}
|
|
1037
1285
|
createSummary.indented('Factory bootstrap: ' + (diagnostics.factory_bootstrap || 'not attempted'));
|
|
1038
1286
|
createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
|
|
1039
1287
|
createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
|
|
1288
|
+
if (diagnostics.factory_tier) {
|
|
1289
|
+
createSummary.indented('Factory tier used: ' + diagnostics.factory_tier);
|
|
1290
|
+
}
|
|
1291
|
+
if (diagnostics.available_apis && Object.keys(diagnostics.available_apis).length > 0) {
|
|
1292
|
+
var apiStr = Object.keys(diagnostics.available_apis).map(function (k: string) { return k + '=' + diagnostics.available_apis[k]; }).join(', ');
|
|
1293
|
+
createSummary.indented('Available APIs: ' + apiStr);
|
|
1294
|
+
}
|
|
1040
1295
|
createSummary.indented('Table API used: ' + diagnostics.table_api_used);
|
|
1041
1296
|
createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
|
|
1042
|
-
|
|
1297
|
+
if (diagnostics.engine_registration) {
|
|
1298
|
+
createSummary.indented('Engine registration: ' + (diagnostics.engine_registration.success ? diagnostics.engine_registration.method : 'FAILED'));
|
|
1299
|
+
if (diagnostics.engine_registration.attempts) {
|
|
1300
|
+
for (var ea = 0; ea < diagnostics.engine_registration.attempts.length; ea++) {
|
|
1301
|
+
createSummary.indented(' ' + diagnostics.engine_registration.attempts[ea]);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1043
1305
|
if (diagnostics.post_verify) {
|
|
1044
1306
|
if (diagnostics.post_verify.error) {
|
|
1045
1307
|
createSummary.indented('Post-verify: error — ' + diagnostics.post_verify.error);
|
|
@@ -1425,5 +1687,5 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1425
1687
|
}
|
|
1426
1688
|
}
|
|
1427
1689
|
|
|
1428
|
-
export const version = '
|
|
1690
|
+
export const version = '5.0.0';
|
|
1429
1691
|
export const author = 'Snow-Flow Team';
|