@jacebenson/jsn 4.0.3 → 4.0.4
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 +1 -1
- package/src/app.js +25 -3
- package/src/cli.js +3 -1
- package/src/commands/auth.js +116 -7
- package/src/commands/dev/domains.js +292 -0
- package/src/commands/dev/scopes.js +88 -36
- package/src/commands/dev/updatesets.js +297 -106
- package/src/commands/dev.js +16 -1
- package/src/commands/records.js +3 -2
- package/src/context.js +52 -5
- package/src/help.js +16 -2
- package/src/mutations.js +10 -1
- package/src/sdk.js +4 -0
package/src/commands/dev.js
CHANGED
|
@@ -15,6 +15,7 @@ import { formsCmd } from './dev/forms.js';
|
|
|
15
15
|
import { listsCmd } from './dev/lists.js';
|
|
16
16
|
import { updateSetsCmd } from './dev/updatesets.js';
|
|
17
17
|
import { scopesCmd } from './dev/scopes.js';
|
|
18
|
+
import { domainsCmd } from './dev/domains.js';
|
|
18
19
|
import { evalCmd } from './dev/eval.js';
|
|
19
20
|
import { restCmd } from './dev/rest.js';
|
|
20
21
|
import { logsCmd } from './dev/logs.js';
|
|
@@ -47,6 +48,7 @@ export function devCmd(wrap) {
|
|
|
47
48
|
.command(rolesCmd(wrap))
|
|
48
49
|
.command(updateSetsCmd(wrap))
|
|
49
50
|
.command(scopesCmd(wrap))
|
|
51
|
+
.command(domainsCmd(wrap))
|
|
50
52
|
.command(propertiesCmd(wrap))
|
|
51
53
|
.command(relationshipsCmd(wrap))
|
|
52
54
|
.command(appmodulesCmd(wrap))
|
|
@@ -61,6 +63,19 @@ export function devCmd(wrap) {
|
|
|
61
63
|
.command(evalCmd(wrap))
|
|
62
64
|
.command(restCmd(wrap));
|
|
63
65
|
},
|
|
64
|
-
handler: () => {
|
|
66
|
+
handler: (argv) => {
|
|
67
|
+
if (argv._[1]) return; // a subcommand ran — its own handler handled it
|
|
68
|
+
console.log('Manage ServiceNow development artifacts.');
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log('Most dev commands are also available at the top level:');
|
|
71
|
+
console.log(' flows, actions, rules, clientscripts, uiactions, uipolicies,');
|
|
72
|
+
console.log(' tables, columns, forms, lists, import, sppages, spwidgets,');
|
|
73
|
+
console.log(' uipages, appmenu, scrapi, acls, roles, properties,');
|
|
74
|
+
console.log(' relationships, appmodules, listcontrols, views, privileges,');
|
|
75
|
+
console.log(' uxscripts, aliases, catalogscripts, cataloguipolicies, logs,');
|
|
76
|
+
console.log(' updatesets, scopes, eval, rest');
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log('Run "jsn dev <command> --help" or "jsn <command> --help" for details.');
|
|
79
|
+
},
|
|
65
80
|
};
|
|
66
81
|
}
|
package/src/commands/records.js
CHANGED
|
@@ -21,7 +21,7 @@ function getDefaultColumns(table) {
|
|
|
21
21
|
|
|
22
22
|
export function recordsCmd(wrap) {
|
|
23
23
|
return {
|
|
24
|
-
command: 'records
|
|
24
|
+
command: 'records [subcommand]',
|
|
25
25
|
describe: 'Query and manage records in any table (e.g. "records list --table incident --query priority=1")',
|
|
26
26
|
builder: (yargs) => {
|
|
27
27
|
return yargs
|
|
@@ -208,7 +208,8 @@ export function recordsCmd(wrap) {
|
|
|
208
208
|
})
|
|
209
209
|
|
|
210
210
|
},
|
|
211
|
-
handler: () => {
|
|
211
|
+
handler: (argv) => {
|
|
212
|
+
if (argv._[1]) return; // a subcommand ran — its own handler handled it
|
|
212
213
|
console.log('Query and manage records in any ServiceNow table.');
|
|
213
214
|
console.log('');
|
|
214
215
|
console.log('Available subcommands:');
|
package/src/context.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// ServiceNow runtime context: current user, scope, update set
|
|
2
2
|
|
|
3
|
+
import { getStringField } from './helpers.js';
|
|
4
|
+
|
|
3
5
|
export async function getCurrentUser(sdk) {
|
|
4
6
|
const params = new URLSearchParams();
|
|
5
7
|
params.set('sysparm_query', 'user_name=javascript:gs.getUserName()');
|
|
@@ -22,24 +24,24 @@ export async function getCurrentApplication(sdk, userSysID) {
|
|
|
22
24
|
params.set('sysparm_limit', '1');
|
|
23
25
|
params.set('sysparm_fields', 'value');
|
|
24
26
|
const records = await sdk.list('sys_user_preference', params);
|
|
25
|
-
if (records.length === 0) return { scope: 'global' };
|
|
27
|
+
if (records.length === 0) return { scope: 'global', appSysId: '' };
|
|
26
28
|
const val = records[0].value?.value || records[0].value;
|
|
27
|
-
if (!val) return { scope: 'global' };
|
|
29
|
+
if (!val) return { scope: 'global', appSysId: '' };
|
|
28
30
|
|
|
29
31
|
// Try to resolve sys_id to scope name
|
|
30
32
|
try {
|
|
31
33
|
const app = await sdk.get('sys_scope', val);
|
|
32
|
-
if (app) return { scope: app.scope || 'global' };
|
|
34
|
+
if (app) return { scope: app.scope || 'global', appSysId: val };
|
|
33
35
|
} catch {
|
|
34
36
|
// fallback
|
|
35
37
|
}
|
|
36
38
|
try {
|
|
37
39
|
const app2 = await sdk.get('sys_app', val);
|
|
38
|
-
if (app2) return { scope: app2.scope || 'global' };
|
|
40
|
+
if (app2) return { scope: app2.scope || 'global', appSysId: val };
|
|
39
41
|
} catch {
|
|
40
42
|
// fallback
|
|
41
43
|
}
|
|
42
|
-
return { scope: val };
|
|
44
|
+
return { scope: val, appSysId: val };
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export async function getCurrentUpdateSet(sdk, userSysID) {
|
|
@@ -60,3 +62,48 @@ export async function getCurrentUpdateSet(sdk, userSysID) {
|
|
|
60
62
|
}
|
|
61
63
|
return { name: val, sys_id: val };
|
|
62
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Find-or-create a sys_user_preference and set its value. Shared by the
|
|
68
|
+
* scope/update-set "set" commands and create auto-set (was copy-pasted 3x).
|
|
69
|
+
*/
|
|
70
|
+
async function setUserPreference(sdk, userSysID, name, value) {
|
|
71
|
+
const params = new URLSearchParams();
|
|
72
|
+
params.set('sysparm_query', `user=${userSysID}^name=${name}`);
|
|
73
|
+
params.set('sysparm_limit', '1');
|
|
74
|
+
params.set('sysparm_fields', 'sys_id');
|
|
75
|
+
const prefs = await sdk.list('sys_user_preference', params);
|
|
76
|
+
if (prefs.length > 0) {
|
|
77
|
+
await sdk.update('sys_user_preference', getStringField(prefs[0], 'sys_id'), { value });
|
|
78
|
+
} else {
|
|
79
|
+
await sdk.create('sys_user_preference', {
|
|
80
|
+
user: userSysID,
|
|
81
|
+
name,
|
|
82
|
+
value,
|
|
83
|
+
type: 'string',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Resolve the current user's sys_id (throws if it can't be determined). */
|
|
89
|
+
export async function requireCurrentUserSysId(sdk) {
|
|
90
|
+
const user = await sdk.list('sys_user', new URLSearchParams({
|
|
91
|
+
sysparm_query: 'user_name=javascript:gs.getUserName()',
|
|
92
|
+
sysparm_limit: '1',
|
|
93
|
+
sysparm_fields: 'sys_id',
|
|
94
|
+
}));
|
|
95
|
+
if (user.length === 0) {
|
|
96
|
+
throw new Error('Could not determine current user');
|
|
97
|
+
}
|
|
98
|
+
return getStringField(user[0], 'sys_id');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Set the current application scope via the apps.current_app preference. */
|
|
102
|
+
export async function setCurrentApplication(sdk, userSysID, scopeSysID) {
|
|
103
|
+
await setUserPreference(sdk, userSysID, 'apps.current_app', scopeSysID);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Set the current update set via the sys_update_set preference. */
|
|
107
|
+
export async function setCurrentUpdateSet(sdk, userSysID, updateSetSysID) {
|
|
108
|
+
await setUserPreference(sdk, userSysID, 'sys_update_set', updateSetSysID);
|
|
109
|
+
}
|
package/src/help.js
CHANGED
|
@@ -111,6 +111,7 @@ const CONFIG_COMMANDS = [
|
|
|
111
111
|
{ name: 'auth', desc: 'Authentication (login, refresh, status)' },
|
|
112
112
|
{ name: 'updatesets', desc: 'Update sets (set, create, export)' },
|
|
113
113
|
{ name: 'scopes', desc: 'Application scopes' },
|
|
114
|
+
{ name: 'domains', desc: 'Domains (domain separation)' },
|
|
114
115
|
];
|
|
115
116
|
|
|
116
117
|
const DEVELOPER_COMMANDS = [
|
|
@@ -153,8 +154,21 @@ LEARN MORE
|
|
|
153
154
|
Use "jsn <command> --help" for more information about a command.`;
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
export function renderHelp() {
|
|
157
|
-
|
|
157
|
+
export function renderHelp(config) {
|
|
158
|
+
// Domain separation is an optional plugin — only advertise `domains`
|
|
159
|
+
// when the active profile's capability probe found it installed.
|
|
160
|
+
const activeName = config?.activeProfile || config?.defaultProfile;
|
|
161
|
+
const hasDS = !!(activeName && config?.profiles?.[activeName]?.domain_separation);
|
|
162
|
+
const configCmds = hasDS
|
|
163
|
+
? CONFIG_COMMANDS
|
|
164
|
+
: CONFIG_COMMANDS.filter((c) => !c.name || c.name !== 'domains');
|
|
165
|
+
|
|
166
|
+
const COMMAND_GROUPS_LOCAL = {
|
|
167
|
+
...COMMAND_GROUPS,
|
|
168
|
+
'CONFIGURATION': configCmds,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const sections = Object.entries(COMMAND_GROUPS_LOCAL).map(([name, cmds]) => renderGroup(name, cmds));
|
|
158
172
|
|
|
159
173
|
return [
|
|
160
174
|
'Usage: jsn <command> [options]',
|
package/src/mutations.js
CHANGED
|
@@ -75,11 +75,20 @@ export const MUTATION_COMMANDS = [
|
|
|
75
75
|
['scopes', 'set'],
|
|
76
76
|
['dev', 'scopes', 'create'],
|
|
77
77
|
['dev', 'scopes', 'set'],
|
|
78
|
-
//
|
|
78
|
+
// Domains: set writes the profile config (root + dev forms)
|
|
79
|
+
['domains', 'set'],
|
|
80
|
+
['dev', 'domains', 'set'],
|
|
81
|
+
// Update sets: create, set, complete, ignore, parent (root + dev forms)
|
|
79
82
|
['updatesets', 'create'],
|
|
80
83
|
['updatesets', 'set'],
|
|
84
|
+
['updatesets', 'complete'],
|
|
85
|
+
['updatesets', 'ignore'],
|
|
86
|
+
['updatesets', 'parent'],
|
|
81
87
|
['dev', 'updatesets', 'create'],
|
|
82
88
|
['dev', 'updatesets', 'set'],
|
|
89
|
+
['dev', 'updatesets', 'complete'],
|
|
90
|
+
['dev', 'updatesets', 'ignore'],
|
|
91
|
+
['dev', 'updatesets', 'parent'],
|
|
83
92
|
];
|
|
84
93
|
|
|
85
94
|
/**
|
package/src/sdk.js
CHANGED
|
@@ -11,6 +11,7 @@ export class SDKClient {
|
|
|
11
11
|
this.baseURL = baseURL.replace(/\/$/, '');
|
|
12
12
|
this.authProvider = authProvider;
|
|
13
13
|
this.timeout = opts.timeout || DEFAULT_TIMEOUT;
|
|
14
|
+
this.domain = opts.domain || '';
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
async _setAuth(req) {
|
|
@@ -53,6 +54,9 @@ export class SDKClient {
|
|
|
53
54
|
if (opts.body && typeof opts.body === 'string') {
|
|
54
55
|
req.headers.set('Content-Type', 'application/json');
|
|
55
56
|
}
|
|
57
|
+
if (this.domain) {
|
|
58
|
+
req.headers.set('X-Now-Domain', this.domain);
|
|
59
|
+
}
|
|
56
60
|
await this._setAuth(req);
|
|
57
61
|
|
|
58
62
|
const resp = await fetch(req);
|