@sap/cds-dk 6.1.1 → 6.1.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/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ This project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
8
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
9
9
 
10
+ ## Version 6.1.4 - 2022-09-09
11
+
12
+ ### Fixed
13
+
14
+ - `cds build` now works again with `@sap/cds` version 4. Previously, this silently failed with no output. However, a warning is now emitted that strongly recommends an upgrade to version 6.
15
+
16
+ ## Version 6.1.3 - 2022-09-06
17
+
18
+ ### Fixed
19
+
20
+ - `cds extend` and other commands no longer fail with `TypeError: Class constructor CliError cannot be invoked without 'new'`
21
+
22
+ ## Version 6.1.2 - 2022-08-25
23
+
24
+ ### Changed
25
+ - `cds init` uses latest Maven Java archetype version 1.27.1 for creating Java projects.
26
+
27
+ ### Fixed
28
+ - `cds add helm` fixed JSON Schema for `srv` property in values.yaml
29
+ - `cds add helm` fixed env property errors for `hana_deployer` and `html5_apps_deployer`
30
+ - `cds add data` now creates proper data file names for entites in a `context`, i.e. `sap.common-Countries.csv` instead of `sap-common-Countries.csv`
31
+ - `cds import` now captures the parameter/property of collection type differently and the associated annotations are pulled out of the `items` object of the parameter/property entry in the csn.
32
+ - Remove redundant `console.log()` statement in `cds lint`
33
+ - `cds push` now shows complete error messages from extension validation
34
+ - `cds push` and related commands now show properly formatted error messages and also fail with a non-zero exit code in error situations
35
+
10
36
  ## Version 6.1.1 - 2022-08-11
11
37
 
12
38
  ### Added
package/bin/build/all.js CHANGED
@@ -65,9 +65,15 @@ function build([project], options = {}) {
65
65
  delete options["options-model"]
66
66
  }
67
67
 
68
- try {
69
- return require('../../lib/cds').build(options)
70
- } catch (error) {
71
- return Promise.reject(error.message)
68
+ const cds = require('../../lib/cds')
69
+ if (typeof cds.build.build === 'function') {
70
+ console.warn(`
71
+ > WARNING: This application uses a very old version ${cds.version} of package '@sap/cds',
72
+ > which will no longer be supported with the next major version of '@sap/cds-dk'.
73
+ > We strongly recommend an upgrade to latest version 6 of '@sap/cds'.
74
+ `)
75
+ return cds.build.build(options)
72
76
  }
77
+ return cds.build(options)
78
+
73
79
  }
@@ -283,7 +283,7 @@ function _log (messages, options) {
283
283
  const { sortMessagesSeverityAware, deduplicateMessages } = cds.compiler
284
284
  deduplicateMessages(messages)
285
285
  messages = sortMessagesSeverityAware (messages)
286
- cds.utils._log (messages, options)
286
+ cds._log (messages, options)
287
287
  }
288
288
 
289
289
  /* eslint no-console:0 */
package/bin/login.js CHANGED
@@ -1,8 +1,8 @@
1
1
  const { bold } = require('../lib/util/term');
2
2
 
