@dynamicweb/cli 1.1.1 → 2.0.0-beta.0

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.
@@ -1,8 +1,12 @@
1
1
  import fetch from 'node-fetch';
2
- import { interactiveEnv, getAgent } from './env.js'
2
+ import { interactiveEnv, getAgent, isJsonOutput, createCommandError } from './env.js'
3
3
  import { updateConfig, getConfig } from './config.js';
4
4
  import { input, password } from '@inquirer/prompts';
5
5
 
6
+ const DEFAULT_OAUTH_TOKEN_PATH = '/Admin/OAuth/token';
7
+ const DEFAULT_CLIENT_ID_ENV = 'DW_CLIENT_ID';
8
+ const DEFAULT_CLIENT_SECRET_ENV = 'DW_CLIENT_SECRET';
9
+
6
10
  export function loginCommand() {
7
11
  return {
8
12
  command: 'login [user]',
@@ -12,8 +16,27 @@ export function loginCommand() {
12
16
  .positional('user', {
13
17
  describe: 'user'
14
18
  })
19
+ .option('oauth', {
20
+ type: 'boolean',
21
+ describe: 'Configures OAuth client_credentials authentication for the current environment'
22
+ })
23
+ .option('output', {
24
+ choices: ['json'],
25
+ describe: 'Outputs a single JSON response for automation-friendly parsing'
26
+ })
15
27
  },
16
- handler: (argv) => handleLogin(argv)
28
+ handler: async (argv) => {
29
+ const output = createLoginOutput(argv);
30
+
31
+ try {
32
+ await handleLogin(argv, output);
33
+ } catch (err) {
34
+ output.fail(err);
35
+ process.exitCode = 1;
36
+ } finally {
37
+ output.finish();
38
+ }
39
+ }
17
40
  }
18
41
  }
19
42
 
@@ -26,17 +49,24 @@ export async function setupUser(argv, env) {
26
49
  askLogin = false;
27
50
  }
28
51
 
52
+ if (!user.apiKey && shouldUseOAuth(argv, env)) {
53
+ return await authenticateWithOAuth(argv, env);
54
+ }
55
+
29
56
  if (!user.apiKey && env.users && (argv.user || env.current?.user)) {
30
57
  user = env.users[argv.user] || env.users[env.current?.user];
31
58
  askLogin = false;
32
59
  }
33
60
 
34
61
  if (askLogin && argv.host) {
35
- console.log('Please add an --apiKey to the command as overriding the host requires that.')
36
- process.exit();
62
+ throw createCommandError('Please add an --apiKey, or provide OAuth client credentials when overriding the host.');
37
63
  }
