snow-flow-test 10.0.1-test.111 → 10.0.1-test.113
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,8 +45,67 @@ 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) {',
|
|
108
|
+
// ── Body parsing (3-method cascade) ──
|
|
50
109
|
' var body = null;',
|
|
51
110
|
' var parseLog = [];',
|
|
52
111
|
' try {',
|
|
@@ -69,7 +128,7 @@ var FLOW_FACTORY_SCRIPT = [
|
|
|
69
128
|
' response.setBody({ success: false, error: "No parseable body", parseLog: parseLog, bodyType: typeof request.body });',
|
|
70
129
|
' return;',
|
|
71
130
|
' }',
|
|
72
|
-
' var result = { success: false, steps: {}, parseLog: parseLog };',
|
|
131
|
+
' var result = { success: false, steps: {}, tier_used: null, parseLog: parseLog };',
|
|
73
132
|
'',
|
|
74
133
|
' try {',
|
|
75
134
|
' var flowName = body.name || "Unnamed Flow";',
|
|
@@ -84,170 +143,201 @@ var FLOW_FACTORY_SCRIPT = [
|
|
|
84
143
|
' var activities = body.activities || [];',
|
|
85
144
|
' var inputs = body.inputs || [];',
|
|
86
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, "");',
|
|
87
149
|
'',
|
|
88
|
-
'
|
|
89
|
-
' var
|
|
90
|
-
' flow.initialize();',
|
|
91
|
-
' flow.setValue("name", flowName);',
|
|
92
|
-
' flow.setValue("description", flowDesc);',
|
|
93
|
-
' flow.setValue("internal_name", flowName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, ""));',
|
|
94
|
-
' flow.setValue("category", flowCategory);',
|
|
95
|
-
' flow.setValue("run_as", runAs);',
|
|
96
|
-
' flow.setValue("active", shouldActivate);',
|
|
97
|
-
' flow.setValue("status", shouldActivate ? "published" : "draft");',
|
|
98
|
-
' flow.setValue("validated", true);',
|
|
99
|
-
' flow.setValue("type", isSubflow ? "subflow" : "flow");',
|
|
100
|
-
'',
|
|
101
|
-
' if (body.flow_definition) {',
|
|
102
|
-
' flow.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
|
|
103
|
-
' flow.setValue("latest_snapshot", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
|
|
104
|
-
' }',
|
|
150
|
+
' var flowSysId = null;',
|
|
151
|
+
' var verSysId = null;',
|
|
105
152
|
'',
|
|
106
|
-
|
|
107
|
-
' if (!flowSysId) {',
|
|
108
|
-
' result.error = "Failed to insert sys_hub_flow record";',
|
|
109
|
-
' response.setStatus(500);',
|
|
110
|
-
' response.setBody(result);',
|
|
111
|
-
' return;',
|
|
112
|
-
' }',
|
|
113
|
-
' result.steps.flow = { success: true, sys_id: flowSysId + "" };',
|
|
114
|
-
'',
|
|
115
|
-
' // Step 2: Create sys_hub_flow_version (this is what Table API misses!)',
|
|
153
|
+
// ── TIER 1: sn_fd.FlowDesigner API ──
|
|
116
154
|
' try {',
|
|
117
|
-
'
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
124
|
-
'
|
|
125
|
-
'
|
|
126
|
-
'
|
|
127
|
-
' if (body.flow_definition) {',
|
|
128
|
-
' ver.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
|
|
129
|
-
' }',
|
|
130
|
-
' var verSysId = ver.insert();',
|
|
131
|
-
' if (verSysId) {',
|
|
132
|
-
' result.steps.version = { success: true, sys_id: verSysId + "" };',
|
|
133
|
-
' // Update flow to point to latest version',
|
|
134
|
-
' var flowUpd = new GlideRecord("sys_hub_flow");',
|
|
135
|
-
' if (flowUpd.get(flowSysId)) {',
|
|
136
|
-
' flowUpd.setValue("latest_version", verSysId);',
|
|
137
|
-
' 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
|
+
' }',
|
|
138
165
|
' }',
|
|
139
|
-
' } else {',
|
|
140
|
-
' result.steps.version = { success: false, error: "Insert returned empty sys_id" };',
|
|
141
166
|
' }',
|
|
142
|
-
' } catch (
|
|
143
|
-
' result.steps.version = { success: false, error: verErr.getMessage ? verErr.getMessage() : verErr + "" };',
|
|
144
|
-
' }',
|
|
167
|
+
' } catch(t1e) { result.steps.tier1 = { success: false, error: t1e.getMessage ? t1e.getMessage() : t1e + "" }; }',
|
|
145
168
|
'',
|
|
146
|
-
|
|
147
|
-
' if (!
|
|
169
|
+
// ── TIER 2: GlideRecord INSERT as draft → UPDATE to published ──
|
|
170
|
+
' if (!flowSysId) {',
|
|
148
171
|
' try {',
|
|
149
|
-
' var
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
158
|
-
'
|
|
159
|
-
'
|
|
160
|
-
'
|
|
161
|
-
'
|
|
162
|
-
'
|
|
163
|
-
'
|
|
164
|
-
'
|
|
165
|
-
'
|
|
166
|
-
'
|
|
167
|
-
'
|
|
168
|
-
'
|
|
169
|
-
'
|
|
170
|
-
'
|
|
171
|
-
'
|
|
172
|
-
'
|
|
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(); }',
|
|
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
|
+
' }',
|
|
173
224
|
' }',
|
|
225
|
+
' result.tier_used = "gliderecord_draft_then_publish";',
|
|
226
|
+
' result.success = true;',
|
|
174
227
|
' }',
|
|
175
|
-
' } catch (
|
|
176
|
-
' result.steps.trigger = { success: false, error: trigErr.getMessage ? trigErr.getMessage() : trigErr + "" };',
|
|
177
|
-
' }',
|
|
228
|
+
' } catch(t2e) { result.steps.tier2 = { success: false, error: t2e.getMessage ? t2e.getMessage() : t2e + "" }; }',
|
|
178
229
|
' }',
|
|
179
230
|
'',
|
|
180
|
-
|
|
181
|
-
'
|
|
182
|
-
' for (var ai = 0; ai < activities.length; ai++) {',
|
|
231
|
+
// ── TIER 3: Raw GlideRecord INSERT (last resort) ──
|
|
232
|
+
' if (!flowSysId) {',
|
|
183
233
|
' try {',
|
|
184
|
-
' var
|
|
185
|
-
'
|
|
186
|
-
'
|
|
187
|
-
'
|
|
188
|
-
'
|
|
189
|
-
'
|
|
190
|
-
'
|
|
191
|
-
'
|
|
192
|
-
'
|
|
193
|
-
'
|
|
194
|
-
'
|
|
195
|
-
'
|
|
196
|
-
'
|
|
197
|
-
'
|
|
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;',
|
|
198
264
|
' }',
|
|
199
|
-
'
|
|
200
|
-
' } catch (actErr) {',
|
|
201
|
-
' // Best-effort per action',
|
|
202
|
-
' }',
|
|
265
|
+
' } catch(t3e) { result.steps.tier3 = { success: false, error: t3e.getMessage ? t3e.getMessage() : t3e + "" }; }',
|
|
203
266
|
' }',
|
|
204
|
-
' result.steps.actions = { success: true, created: actionsCreated, requested: activities.length };',
|
|
205
267
|
'',
|
|
206
|
-
|
|
207
|
-
'
|
|
208
|
-
'
|
|
209
|
-
'
|
|
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") {',
|
|
210
275
|
' try {',
|
|
211
|
-
' var
|
|
212
|
-
' var
|
|
213
|
-
'
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
'
|
|
217
|
-
'
|
|
218
|
-
'
|
|
219
|
-
'
|
|
220
|
-
'
|
|
221
|
-
'
|
|
222
|
-
'
|
|
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 + "" }; }',
|
|
223
293
|
' }',
|
|
224
|
-
'
|
|
294
|
+
'',
|
|
295
|
+
' var actionsCreated = 0;',
|
|
296
|
+
' for (var ai = 0; ai < activities.length; ai++) {',
|
|
225
297
|
' try {',
|
|
226
|
-
' var
|
|
227
|
-
' var
|
|
228
|
-
'
|
|
229
|
-
'
|
|
230
|
-
'
|
|
231
|
-
'
|
|
232
|
-
'
|
|
233
|
-
'
|
|
234
|
-
' if (
|
|
235
|
-
'
|
|
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) {}',
|
|
236
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
|
+
' }',
|
|
329
|
+
' }',
|
|
330
|
+
' result.steps.variables = { success: true, created: varsCreated };',
|
|
237
331
|
' }',
|
|
238
|
-
' result.steps.variables = { success: true, created: varsCreated };',
|
|
239
332
|
'',
|
|
240
|
-
' result.success
|
|
241
|
-
'
|
|
242
|
-
' result.version_created = !!(result.steps.version && result.steps.version.success);',
|
|
243
|
-
' response.setStatus(201);',
|
|
333
|
+
' if (result.success) response.setStatus(201);',
|
|
334
|
+
' else response.setStatus(500);',
|
|
244
335
|
'',
|
|
245
336
|
' } catch (e) {',
|
|
246
337
|
' result.success = false;',
|
|
247
338
|
' result.error = e.getMessage ? e.getMessage() : e + "";',
|
|
248
339
|
' response.setStatus(500);',
|
|
249
340
|
' }',
|
|
250
|
-
'',
|
|
251
341
|
' response.setBody(result);',
|
|
252
342
|
'})(request, response);'
|
|
253
343
|
].join('\n');
|
|
@@ -317,17 +407,32 @@ async function probeFlowFactoryNamespace(
|
|
|
317
407
|
}
|
|
318
408
|
}
|
|
319
409
|
|
|
320
|
-
// 5. Probe each candidate — GET
|
|
410
|
+
// 5. Probe each candidate — GET the /discover endpoint (a real GET handler)
|
|
411
|
+
// If /discover doesn't exist yet, fall back to /create (expects 405)
|
|
321
412
|
for (var j = 0; j < unique.length; j++) {
|
|
413
|
+
// Try /discover first (GET endpoint, returns 200 when namespace is correct)
|
|
414
|
+
try {
|
|
415
|
+
var discoverResp = await client.get('/api/' + unique[j] + '/' + FLOW_FACTORY_API_ID + '/discover');
|
|
416
|
+
if (discoverResp.status === 200 || discoverResp.data) {
|
|
417
|
+
return unique[j]; // Namespace confirmed via /discover
|
|
418
|
+
}
|
|
419
|
+
} catch (discoverErr: any) {
|
|
420
|
+
var ds = discoverErr.response?.status;
|
|
421
|
+
if (ds === 401 || ds === 403) {
|
|
422
|
+
return unique[j]; // Namespace correct but auth issue
|
|
423
|
+
}
|
|
424
|
+
// 404 = wrong namespace OR /discover not deployed yet, try /create
|
|
425
|
+
}
|
|
426
|
+
// Fallback: try /create (POST-only, expect 405 for correct namespace)
|
|
322
427
|
try {
|
|
323
428
|
await client.get('/api/' + unique[j] + '/' + FLOW_FACTORY_API_ID + '/create');
|
|
324
|
-
return unique[j]; // 200 =
|
|
325
|
-
} catch (
|
|
326
|
-
var
|
|
327
|
-
if (
|
|
429
|
+
return unique[j]; // 200 = unexpected but valid
|
|
430
|
+
} catch (createErr: any) {
|
|
431
|
+
var cs = createErr.response?.status;
|
|
432
|
+
if (cs === 405 || cs === 401 || cs === 403) {
|
|
328
433
|
return unique[j]; // Namespace correct — method or auth rejected
|
|
329
434
|
}
|
|
330
|
-
// 404 = wrong namespace, try next
|
|
435
|
+
// 404 = wrong namespace, try next candidate
|
|
331
436
|
}
|
|
332
437
|
}
|
|
333
438
|
|
|
@@ -398,7 +503,7 @@ async function ensureFlowFactoryAPI(
|
|
|
398
503
|
name: 'create',
|
|
399
504
|
active: true,
|
|
400
505
|
relative_path: '/create',
|
|
401
|
-
short_description: 'Create a flow
|
|
506
|
+
short_description: 'Create a flow/subflow via 3-tier approach: sn_fd API → draft+publish → raw GlideRecord',
|
|
402
507
|
operation_script: FLOW_FACTORY_SCRIPT,
|
|
403
508
|
requires_authentication: true,
|
|
404
509
|
enforce_acl: 'false'
|
|
@@ -409,6 +514,23 @@ async function ensureFlowFactoryAPI(
|
|
|
409
514
|
throw new Error('Failed to create Scripted REST operation: ' + (opError.message || opError));
|
|
410
515
|
}
|
|
411
516
|
|
|
517
|
+
// 5b. Deploy the GET /discover resource (API capability probing)
|
|
518
|
+
try {
|
|
519
|
+
await client.post('/api/now/table/sys_ws_operation', {
|
|
520
|
+
web_service_definition: apiSysId,
|
|
521
|
+
http_method: 'GET',
|
|
522
|
+
name: 'discover',
|
|
523
|
+
active: true,
|
|
524
|
+
relative_path: '/discover',
|
|
525
|
+
short_description: 'Probe available sn_fd APIs, methods and fields for this ServiceNow instance',
|
|
526
|
+
operation_script: FLOW_FACTORY_DISCOVER_SCRIPT,
|
|
527
|
+
requires_authentication: true,
|
|
528
|
+
enforce_acl: 'false'
|
|
529
|
+
});
|
|
530
|
+
} catch (_) {
|
|
531
|
+
// Non-fatal: create endpoint is more important than discover
|
|
532
|
+
}
|
|
533
|
+
|
|
412
534
|
// 6. Probe to discover the namespace ServiceNow assigned
|
|
413
535
|
var resolvedNs = await probeFlowFactoryNamespace(client, apiSysId, instanceUrl);
|
|
414
536
|
if (!resolvedNs) {
|
|
@@ -431,6 +553,29 @@ async function ensureFlowFactoryAPI(
|
|
|
431
553
|
*/
|
|
432
554
|
function invalidateFlowFactoryCache(): void {
|
|
433
555
|
_flowFactoryCache = null;
|
|
556
|
+
_discoveryCache = null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Call the /discover endpoint on the Flow Factory API to learn which
|
|
561
|
+
* sn_fd APIs and methods are available on the target ServiceNow instance.
|
|
562
|
+
* Results are cached alongside the factory cache.
|
|
563
|
+
*/
|
|
564
|
+
var _discoveryCache: any = null;
|
|
565
|
+
|
|
566
|
+
async function discoverInstanceCapabilities(
|
|
567
|
+
client: any,
|
|
568
|
+
namespace: string
|
|
569
|
+
): Promise<any> {
|
|
570
|
+
if (_discoveryCache) return _discoveryCache;
|
|
571
|
+
try {
|
|
572
|
+
var discoverEndpoint = '/api/' + namespace + '/' + FLOW_FACTORY_API_ID + '/discover';
|
|
573
|
+
var resp = await client.get(discoverEndpoint);
|
|
574
|
+
_discoveryCache = resp.data?.result || resp.data || null;
|
|
575
|
+
return _discoveryCache;
|
|
576
|
+
} catch (_) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
434
579
|
}
|
|
435
580
|
|
|
436
581
|
/**
|
|
@@ -780,6 +925,14 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
780
925
|
diagnostics.factory_bootstrap = 'success';
|
|
781
926
|
diagnostics.factory_namespace = factory.namespace;
|
|
782
927
|
|
|
928
|
+
// Call /discover to learn what sn_fd APIs exist on this instance
|
|
929
|
+
var discovery = await discoverInstanceCapabilities(client, factory.namespace);
|
|
930
|
+
if (discovery) {
|
|
931
|
+
diagnostics.instance_build = discovery.build_tag || discovery.build_name || 'unknown';
|
|
932
|
+
diagnostics.available_apis = discovery.apis || {};
|
|
933
|
+
diagnostics.available_methods = discovery.methods || {};
|
|
934
|
+
}
|
|
935
|
+
|
|
783
936
|
var factoryPayload = {
|
|
784
937
|
name: flowName,
|
|
785
938
|
description: flowDescription,
|
|
@@ -818,12 +971,15 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
818
971
|
|
|
819
972
|
var factoryResult = factoryResp.data?.result || factoryResp.data;
|
|
820
973
|
if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
|
|
974
|
+
var tierUsed = factoryResult.tier_used || 'unknown';
|
|
821
975
|
diagnostics.factory_call = 'success';
|
|
976
|
+
diagnostics.factory_tier = tierUsed;
|
|
977
|
+
diagnostics.factory_steps = factoryResult.steps || {};
|
|
822
978
|
flowSysId = factoryResult.flow_sys_id;
|
|
823
979
|
usedMethod = 'scripted_rest_api';
|
|
824
980
|
versionCreated = !!factoryResult.version_created;
|
|
825
981
|
diagnostics.version_created = versionCreated;
|
|
826
|
-
diagnostics.version_method = 'factory';
|
|
982
|
+
diagnostics.version_method = 'factory (' + tierUsed + ')';
|
|
827
983
|
diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
|
|
828
984
|
|
|
829
985
|
// Extract step details
|
|
@@ -1138,9 +1294,19 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1138
1294
|
|
|
1139
1295
|
// Diagnostics section
|
|
1140
1296
|
createSummary.blank().line('Diagnostics:');
|
|
1297
|
+
if (diagnostics.instance_build) {
|
|
1298
|
+
createSummary.indented('Instance build: ' + diagnostics.instance_build);
|
|
1299
|
+
}
|
|
1141
1300
|
createSummary.indented('Factory bootstrap: ' + (diagnostics.factory_bootstrap || 'not attempted'));
|
|
1142
1301
|
createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
|
|
1143
1302
|
createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
|
|
1303
|
+
if (diagnostics.factory_tier) {
|
|
1304
|
+
createSummary.indented('Factory tier used: ' + diagnostics.factory_tier);
|
|
1305
|
+
}
|
|
1306
|
+
if (diagnostics.available_apis && Object.keys(diagnostics.available_apis).length > 0) {
|
|
1307
|
+
var apiStr = Object.keys(diagnostics.available_apis).map(function (k: string) { return k + '=' + diagnostics.available_apis[k]; }).join(', ');
|
|
1308
|
+
createSummary.indented('Available APIs: ' + apiStr);
|
|
1309
|
+
}
|
|
1144
1310
|
createSummary.indented('Table API used: ' + diagnostics.table_api_used);
|
|
1145
1311
|
createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
|
|
1146
1312
|
if (diagnostics.engine_registration) {
|
|
@@ -1536,5 +1702,5 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1536
1702
|
}
|
|
1537
1703
|
}
|
|
1538
1704
|
|
|
1539
|
-
export const version = '
|
|
1705
|
+
export const version = '5.0.0';
|
|
1540
1706
|
export const author = 'Snow-Flow Team';
|