snow-flow 10.0.1-dev.398 → 10.0.1-dev.399
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
|
@@ -344,52 +344,57 @@ var FLOW_FACTORY_SCRIPT = [
|
|
|
344
344
|
|
|
345
345
|
/**
|
|
346
346
|
* Resolve the REST API namespace for a sys_ws_definition record.
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
347
|
+
*
|
|
348
|
+
* Collects namespace candidates from multiple sources, then VERIFIES each
|
|
349
|
+
* one via HTTP before accepting it. This prevents returning invalid values
|
|
350
|
+
* like scope sys_ids or numeric identifiers that aren't valid URL namespaces.
|
|
351
|
+
*
|
|
352
|
+
* Verification: GET /api/{candidate}/{api_id}/discover
|
|
353
|
+
* - 200 = correct namespace, /discover endpoint works
|
|
354
|
+
* - 401/403 = correct namespace, auth issue
|
|
355
|
+
* - 405 = correct namespace, wrong HTTP method (endpoint exists)
|
|
356
|
+
* - 400/404 = wrong namespace or API not registered yet
|
|
350
357
|
*/
|
|
351
358
|
async function resolveFactoryNamespace(
|
|
352
359
|
client: any,
|
|
353
360
|
apiSysId: string,
|
|
354
361
|
instanceUrl: string
|
|
355
362
|
): Promise<string | null> {
|
|
356
|
-
// ──
|
|
363
|
+
// ── Collect namespace candidates (ordered by likelihood) ──
|
|
364
|
+
var candidates: string[] = [];
|
|
365
|
+
|
|
366
|
+
// Most common for PDI / global scope custom APIs
|
|
367
|
+
candidates.push('global');
|
|
368
|
+
|
|
369
|
+
// Dot-walk to sys_scope.scope
|
|
357
370
|
try {
|
|
358
371
|
var dotWalkResp = await client.get('/api/now/table/sys_ws_definition/' + apiSysId, {
|
|
359
372
|
params: {
|
|
360
|
-
sysparm_fields: 'namespace
|
|
373
|
+
sysparm_fields: 'namespace.scope',
|
|
361
374
|
sysparm_display_value: 'false'
|
|
362
375
|
}
|
|
363
376
|
});
|
|
364
377
|
var scopeStr = dotWalkResp.data.result?.['namespace.scope'];
|
|
365
|
-
if (scopeStr && typeof scopeStr === 'string' && scopeStr.length >
|
|
366
|
-
|
|
378
|
+
if (scopeStr && typeof scopeStr === 'string' && scopeStr.length > 1) {
|
|
379
|
+
candidates.push(scopeStr);
|
|
367
380
|
}
|
|
368
381
|
} catch (_) {}
|
|
369
382
|
|
|
370
|
-
//
|
|
383
|
+
// Read namespace display_value (might be scope string itself)
|
|
371
384
|
try {
|
|
372
385
|
var nsResp = await client.get('/api/now/table/sys_ws_definition/' + apiSysId, {
|
|
373
|
-
params: { sysparm_fields: 'namespace', sysparm_display_value: '
|
|
386
|
+
params: { sysparm_fields: 'namespace', sysparm_display_value: 'true' }
|
|
374
387
|
});
|
|
375
|
-
var
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
if (typeof ns === 'object') {
|
|
379
|
-
var dv = ns.display_value || '';
|
|
380
|
-
var val = ns.value || '';
|
|
381
|
-
// display_value might be the scope string itself (e.g. "global", "x_snflw")
|
|
382
|
-
if (dv && dv !== 'Global') return dv;
|
|
383
|
-
// If display_value is "Global", the scope string is typically "global"
|
|
384
|
-
if (dv === 'Global') return 'global';
|
|
385
|
-
if (val && val.length > 0) return val;
|
|
386
|
-
} else if (typeof ns === 'string' && ns.length > 0) {
|
|
387
|
-
return ns === 'Global' ? 'global' : ns;
|
|
388
|
-
}
|
|
388
|
+
var nsDisplay = nsResp.data.result?.namespace;
|
|
389
|
+
if (typeof nsDisplay === 'string' && nsDisplay.length > 1) {
|
|
390
|
+
candidates.push(nsDisplay === 'Global' ? 'global' : nsDisplay);
|
|
389
391
|
}
|
|
390
392
|
} catch (_) {}
|
|
391
393
|
|
|
392
|
-
//
|
|
394
|
+
// OOB namespace
|
|
395
|
+
candidates.push('now');
|
|
396
|
+
|
|
397
|
+
// Company code from sys_properties
|
|
393
398
|
try {
|
|
394
399
|
var compResp = await client.get('/api/now/table/sys_properties', {
|
|
395
400
|
params: {
|
|
@@ -399,27 +404,65 @@ async function resolveFactoryNamespace(
|
|
|
399
404
|
}
|
|
400
405
|
});
|
|
401
406
|
var companyCode = compResp.data.result?.[0]?.value;
|
|
402
|
-
if (companyCode)
|
|
407
|
+
if (companyCode) candidates.push(companyCode);
|
|
403
408
|
} catch (_) {}
|
|
404
409
|
|
|
405
|
-
//
|
|
406
|
-
var probeCandidates = ['global', 'now'];
|
|
410
|
+
// Instance subdomain (e.g. "dev354059")
|
|
407
411
|
try {
|
|
408
412
|
var match = instanceUrl.match(/https?:\/\/([^.]+)\./);
|
|
409
|
-
if (match && match[1])
|
|
413
|
+
if (match && match[1]) candidates.push(match[1]);
|
|
410
414
|
} catch (_) {}
|
|
411
415
|
|
|
412
|
-
|
|
416
|
+
// ── Deduplicate ──
|
|
417
|
+
var seen: Record<string, boolean> = {};
|
|
418
|
+
var unique: string[] = [];
|
|
419
|
+
for (var i = 0; i < candidates.length; i++) {
|
|
420
|
+
var lower = candidates[i].toLowerCase();
|
|
421
|
+
if (!seen[lower]) {
|
|
422
|
+
seen[lower] = true;
|
|
423
|
+
unique.push(lower);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// ── Verify each candidate via HTTP ──
|
|
428
|
+
for (var j = 0; j < unique.length; j++) {
|
|
429
|
+
var ns = unique[j];
|
|
430
|
+
// Check 1: GET /discover (v5 endpoint, returns 200 with discovery data)
|
|
413
431
|
try {
|
|
414
|
-
var
|
|
415
|
-
if (
|
|
416
|
-
} catch (
|
|
417
|
-
var
|
|
418
|
-
if (
|
|
432
|
+
var discoverResp = await client.get('/api/' + ns + '/' + FLOW_FACTORY_API_ID + '/discover');
|
|
433
|
+
if (discoverResp.status === 200 || discoverResp.data) return ns;
|
|
434
|
+
} catch (discoverErr: any) {
|
|
435
|
+
var ds = discoverErr.response?.status;
|
|
436
|
+
if (ds === 401 || ds === 403 || ds === 405) return ns;
|
|
437
|
+
// 400/404 = wrong namespace or not registered yet
|
|
438
|
+
}
|
|
439
|
+
// Check 2: GET /create (POST-only, expect 405 for correct namespace)
|
|
440
|
+
try {
|
|
441
|
+
await client.get('/api/' + ns + '/' + FLOW_FACTORY_API_ID + '/create');
|
|
442
|
+
return ns; // 200 = unexpected but valid
|
|
443
|
+
} catch (createErr: any) {
|
|
444
|
+
var cs = createErr.response?.status;
|
|
445
|
+
if (cs === 405 || cs === 401 || cs === 403) return ns;
|
|
446
|
+
// 400/404 = wrong namespace (ServiceNow may return 400 instead of 405)
|
|
447
|
+
}
|
|
448
|
+
// Check 3: POST /create with empty body — distinguishes "wrong namespace" from
|
|
449
|
+
// "correct namespace but script validation error". Wrong namespace returns
|
|
450
|
+
// 400 with "Requested URI does not represent any resource". Our script returns
|
|
451
|
+
// a different error body (e.g. {success:false, error:"..."}).
|
|
452
|
+
try {
|
|
453
|
+
await client.post('/api/' + ns + '/' + FLOW_FACTORY_API_ID + '/create', {});
|
|
454
|
+
return ns; // Unexpected success, namespace confirmed
|
|
455
|
+
} catch (postErr: any) {
|
|
456
|
+
var pe = postErr.response;
|
|
457
|
+
if (!pe) continue;
|
|
458
|
+
if (pe.status === 401 || pe.status === 403) return ns;
|
|
459
|
+
// Check error body — "Requested URI" = wrong namespace, anything else = our script
|
|
460
|
+
var errStr = JSON.stringify(pe.data || '');
|
|
461
|
+
if (!errStr.includes('Requested URI')) return ns;
|
|
419
462
|
}
|
|
420
463
|
}
|
|
421
464
|
|
|
422
|
-
return null;
|
|
465
|
+
return null; // No candidate verified — API may not be registered yet
|
|
423
466
|
}
|
|
424
467
|
|
|
425
468
|
/**
|
|
@@ -546,25 +589,19 @@ async function ensureFlowFactoryAPI(
|
|
|
546
589
|
// Non-fatal: create endpoint is more important than discover
|
|
547
590
|
}
|
|
548
591
|
|
|
549
|
-
// 6.
|
|
592
|
+
// 6. Wait for ServiceNow REST framework to register endpoints
|
|
593
|
+
await new Promise(resolve => setTimeout(resolve, 4000));
|
|
594
|
+
|
|
595
|
+
// 7. Resolve namespace via HTTP-verified probing
|
|
550
596
|
var resolvedNs = await resolveFactoryNamespace(client, apiSysId, instanceUrl);
|
|
551
597
|
|
|
552
|
-
//
|
|
598
|
+
// 8. If resolution failed, wait longer and retry (some instances are slow)
|
|
553
599
|
if (!resolvedNs) {
|
|
554
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
600
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
555
601
|
resolvedNs = await resolveFactoryNamespace(client, apiSysId, instanceUrl);
|
|
556
602
|
}
|
|
557
603
|
if (!resolvedNs) {
|
|
558
|
-
|
|
559
|
-
try {
|
|
560
|
-
var globalTest = await client.get('/api/global/' + FLOW_FACTORY_API_ID + '/discover');
|
|
561
|
-
if (globalTest.status === 200 || globalTest.data) resolvedNs = 'global';
|
|
562
|
-
} catch (gtErr: any) {
|
|
563
|
-
if (gtErr.response?.status === 401 || gtErr.response?.status === 403) resolvedNs = 'global';
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
if (!resolvedNs) {
|
|
567
|
-
throw new Error('Flow Factory deployed (sys_id=' + apiSysId + ') but namespace could not be resolved. This may indicate a ServiceNow scope/permissions issue.');
|
|
604
|
+
throw new Error('Flow Factory deployed (sys_id=' + apiSysId + ') but no namespace candidate could be verified via HTTP after 9s. Candidates tried: global, dot-walk scope, display_value, now, company code, subdomain.');
|
|
568
605
|
}
|
|
569
606
|
|
|
570
607
|
_flowFactoryCache = { apiSysId: apiSysId, namespace: resolvedNs, timestamp: Date.now() };
|