38
64
  else if (askLogin) {
39
- console.log('Current user not set, please login')
65
+ if (isJsonOutput(argv)) {
66
+ throw createCommandError('Current user not set, please login');
67
+ }
68
+
69
+ logMessage(argv, 'Current user not set, please login');
40
70
  await interactiveLogin(argv, {
41
71
  environment: {
42
72
  type: 'input',
@@ -59,27 +89,40 @@ export async function setupUser(argv, env) {
59
89
  return user;
60
90
  }
61
91
 
62
- async function handleLogin(argv) {
63
- argv.user ? changeUser(argv) : await interactiveLogin(argv, {
92
+ async function handleLogin(argv, output) {
93
+ if (shouldUseOAuth(argv, getCurrentEnv(argv))) {
94
+ if (isJsonOutput(argv)) {
95
+ output.addData(await nonInteractiveOAuthLogin(argv));
96
+ } else {
97
+ output.addData(await interactiveOAuthLogin(argv, output));
98
+ }
99
+ } else if (argv.user) {
100
+ output.addData(await changeUser(argv));
101
+ } else {
102
+ if (isJsonOutput(argv)) {
103
+ throw createCommandError('Interactive login is not supported with --output json. Use --apiKey, or configure OAuth with --oauth --clientIdEnv/--clientSecretEnv.');
104
+ }
105
+ output.addData(await interactiveLogin(argv, {
64
106
  environment: {
65
107
  type: 'input',
66
108
  default: getConfig()?.current?.env || 'dev',
67
109
  prompt: 'if-no-arg'
68
110
  },
69
- username: {
111
+ username: {
70
112
  type: 'input'
71
113
  },
72
- password: {
114
+ password: {
73
115
  type: 'password'
74
116
  },
75
117
  interactive: {
76
118
  default: true
77
119
  }
78
- })
120
+ }, output))
121
+ }
79
122
  }
80
123
 
81
- export async function interactiveLogin(argv, options) {
82
- if (argv.verbose) console.info('Now logging in')
124
+ export async function interactiveLogin(argv, options, output) {
125
+ verboseLog(argv, 'Now logging in');
83
126
  const result = {};
84
127
  for (const [key, config] of Object.entries(options)) {
85
128
  if (key === 'interactive') continue;
@@ -95,41 +138,57 @@ export async function interactiveLogin(argv, options) {
95
138
  });
96
139
  }
97
140
  if (!getConfig().env || !getConfig().env[result.environment] || !getConfig().env[result.environment].host || !getConfig().env[result.environment].protocol) {
98
- if (!argv.host)
99
- console.log(`The environment specified is missing parameters, please specify them`)
100
- await interactiveEnv(argv, {
101
- environment: {
102
- type: 'input',
103
- default: result.environment,
104
- prompt: 'never'
105
- },
106
- host: {
107
- describe: 'Enter your host including protocol, i.e "https://yourHost.com":',
108
- type: 'input',
109
- prompt: 'always'
110
- },
111
- interactive: {
112
- default: true
113
- }
114
- })
141
+ if (argv.host) {
142
+ ensureEnvironmentFromArgs(result.environment, argv);
143
+ } else {
144
+ logMessage(argv, `The environment specified is missing parameters, please specify them`);
145
+ await interactiveEnv(argv, {
146
+ environment: {
147
+ type: 'input',
148
+ default: result.environment,
149
+ prompt: 'never'
150
+ },
151
+ host: {
152
+ describe: 'Enter your host including protocol, i.e "https://yourHost.com":',
153
+ type: 'input',
154
+ prompt: 'always'
155
+ },
156
+ interactive: {
157
+ default: true
158
+ }
159
+ }, output)
160
+ }
115
161
  }
116
- await loginInteractive(result, argv.verbose);
162
+ return await loginInteractive(result, argv.verbose, argv);
117
163
  }
118
164
 
119
- async function loginInteractive(result, verbose) {
165
+ async function loginInteractive(result, verbose, argv) {
120
166
  var protocol = getConfig().env[result.environment].protocol;
121
167
  var token = await login(result.username, result.password, result.environment, protocol, verbose);
122
- if (!token) return;
168
+ if (!token) {
169
+ throw createCommandError(`Could not fetch a login token for user ${result.username}.`);
170
+ }
123
171
  var apiKey = await getApiKey(token, result.environment, protocol, verbose)
124
- if (!apiKey) return;
172
+ if (!apiKey) {
173
+ throw createCommandError(`Could not create an API Key for the logged in user ${result.username}.`);
174
+ }
125
175
  getConfig().env = getConfig().env || {};
126
176
  getConfig().env[result.environment].users = getConfig().env[result.environment].users || {};
127
177
  getConfig().env[result.environment].users[result.username] = getConfig().env[result.environment].users[result.username] || {};
128
178
  getConfig().env[result.environment].users[result.username].apiKey = apiKey;
129
179
  getConfig().env[result.environment].current = getConfig().env[result.environment].current || {};
130
180
  getConfig().env[result.environment].current.user = result.username;
131
- console.log("You're now logged in as " + result.username)
181
+ getConfig().env[result.environment].current.authType = 'user';
182
+ logMessage(argv, "You're now logged in as " + result.username);
132
183
  updateConfig();
184
+
185
+ return {
186
+ environment: result.environment,
187
+ username: result.username,
188
+ apiKey,
189
+ host: getConfig().env[result.environment].host,
190
+ protocol
191
+ };
133
192
  }
134
193
 
135
194
  async function login(username, password, env, protocol, verbose) {
@@ -153,14 +212,13 @@ async function login(username, password, env, protocol, verbose) {
153
212
  }
154
213
  else {
155
214
  if (verbose) console.info(res)
156
- console.log(`Login attempt failed with username ${username}, please verify its a valid user in your Dynamicweb solution.`)
215
+ throw createCommandError(`Login attempt failed with username ${username}, please verify its a valid user in your Dynamicweb solution.`, res.status)
157
216
  }
158
217
  }
159
218
 
160
219
  function parseCookies (cookieHeader) {
161
220
  const list = {};
162
221
  if (!cookieHeader) {
163
- console.log(`Could not get the necessary information from the login request, please verify its a valid user in your Dynamicweb solution.`)
164
222
  return list;
165
223
  }
166
224
 
@@ -173,10 +231,6 @@ function parseCookies (cookieHeader) {
173
231
  list[name] = decodeURIComponent(value);
174
232
  });
175
233
 
176
- if (!list.user) {
177
- console.log(`Could not get the necessary information from the login request, please verify its a valid user in your Dynamicweb solution.`)
178
- }
179
-
180
234
  return list;
181
235
  }
182
236
 
@@ -193,7 +247,7 @@ async function getToken(user, env, protocol, verbose) {
193
247
  }
194
248
  else {
195
249
  if (verbose) console.info(res)
196
- console.log(`Could not fetch the token for the logged in user ${user}, please verify its a valid user in your Dynamicweb solution.`)
250
+ throw createCommandError(`Could not fetch the token for the logged in user ${user}, please verify its a valid user in your Dynamicweb solution.`, res.status)
197
251
  }
198
252
  }
199
253
 
@@ -218,12 +272,297 @@ async function getApiKey(token, env, protocol, verbose) {
218
272
  }
219
273
  else {
220
274
  if (verbose) console.info(res)
221
- console.log(`Could not create an API Key for the logged in user, please verify its a valid user in your Dynamicweb solution.`)
275
+ throw createCommandError(`Could not create an API Key for the logged in user, please verify its a valid user in your Dynamicweb solution.`, res.status)
222
276
  }
223
277
  }
224
278
 
225
279
  async function changeUser(argv) {
280
+ if (!getConfig().current?.env || !getConfig().env?.[getConfig().current.env]) {
281
+ throw createCommandError('Current environment not set, please set it before changing user.');
282
+ }
283
+
284
+ getConfig().env[getConfig().current.env].current = getConfig().env[getConfig().current.env].current || {};
226
285
  getConfig().env[getConfig().current.env].current.user = argv.user;
286
+ getConfig().env[getConfig().current.env].current.authType = 'user';
227
287
  updateConfig();
228
- console.log(`You're now logged in as ${getConfig().env[getConfig().current.env].current.user}`);
229
- }
288
+ logMessage(argv, `You're now logged in as ${getConfig().env[getConfig().current.env].current.user}`);
289
+
290
+ return {
291
+ environment: getConfig().current.env,
292
+ username: getConfig().env[getConfig().current.env].current.user
293
+ };
294
+ }
295
+
296
+ async function interactiveOAuthLogin(argv, output) {
297
+ verboseLog(argv, 'Configuring OAuth client credentials authentication');
298
+
299
+ const currentEnvName = getConfig()?.current?.env || 'dev';
300
+ const environment = await input({
301
+ message: 'environment',
302
+ default: currentEnvName
303
+ });
304
+ const existingEnv = getConfig()?.env?.[environment] || {};
305
+ const existingAuth = existingEnv.auth || {};
306
+
307
+ const result = {
308
+ environment,
309
+ clientIdEnv: argv.clientIdEnv || existingAuth.clientIdEnv || DEFAULT_CLIENT_ID_ENV,
310
+ clientSecretEnv: argv.clientSecretEnv || existingAuth.clientSecretEnv || DEFAULT_CLIENT_SECRET_ENV
311
+ };
312
+
313
+ if (!argv.clientIdEnv) {
314
+ result.clientIdEnv = await input({
315
+ message: 'clientIdEnv',
316
+ default: result.clientIdEnv
317
+ });
318
+ }
319
+
320
+ if (!argv.clientSecretEnv) {
321
+ result.clientSecretEnv = await input({
322
+ message: 'clientSecretEnv',
323
+ default: result.clientSecretEnv
324
+ });
325
+ }
326
+
327
+ if (argv.host) {
328
+ ensureEnvironmentFromArgs(result.environment, argv);
329
+ } else if (!getConfig().env || !getConfig().env[result.environment] || !getConfig().env[result.environment].host || !getConfig().env[result.environment].protocol) {
330
+ logMessage(argv, 'The environment specified is missing parameters, please specify them');
331
+ await interactiveEnv(argv, {
332
+ environment: {
333
+ type: 'input',
334
+ default: result.environment,
335
+ prompt: 'never'
336
+ },
337
+ host: {
338
+ describe: 'Enter your host including protocol, i.e "https://yourHost.com":',
339
+ type: 'input',
340
+ prompt: 'always'
341
+ },
342
+ interactive: {
343
+ default: true
344
+ }
345
+ }, output);
346
+ }
347
+
348
+ const oauthResult = await finalizeOAuthLogin(result.environment, result.clientIdEnv, result.clientSecretEnv, argv);
349
+
350
+ logMessage(argv, `OAuth authentication is now configured for ${result.environment}`);
351
+
352
+ return oauthResult;
353
+ }
354
+
355
+ async function nonInteractiveOAuthLogin(argv) {
356
+ verboseLog(argv, 'Configuring OAuth client credentials authentication (non-interactive)');
357
+
358
+ const environment = getConfig()?.current?.env;
359
+ if (!environment) {
360
+ throw createCommandError('No environment set. Configure one with "dw env" first.');
361
+ }
362
+
363
+ if (argv.host) {
364
+ ensureEnvironmentFromArgs(environment, argv);
365
+ } else if (!getConfig().env?.[environment]?.host) {
366
+ throw createCommandError(`Environment "${environment}" has no host configured. Pass --host or set it up with "dw env" first.`);
367
+ }
368
+
369
+ const clientIdEnv = argv.clientIdEnv || getConfig().env?.[environment]?.auth?.clientIdEnv || DEFAULT_CLIENT_ID_ENV;
370
+ const clientSecretEnv = argv.clientSecretEnv || getConfig().env?.[environment]?.auth?.clientSecretEnv || DEFAULT_CLIENT_SECRET_ENV;
371
+
372
+ return await finalizeOAuthLogin(environment, clientIdEnv, clientSecretEnv, argv);
373
+ }
374
+
375
+ async function finalizeOAuthLogin(environment, clientIdEnv, clientSecretEnv, argv) {
376
+ const env = getConfig().env[environment];
377
+ const oauthConfig = resolveOAuthConfig({
378
+ ...argv,
379
+ clientIdEnv,
380
+ clientSecretEnv,
381
+ oauth: true
382
+ }, env);
383
+
384
+ const tokenResult = await fetchOAuthToken(env, oauthConfig, argv.verbose);
385
+
386
+ getConfig().current = getConfig().current || {};
387
+ getConfig().current.env = environment;
388
+ env.auth = {
389
+ type: 'oauth_client_credentials',
390
+ clientIdEnv,
391
+ clientSecretEnv
392
+ };
393
+ env.current = env.current || {};
394
+ env.current.authType = 'oauth_client_credentials';
395
+ delete env.current.user;
396
+ updateConfig();
397
+
398
+ return {
399
+ environment,
400
+ authType: 'oauth_client_credentials',
401
+ clientIdEnv,
402
+ clientSecretEnv,
403
+ expires: tokenResult.expires || null
404
+ };
405
+ }
406
+
407
+ async function authenticateWithOAuth(argv, env) {
408
+ const oauthConfig = resolveOAuthConfig(argv, env, true);
409
+ const tokenResult = await fetchOAuthToken(env, oauthConfig, argv.verbose);
410
+
411
+ return {
412
+ apiKey: tokenResult.token,
413
+ authType: 'oauth_client_credentials',
414
+ expires: tokenResult.expires || null
415
+ };
416
+ }
417
+
418
+ function shouldUseOAuth(argv, env = {}) {
419
+ if (argv.auth === 'user') {
420
+ return false;
421
+ }
422
+
423
+ if (argv.oauth || argv.auth === 'oauth') {
424
+ return true;
425
+ }
426
+
427
+ if (argv.clientId || argv.clientSecret || argv.clientIdEnv || argv.clientSecretEnv) {
428
+ return true;
429
+ }
430
+
431
+ if (env?.current?.authType) {
432
+ return env.current.authType === 'oauth_client_credentials';
433
+ }
434
+
435
+ return env?.auth?.type === 'oauth_client_credentials';
436
+ }
437
+
438
+ function resolveOAuthConfig(argv, env = {}, requireCredentials = true) {
439
+ const authConfig = env?.auth || {};
440
+ const clientIdEnv = argv.clientIdEnv || authConfig.clientIdEnv || DEFAULT_CLIENT_ID_ENV;
441
+ const clientSecretEnv = argv.clientSecretEnv || authConfig.clientSecretEnv || DEFAULT_CLIENT_SECRET_ENV;
442
+ const clientId = argv.clientId || process.env[clientIdEnv];
443
+ const clientSecret = argv.clientSecret || process.env[clientSecretEnv];
444
+
445
+ if (requireCredentials) {
446
+ if (!clientId) {
447
+ throw createCommandError(`OAuth client ID not found. Set --clientId or export ${clientIdEnv}.`);
448
+ }
449
+
450
+ if (!clientSecret) {
451
+ throw createCommandError(`OAuth client secret not found. Set --clientSecret or export ${clientSecretEnv}.`);
452
+ }
453
+ }
454
+
455
+ return {
456
+ clientId,
457
+ clientSecret,
458
+ clientIdEnv,
459
+ clientSecretEnv
460
+ };
461
+ }
462
+
463
+ async function fetchOAuthToken(env, oauthConfig, verbose) {
464
+ const res = await fetch(`${env.protocol}://${env.host}${DEFAULT_OAUTH_TOKEN_PATH}`, {
465
+ method: 'POST',
466
+ headers: {
467
+ 'Content-Type': 'application/json'
468
+ },
469
+ body: JSON.stringify({
470
+ grant_type: 'client_credentials',
471
+ client_id: oauthConfig.clientId,
472
+ client_secret: oauthConfig.clientSecret
473
+ }),
474
+ agent: getAgent(env.protocol)
475
+ });
476
+
477
+ const body = await parseJsonSafe(res);
478
+
479
+ if (!res.ok) {
480
+ if (verbose) {
481
+ console.info(res);
482
+ }
483
+
484
+ throw createCommandError(`OAuth token request failed at ${DEFAULT_OAUTH_TOKEN_PATH}.`, res.status, body);
485
+ }
486
+
487
+ const token = body?.token || body?.Token;
488
+ const expires = body?.expires || body?.Expires || null;
489
+
490
+ if (!token) {
491
+ throw createCommandError('OAuth token response did not include a token.', res.status, body);
492
+ }
493
+
494
+ return { token, expires };
495
+ }
496
+
497
+ function getCurrentEnv(argv) {
498
+ if (argv.host) {
499
+ return {
500
+ host: argv.host,
501
+ protocol: argv.protocol || 'https'
502
+ };
503
+ }
504
+
505
+ return getConfig()?.env?.[getConfig()?.current?.env] || {};
506
+ }
507
+
508
+ function ensureEnvironmentFromArgs(environment, argv) {
509
+ getConfig().env = getConfig().env || {};
510
+ getConfig().env[environment] = getConfig().env[environment] || {};
511
+ getConfig().env[environment].host = argv.host;
512
+ getConfig().env[environment].protocol = argv.protocol || 'https';
513
+ getConfig().current = getConfig().current || {};
514
+ getConfig().current.env = environment;
515
+ updateConfig();
516
+ }
517
+
518
+ function logMessage(argv, ...args) {
519
+ if (!isJsonOutput(argv)) {
520
+ console.log(...args);
521
+ }
522
+ }
523
+
524
+ function verboseLog(argv, ...args) {
525
+ if (argv?.verbose && !isJsonOutput(argv)) {
526
+ console.info(...args);
527
+ }
528
+ }
529
+
530
+ function createLoginOutput(argv) {
531
+ const response = {
532
+ ok: true,
533
+ command: 'login',
534
+ operation: shouldUseOAuth(argv, getCurrentEnv(argv)) ? 'oauth-login' : argv.user ? 'select-user' : 'login',
535
+ status: 200,
536
+ data: [],
537
+ errors: [],
538
+ meta: {}
539
+ };
540
+
541
+ return {
542
+ json: isJsonOutput(argv),
543
+ addData(entry) {
544
+ response.data.push(entry);
545
+ },
546
+ fail(err) {
547
+ response.ok = false;
548
+ response.status = err?.status || 1;
549
+ response.errors.push({
550
+ message: err?.message || 'Unknown login command error.',
551
+ details: err?.details ?? null
552
+ });
553
+ },
554
+ finish() {
555
+ if (this.json) {
556
+ console.log(JSON.stringify(response, null, 2));
557
+ }
558
+ }
559
+ };
560
+ }
561
+
562
+ async function parseJsonSafe(res) {
563
+ try {
564
+ return await res.json();
565
+ } catch {
566
+ return null;
567
+ }
568
+ }
@@ -1,9 +1,9 @@
1
1
  import fetch from 'node-fetch';
2
- import { setupEnv, getAgent } from './env.js';
2
+ import { setupEnv, getAgent, createCommandError } from './env.js';
3
3
  import { setupUser } from './login.js';
4
4
  import { input } from '@inquirer/prompts';
5
5
 
6
- const exclude = ['_', '$0', 'query', 'list', 'i', 'l', 'interactive', 'verbose', 'v', 'host', 'protocol', 'apiKey', 'env', 'output']
6
+ const exclude = ['_', '$0', 'query', 'list', 'i', 'l', 'interactive', 'verbose', 'v', 'host', 'protocol', 'apiKey', 'env', 'output', 'auth', 'clientId', 'clientSecret', 'clientIdEnv', 'clientSecretEnv', 'oauth']
7
7
 
8
8
  export function queryCommand() {
9
9
  return {
@@ -24,7 +24,8 @@ export function queryCommand() {
24
24
  })
25
25
  .option('output', {
26
26
  choices: ['json'],
27
- describe: 'Outputs a single JSON response for automation-friendly parsing'
27
+ describe: 'Outputs a single JSON response for automation-friendly parsing',
28
+ conflicts: 'interactive'
28
29
  })
29
30
  },
30
31
  handler: async (argv) => {
@@ -33,32 +34,35 @@ export function queryCommand() {
33
34
  try {
34
35
  output.verboseLog(`Running query ${argv.query}`);
35
36
  await handleQuery(argv, output);
36
- output.finish();
37
37
  } catch (err) {
38
38
  output.fail(err);
39
+ if (!output.json) {
40
+ console.error(err.stack || err.message || String(err));
41
+ }
42
+ process.exitCode = 1;
43
+ } finally {
39
44
  output.finish();
40
- process.exit(1);
41
45
  }
42
46
  }
43
47
  }
44
48
  }
45
49
 
46
50
  async function handleQuery(argv, output) {
47
- let env = await setupEnv(argv);
51
+ let env = await setupEnv(argv, output);
48
52
  let user = await setupUser(argv, env);
49
53
  if (argv.list) {
50
54
  const properties = await getProperties(env, user, argv.query);
51
55
  output.addData(properties);
52
56
  output.log(properties);
53
57
  } else {
54
- let response = await runQuery(env, user, argv.query, await getQueryParams(argv));
58
+ let response = await runQuery(env, user, argv.query, await getQueryParams(env, user, argv, output));
55
59
  output.addData(response);
56
60
  output.log(response);
57
61
  }
58
62
  }
59
63
 
60
64
  async function getProperties(env, user, query) {
61
- let res = await fetch(`${env.protocol}://${env.host}/Admin/Api/QueryByName?name=${query}`, {
65
+ let res = await fetch(`${env.protocol}://${env.host}/Admin/Api/QueryByName?name=${encodeURIComponent(query)}`, {
62
66
  method: 'GET',
63
67
  headers: {
64
68
  'Authorization': `Bearer ${user.apiKey}`
@@ -67,7 +71,7 @@ async function getProperties(env, user, query) {
67
71
  })
68
72
  if (res.ok) {
69
73
  let body = await res.json()
70
- if (body.model.properties.groups === undefined) {
74
+ if (body?.model?.properties?.groups === undefined) {
71
75
  throw createCommandError('Unable to fetch query parameters.', res.status, body);
72
76
  }
73
77
  return body.model.properties.groups.filter(g => g.name === 'Properties')[0].fields.map(field => `${field.name} (${field.typeName})`)
@@ -76,14 +80,12 @@ async function getProperties(env, user, query) {
76
80
  throw createCommandError('Unable to fetch query parameters.', res.status, await parseJsonSafe(res));
77
81
  }
78
82
 
79
- async function getQueryParams(argv) {
83
+ async function getQueryParams(env, user, argv, output) {
80
84
  let params = {}
81
85
  if (argv.interactive) {
82
- let env = await setupEnv(argv);
83
- let user = await setupUser(argv, env);
84
86
  let properties = await getProperties(env, user, argv.query);
85
- console.log('The following properties will be requested:')
86
- console.log(properties)
87
+ output.log('The following properties will be requested:')
88
+ output.log(properties)
87
89
  for (const p of properties) {
88
90
  const value = await input({ message: p });
89
91
  if (value) {
@@ -156,12 +158,6 @@ function createQueryOutput(argv) {
156
158
  };
157
159
  }
158
160
 
159
- function createCommandError(message, status, details = null) {
160
- const error = new Error(message);
161
- error.status = status;
162
- error.details = details;
163
- return error;
164
- }
165
161
 
166
162
  async function parseJsonSafe(res) {
167
163
  try {