3
3
  module.exports = Object.assign(login, {
4
- options: ['--subdomain', '--directory', '--passcode', '--clientid'],
5
- shortcuts: ['-s', '-d', '-p', '-c'],
4
+ options: ['--subdomain', '--directory', '--passcode', '--user', '--clientid'],
5
+ shortcuts: ['-s', '-d', '-p', '-u', '-c'],
6
6
  flags: ['--plain'],
7
7
  get help() {
8
8
  const config = require('../lib/client/settings_manager').SettingsManager.config
@@ -71,10 +71,16 @@ module.exports = Object.assign(login, {
71
71
 
72
72
  Tenant-specific passcode for authentication.
73
73
 
74
- *-u* | *--username* <name>
74
+ *-u* | *--user* <name>[:[<password>]]
75
75
 
76
- Username for authentication with Basic Auth in test scenarios. For security reasons,
77
- username will only be saved for app URLs that look like localhost.
76
+ Username and optionally password for authentication with Basic Auth in test scenarios.
77
+ See https://cap.cloud.sap/docs/guides/multitenancy/#send-subscribe-events for information
78
+ on suitable mock users.
79
+
80
+ When typing the colon but omitting <password>, the password is assumed empty.
81
+
82
+ For security reasons, username will only be saved for app URLs that look like localhost.
83
+ The same is true for an empty password. Non-empty passwords will never be saved.
78
84
 
79
85
  *-c* | *--clientid* <clientid>
80
86
 
@@ -105,8 +111,12 @@ module.exports = Object.assign(login, {
105
111
 
106
112
 
107
113
  async function login ([url], options = {}) {
114
+ const [username, password] = options.user?.split(':') ?? [];
115
+ delete options.user;
108
116
  Object.assign(options, {
109
117
  url,
118
+ username,
119
+ password,
110
120
  tokenStorage: options.plain ? 'plain' : 'keyring',
111
121
  renewLogin: true,
112
122
  saveData: true
package/bin/subscribe.js CHANGED
@@ -36,7 +36,7 @@ async function subscribe([tenant], { to: url, user } = {}) {
36
36
  if (!tenant) throw new Error('Must provide a tenant')
37
37
  if (!url) throw new Error('Must provide an app URL via `--to`') // for now, if we don't infer it from the project
38
38
  if (typeof user !== 'string') throw new Error('Must provide a username')
39
- const [, username, , password = ''] = /([^:]+)(:(.*))?/.exec(user);
39
+ const [username, password = ''] = user.split(':');
40
40
  Logger.log(`Subscribing to ${url} for tenant '${tenant}' with user '${username}'${password === '' ? ' and empty password' : ''}`);
41
41
  const options = { auth: { username, password } };
42
42
  return require('axios').post (url+'/-/cds/deployment/subscribe', { tenant }, options);
package/lib/cds.js CHANGED
@@ -1,9 +1,8 @@
1
- /** @type { import('@sap/cds/apis/cds') } */
1
+ /** @type { import('@sap/cds/lib') } */
2
2
  const cds = module.exports = require(_local ('@sap/cds'))
3
3
 
4
4
  // for internal use in cds-dk only
5
- cds.utils._log = require(_local ('@sap/cds/bin/utils/log'))
6
-
5
+ cds._log = require(_local ('@sap/cds/bin/utils/log'))
7
6
 
8
7
  function _local (id) {
9
8
  return require.resolve (id, {paths:[
@@ -6,6 +6,7 @@ const AuthManager = require('./auth_manager');
6
6
  const BaseCommand = require('./base_command');
7
7
  const { collectFileContent, collectFiles, toUnixPath } = require('./helper/fs_helper');
8
8
  const Logger = require('./helper/logging');
9
+ const { CliError } = require('./helper/errors');
9
10
 
10
11
  const COMMAND_URL_SUFFIX = 'asyncActivate';
11
12
  const COMMAND_URL_SUFFIX_SYNC = 'activate';
@@ -27,7 +28,7 @@ async function checkFinished(jobId, statusUrl, params, finisher) {
27
28
  finisher.finished = true;
28
29
  clearInterval(finisher.timer);
29
30
  if (status === JOB_STATUS.FAILED) {
30
- return finisher.reject(new Error(`Job ${jobId} failed: ${error.message || JSON.stringify(error)}`));
31
+ return finisher.reject(new CliError(`Job ${jobId} failed: ${error.message || JSON.stringify(error)}`));
31
32
  }
32
33
  Logger.log('Extension active');
33
34
  finisher.resolve();
@@ -55,17 +56,17 @@ module.exports = class Activate extends BaseCommand {
55
56
  Logger.log(`Activating extension located at ${params.get('projectFolder')} to URL ${params.get('appUrl')}`);
56
57
  }
57
58
 
58
- if (!await Activate.checkProject(params.get('projectFolder'))) {
59
- throw new Error(params.get('projectFolder') + ' is not a valid extension project');
59
+ if (!await this.checkProject(params.get('projectFolder'))) {
60
+ throw new CliError(params.get('projectFolder') + ' is not a valid extension project');
60
61
  }
61
62
 
62
63
  try {
63
- await Activate.runActivate(injection.cds, params);
64
+ await this.runActivate(injection.cds, params);
64
65
  if (params.get('wsdl')) {
65
- await Activate.runCustomFileUpload(params);
66
+ await this.runCustomFileUpload(params);
66
67
  }
67
68
  } catch (error) {
68
- Activate.handleHttpError(error, params);
69
+ await this.handleHttpError(error, params);
69
70
  }
70
71
  } catch (error) {
71
72
  Logger.error(undefined, error);
@@ -153,7 +154,7 @@ module.exports = class Activate extends BaseCommand {
153
154
  return wsdlFiles;
154
155
  } catch (error) {
155
156
  Logger.error(undefined, error);
156
- throw new Error('No custom files found');
157
+ throw new CliError('No custom files found');
157
158
  }
158
159
  }
159
160
 
@@ -161,7 +162,7 @@ module.exports = class Activate extends BaseCommand {
161
162
  let activationUrl;
162
163
  const isSync = params.get('sync');
163
164
 
164
- return Activate.collectExtensionFiles(cds, params.get('projectFolder'))
165
+ return this.collectExtensionFiles(cds, params.get('projectFolder'))
165
166
  .then(allFiles => {
166
167
  if (allFiles.size === 0) {
167
168
  return null;
@@ -169,8 +170,8 @@ module.exports = class Activate extends BaseCommand {
169
170
 
170
171
  Logger.log('Activating extension...');
171
172
 
172
- const urlSuffix = isSync ? Activate.COMMAND_URL_SUFFIX_SYNC : Activate.COMMAND_URL_SUFFIX;
173
- activationUrl = Activate.getMtxApiUrl(params.get('appUrl'), urlSuffix);
173
+ const urlSuffix = isSync ? this.COMMAND_URL_SUFFIX_SYNC : this.COMMAND_URL_SUFFIX;
174
+ activationUrl = this.getMtxApiUrl(params.get('appUrl'), urlSuffix);
174
175
  const requestOptions = Object.assign({
175
176
  headers: {}
176
177
  }, params.get('reqAuth'));
@@ -189,17 +190,17 @@ module.exports = class Activate extends BaseCommand {
189
190
  const { jobID: jobId, statusPath } = response.data;
190
191
 
191
192
  if (jobId === undefined) {
192
- throw new Error('Activation failed: did not receive job ID');
193
+ throw new CliError('Activation failed: did not receive job ID');
193
194
  }
194
195
 
195
196
  if (!statusPath) {
196
- throw new Error(`Job ${jobId} failed: did not receive job-status path`);
197
+ throw new CliError(`Job ${jobId} failed: did not receive job-status path`);
197
198
  }
198
199
 
199
200
  Logger.debug(`Job ID: ${jobId}`);
200
201
 
201
202
  return new Promise((resolve, reject) => {
202
- const statusUrl = Activate.concatUrls(params.get('appUrl'), statusPath);
203
+ const statusUrl = this.concatUrls(params.get('appUrl'), statusPath);
203
204
  const finisher = {
204
205
  resolve,
205
206
  reject,
@@ -214,7 +215,7 @@ module.exports = class Activate extends BaseCommand {
214
215
  static async runCustomFileUpload(params) {
215
216
  Logger.log(`Uploading custom files to ${params.get('appUrl')}...`);
216
217
 
217
- const customFiles = Activate.collectCustomFiles(params.get('projectFolder'));
218
+ const customFiles = this.collectCustomFiles(params.get('projectFolder'));
218
219
 
219
220
  const customFilesSerialized = [];
220
221
  customFiles.forEach((value, key) => {
@@ -225,7 +226,7 @@ module.exports = class Activate extends BaseCommand {
225
226
  });
226
227
  });
227
228
 
228
- const customFileUrl = Activate.getMtxApiUrl(params.get('appUrl'), Activate.UPDATE_CUSTOM_FILE_URL_SUFFIX);
229
+ const customFileUrl = this.getMtxApiUrl(params.get('appUrl'), this.UPDATE_CUSTOM_FILE_URL_SUFFIX);
229
230
  const requestOptions = Object.assign({
230
231
  headers: {},
231
232
  }, params.get('reqAuth'));
@@ -4,6 +4,7 @@ const axios = require('axios');
4
4
  const Logger = require('./helper/logging');
5
5
  const { SettingsManager } = require('./settings_manager');
6
6
  const { ParamCollection } = require('./params');
7
+ const { CliError } = require('./helper/errors');
7
8
 
8
9
  function addTokenInfo(params, authData) {
9
10
  params.set('token', authData.access_token);
@@ -60,7 +61,7 @@ async function retrieveTokenOrPasscodeUrl(params) {
60
61
  try {
61
62
  response = await axios.get(tokenUrl);
62
63
  } catch (error) {
63
- throw new Error(`Failed to get authentication token: ${error}.\nResponse body: ${util.inspect(error.response && error.response.data)}`);
64
+ throw new CliError(`Failed to get authentication token: ${error}.\nResponse body: ${util.inspect(error.response && error.response.data)}`);
64
65
  }
65
66
  authData = response.data;
66
67
 
@@ -89,7 +90,7 @@ module.exports = class AuthManager {
89
90
  if (params.get('saveData')) {
90
91
  await SettingsManager.saveSettings(params);
91
92
  }
92
- throw new Error('Failed to login: no valid token or passcode provided. ' +
93
+ throw new CliError('Failed to login: no valid token or passcode provided. ' +
93
94
  'Get a passcode' + (params.get('passcodeUrl') ? ' from ' + params.get('passcodeUrl') : '') +
94
95
  ' and supply the code when re-running the command');
95
96
  }
@@ -1,7 +1,7 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs').promises;
3
3
 
4
- const Logger = require('./helper/logging');
4
+ const { CliError } = require('./helper/errors');
5
5
 
6
6
  const MTX_URL_PATH_PREFIX = 'mtx/v1/model/';
7
7
  const CUSTOM_FILE_URL_SUFFIX = 'customTenantContent';
@@ -22,15 +22,13 @@ module.exports = class BaseCommand {
22
22
 
23
23
  switch (error.response && error.response.status) {
24
24
  case 401:
25
- Logger.error('Request failed: authorization might have expired. Please retry with valid passcode' +
25
+ throw new CliError('Request failed: authorization might have expired. Please retry with valid passcode' +
26
26
  (params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : ''), error);
27
- break;
28
27
  case 404:
29
- Logger.error(`Request failed. Please check application URL ${params.get('appUrl')}`, error);
30
- break;
28
+ throw new CliError(`Request failed. Please check application URL ${params.get('appUrl')}`, error);
31
29
  default:
32
30
  // Including 422 (compilation or native-extension error).
33
- Logger.error(undefined, error);
31
+ throw new CliError(undefined, error);
34
32
  }
35
33
  }
36
34
 
@@ -7,6 +7,7 @@ const BaseCommand = require('./base_command');
7
7
  const { normalizePath, isParent } = require('./helper/fs_helper');
8
8
  const Logger = require('./helper/logging');
9
9
  const Question = require('./helper/question');
10
+ const { CliError } = require('./helper/errors');
10
11
 
11
12
  const COMMAND_URL_SUFFIX = 'content';
12
13
  const CUSTOM_FILE_FOLDERS = ['wsdl'];
@@ -88,7 +89,7 @@ module.exports = class Extend extends BaseCommand {
88
89
 
89
90
  const runOnExistingProject = await Extend.checkProject(params.get('projectFolder'));
90
91
  if (runOnExistingProject && !(params.get('force') || params.get('templates'))) {
91
- throw new Error(folderExists(params.get('projectFolder')));
92
+ throw new CliError(folderExists(params.get('projectFolder')));
92
93
  }
93
94
 
94
95
  try {
@@ -0,0 +1,11 @@
1
+ const Logger = require('./logging');
2
+ class CliError extends Error {
3
+ stack = '';
4
+ constructor(message, error) {
5
+ super(Logger.getMessage(message, error));
6
+ }
7
+ }
8
+
9
+ module.exports = {
10
+ CliError
11
+ };
@@ -17,20 +17,13 @@ module.exports = class Logger {
17
17
  }
18
18
 
19
19
  static getMessage(errorText, error) {
20
- let message = errorText && errorText + '\n' || '';
20
+ let message = errorText ? errorText + '\n' : '';
21
21
  if (!error) {
22
22
  return message;
23
23
  }
24
- if (process.env.DEBUG) {
25
- if ('response' in error && error.response.data) {
26
- const body = error.response.data;
27
- if (body.error && body.error.message) {
28
- message += body.error.message + '\n';
29
- }
30
- }
24
+ message += error.response?.data?.error?.message || error.message;
25
+ if (process.env.DEBUG && error.stack) {
31
26
  message += '\n' + error.stack;
32
- } else {
33
- message += error.message;
34
27
  }
35
28
  return message;
36
29
  }
@@ -1,5 +1,6 @@
1
1
  const Logger = require('./helper/logging');
2
2
  const { escapeRegex } = require('./helper/string_helper');
3
+ const { CliError } = require('./helper/errors');
3
4
 
4
5
 
5
6
  // PARAM BASE TYPES
@@ -104,10 +105,10 @@ class Param {
104
105
  expectedType = typeof this.allowedValues[0];
105
106
  }
106
107
  if (typeof val !== expectedType) {
107
- throw new Error(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${expectedType}`);
108
+ throw new CliError(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${expectedType}`);
108
109
  }
109
110
  if (this.allowedValues && !(this.allowedValues).includes(val)) {
110
- throw new Error(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected one of ${JSON.stringify(this.allowedValues)}`);
111
+ throw new CliError(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected one of ${JSON.stringify(this.allowedValues)}`);
111
112
  }
112
113
  }
113
114
 
@@ -246,7 +247,7 @@ class ParamCollection {
246
247
  */
247
248
  checkFrozen() {
248
249
  if (this.isFrozen) {
249
- throw new Error('cannot modify read-only parameter collection');
250
+ throw new CliError('cannot modify read-only parameter collection');
250
251
  }
251
252
  }
252
253
 
@@ -267,7 +268,7 @@ class ParamCollection {
267
268
  static mainName(name) {
268
269
  const paramDef = ParamDefs[name];
269
270
  if (!paramDef) {
270
- throw new Error(`Unknown parameter: '${name}'`);
271
+ throw new CliError(`Unknown parameter: '${name}'`);
271
272
  }
272
273
  return paramDef.mainName;
273
274
  }
@@ -7,13 +7,13 @@ const { unpackTarArchive } = require('@sap/cds/lib/utils/resources');
7
7
  const { login } = require('./auth_manager');
8
8
  const BaseCommand = require('./base_command');
9
9
  const Logger = require('./helper/logging');
10
+ const { CliError } = require('./helper/errors');
10
11
 
11
12
  module.exports = class Pull extends BaseCommand {
12
13
 
13
14
  static async run(paramValues) {
14
15
  try {
15
16
  const params = await login(paramValues);
16
-
17
17
  Logger.log(`Pulling base model from ${params.get('appUrl')}, for subdomain '${params.get('subdomain')}', to project folder '${params.get('projectFolder')}'`);
18
18
 
19
19
  const projectFolder = params.get('projectFolder');
@@ -24,8 +24,8 @@ module.exports = class Pull extends BaseCommand {
24
24
  const tgzBuffer = Buffer.from(await this.getTgz(params));
25
25
  return unpackTarArchive(tgzBuffer, targetDir, false);
26
26
 
27
- } catch (mainError) {
28
- Logger.error(undefined, mainError);
27
+ } catch (error) {
28
+ throw new CliError(undefined, error);
29
29
  }
30
30
  }
31
31
 
@@ -41,11 +41,8 @@ module.exports = class Pull extends BaseCommand {
41
41
  const pullUrl = `${(params.get('appUrl'))}/-/cds/extensibility/pull`;
42
42
  const options = { ...params.get('reqAuth'), responseType: 'arraybuffer' };
43
43
 
44
- try {
45
- const response = await axios.post(pullUrl, {}, options);
46
- return response.data;
47
- } catch (error) {
48
- return this.handleHttpError(error, params);
49
- }
44
+ return axios.post(pullUrl, {}, options)
45
+ .then(response => response.data)
46
+ .catch(error => this.handleHttpError(error, params));
50
47
  }
51
48
  }
@@ -5,23 +5,22 @@ const axios = require('axios');
5
5
  const { login } = require('./auth_manager');
6
6
  const BaseCommand = require('./base_command');
7
7
  const Logger = require('./helper/logging');
8
+ const { CliError } = require('./helper/errors');
8
9
 
9
10
  module.exports = class Push extends BaseCommand {
10
11
 
11
12
  static async run(paramValues) {
12
13
  try {
13
14
  const params = await login(paramValues);
14
-
15
15
  const projectFolder = params.get('projectFolder');
16
16
  Logger.log(`Pushing extension from project folder '${projectFolder}', for subdomain '${params.get('subdomain')}', to app URL ${params.get('appUrl')}`);
17
17
 
18
- const tgzPath = join(projectFolder, 'gen', 'extension.tgz');
19
- const tgz = fs.readFileSync(tgzPath, { encoding: 'base64' });
18
+ const tgz = fs.readFileSync(join(projectFolder, 'gen', 'extension.tgz'), { encoding: 'base64' });
20
19
  const extensionName = this.getExtensionName(projectFolder);
21
20
  return this.pushTgz(params, tgz, extensionName);
22
21
 
23
- } catch (mainError) {
24
- Logger.error(undefined, mainError);
22
+ } catch (error) {
23
+ throw new CliError(undefined, error);
25
24
  }
26
25
  }
27
26
 
@@ -29,7 +28,7 @@ module.exports = class Push extends BaseCommand {
29
28
  try {
30
29
  return require(join(projectFolder, 'package.json')).name;
31
30
  } catch (error) {
32
- throw new Error(`Extension project at ${projectFolder} is missing package.json`);
31
+ throw new CliError(`Extension project at ${projectFolder} is missing package.json`);
33
32
  }
34
33
  }
35
34
 
@@ -37,13 +36,10 @@ module.exports = class Push extends BaseCommand {
37
36
  const pushUrl = `${params.get('appUrl')}/-/cds/extensibility/push`;
38
37
  const options = { ...params.get('reqAuth') };
39
38
 
40
- try {
41
- return axios.post(pushUrl, {
42
- extension: tgz,
43
- tag: extensionName
44
- }, options);
45
- } catch (error) {
46
- return this.handleHttpError(error, params);
47
- }
39
+ return axios.post(pushUrl, {
40
+ extension: tgz,
41
+ tag: extensionName
42
+ }, options)
43
+ .catch(error => this.handleHttpError(error, params));
48
44
  }
49
45
  }
@@ -9,6 +9,7 @@ const { getAppFromSuggestions, getAppName, getSubdomain } = require('./helper/cf
9
9
  const isBas = require('./helper/env_helper');
10
10
  const Logger = require('./helper/logging');
11
11
  const Question = require('./helper/question');
12
+ const { CliError } = require('./helper/errors');
12
13
  const { httpSchema, httpsSchema, extractSubdomain, schemaRegex, localhostRegex } = require('./helper/url_helper');
13
14
  const { ParamCollection, Persistence } = require('./params');
14
15
  const { capitalize } = require('./helper/string_helper');
@@ -95,7 +96,7 @@ async function getTokenBaseUrl(params, renewUrl) {
95
96
  return params.get('tokenUrl');
96
97
  }
97
98
  if (!params.has('appUrl')) {
98
- throw new Error('Failed to determine token URL: app URL not given');
99
+ throw new CliError('Failed to determine token URL: app URL not given');
99
100
  }
100
101
  const baseUrl = new URL(params.get('appUrl'));
101
102
  const cleanPathname = pathname => baseUrl.pathname.replace(/\/?$/, pathname);
@@ -116,7 +117,7 @@ async function getTokenBaseUrl(params, renewUrl) {
116
117
  if (e.response?.status === 404) {
117
118
  continue;
118
119
  } else {
119
- throw Error(`Failed with root cause ${e.message}`);
120
+ throw new CliError(`Failed with root cause ${e.message}`);
120
121
  }
121
122
  }
122
123
  }
@@ -230,12 +231,12 @@ class SettingsManager {
230
231
  }
231
232
  }
232
233
  if (!params.has('appUrl')) {
233
- throw new Error('App URL not given. Please specify it or log in to Cloud Foundry and repeat this command.');
234
+ throw new CliError('App URL not given. Please specify it or log in to Cloud Foundry and repeat this command.');
234
235
  }
235
236
  if (!params.has('subdomain')) {
236
237
  const subdomain = extractSubdomain(params.get('appUrl'));
237
238
  if (!subdomain) {
238
- throw new Error('Subdomain not given');
239
+ throw new CliError('Subdomain not given');
239
240
  }
240
241
  params.set('subdomain', subdomain);
241
242
  Logger.log(`Subdomain determined from app URL: ${subdomain}`);
@@ -267,7 +268,7 @@ class SettingsManager {
267
268
  return;
268
269
  }
269
270
  if (params.get('tokenStorage') === 'keyring') {
270
- throw new Error(`${capitalize(TOKEN_STORAGE_DESC.keyring)} requested but keytar not installed. Run 'npm install -g keytar' or ` +
271
+ throw new CliError(`${capitalize(TOKEN_STORAGE_DESC.keyring)} requested but keytar not installed. Run 'npm install -g keytar' or ` +
271
272
  `switch to ${TOKEN_STORAGE_DESC.plain} by adding '--plain' (discouraged).`);
272
273
  }
273
274
  if (params.get('renewLogin') || logout) {
@@ -288,7 +289,7 @@ class SettingsManager {
288
289
  params.set('clientsecret', await Question.askQuestion('clientsecret: ', undefined, true));
289
290
  console.log();
290
291
  if (params.get('clientsecret') === '') {
291
- throw new Error('clientsecret cannot be empty');
292
+ throw new CliError('clientsecret cannot be empty');
292
293
  }
293
294
  }
294
295
  async function addPasscode() {
@@ -296,7 +297,7 @@ class SettingsManager {
296
297
  params.set('passcode', await Question.askQuestion(prompt, undefined, true));
297
298
  console.log();
298
299
  if (params.get('passcode') === '') {
299
- throw new Error('Passcode cannot be empty');
300
+ throw new CliError('Passcode cannot be empty');
300
301
  }
301
302
  }
302
303
  function setRelevantAuth(auth) {
@@ -338,7 +339,7 @@ class SettingsManager {
338
339
  if (params.get('password') === '') {
339
340
  params.set('isEmptyPassword', true);
340
341
  // TODO check password only in production env
341
- // throw new Error('Password cannot be empty');
342
+ // throw new CliError('Password cannot be empty');
342
343
  } else {
343
344
  params.delete('isEmptyPassword');
344
345
  }