@sap/cds-dk 7.0.2 → 7.1.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.
Files changed (90) hide show
  1. package/bin/add.js +2 -2
  2. package/bin/bind.js +5 -7
  3. package/bin/build.js +2 -7
  4. package/bin/cds.js +6 -8
  5. package/bin/deploy/to-hana.js +1 -1
  6. package/bin/init.js +2 -2
  7. package/bin/subscribe.js +4 -4
  8. package/bin/unsubscribe.js +4 -4
  9. package/bin/upgrade.js +4 -4
  10. package/lib/bind/cfKeyProvider.js +10 -10
  11. package/lib/bind/index.js +4 -3
  12. package/lib/bind/userCdsrcJsonStorageProvider.js +2 -10
  13. package/lib/build/buildTaskEngine.js +9 -22
  14. package/lib/build/buildTaskFactory.js +18 -27
  15. package/lib/build/constants.js +16 -14
  16. package/lib/build/index.js +12 -3
  17. package/lib/build/provider/buildTaskProviderInternal.js +23 -15
  18. package/lib/build/provider/mtx/index.js +8 -2
  19. package/lib/build/provider/mtx/resourcesTarBuilder.js +2 -6
  20. package/lib/build/provider/mtx-sidecar/index.js +13 -8
  21. package/lib/build/util.js +13 -0
  22. package/lib/client/activate.js +7 -7
  23. package/lib/client/auth_manager.js +5 -5
  24. package/lib/client/base_command.js +7 -10
  25. package/lib/client/extend.js +2 -2
  26. package/lib/client/helper/logging.js +7 -2
  27. package/lib/client/params.js +2 -3
  28. package/lib/client/pull.js +3 -3
  29. package/lib/client/push.js +3 -3
  30. package/lib/client/settings_manager.js +7 -8
  31. package/lib/client/tenant_action.js +3 -3
  32. package/lib/compile/asyncapi/components/messages/index.js +41 -18
  33. package/lib/compile/asyncapi/index.js +23 -2
  34. package/lib/deploy/to-hana/hana.js +1 -2
  35. package/lib/import/common.js +3 -0
  36. package/lib/import/message.js +2 -1
  37. package/lib/import/odata/metadataconverter/MetadataConverter.js +1 -0
  38. package/lib/import/odata/v4parser.js +85 -95
  39. package/lib/init/constants.js +1 -1
  40. package/lib/init/index.js +78 -73
  41. package/lib/init/template/approuter/index.js +3 -4
  42. package/lib/init/template/auditlog/index.js +3 -4
  43. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +15 -23
  44. package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +22 -17
  45. package/lib/init/template/cf-manifest/index.js +2 -2
  46. package/lib/init/template/connectivity/index.js +2 -3
  47. package/lib/init/template/data/index.js +7 -8
  48. package/lib/init/template/destination/index.js +3 -5
  49. package/lib/init/template/enterprise-messaging/index.js +4 -5
  50. package/lib/init/template/enterprise-messaging-shared/index.js +3 -4
  51. package/lib/init/template/extensibility/index.js +2 -3
  52. package/lib/init/template/file-based-messaging/index.js +2 -2
  53. package/lib/init/template/hana/files/mta.yaml.hbs +1 -1
  54. package/lib/init/template/hana/index.js +39 -63
  55. package/lib/init/template/helm/index.js +15 -14
  56. package/lib/init/template/html5-repo/index.js +3 -7
  57. package/lib/init/template/java/index.js +3 -3
  58. package/lib/init/template/kibana/index.js +3 -4
  59. package/lib/init/template/lint/index.js +13 -13
  60. package/lib/init/template/mta/index.js +3 -2
  61. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -1
  62. package/lib/init/template/multitenancy/index.js +16 -37
  63. package/lib/init/template/nodejs/index.js +4 -6
  64. package/lib/init/template/pipeline/index.js +3 -4
  65. package/lib/init/template/redis-messaging/index.js +3 -4
  66. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +1 -1
  67. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
  68. package/lib/init/template/sample/files/srv/admin-service.cds +1 -1
  69. package/lib/init/template/sample/files/srv/cat-service.cds +1 -1
  70. package/lib/init/template/sample/index.js +6 -5
  71. package/lib/init/template/sqlite/index.js +1 -1
  72. package/lib/init/template/templateBase.js +9 -19
  73. package/lib/init/template/tiny-sample/index.js +5 -5
  74. package/lib/init/template/toggles/index.js +1 -2
  75. package/lib/init/template/typer/files/xsconfig.json +0 -1
  76. package/lib/init/template/typer/index.js +1 -1
  77. package/lib/init/template/xsuaa/index.js +3 -4
  78. package/lib/init/util/indent.js +57 -0
  79. package/lib/init/util/merge.js +72 -16
  80. package/lib/init/util/mvn.js +40 -31
  81. package/lib/init/util/projectReader.js +52 -168
  82. package/lib/init/util/template.js +12 -11
  83. package/lib/init/util/xml.js +6 -6
  84. package/lib/util/cfUtil.js +1 -2
  85. package/lib/util/pruneAxiosErrors.js +22 -0
  86. package/lib/watch/open.js +6 -3
  87. package/npm-shrinkwrap.json +356 -309
  88. package/package.json +1 -2
  89. package/lib/client/helper/errors.js +0 -26
  90. package/lib/util/version.js +0 -27
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const cds = require('../../cds')
4
- const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue } = require('../util')
4
+ const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue, isClassicMtx } = require('../util')
5
5
  const BuildTaskProvider = require('../buildTaskProvider')
6
6
 
7
7
  const { FILE_EXT_CDS, BUILD_TASK_HANA, BUILD_TASK_FIORI, BUILD_TASK_JAVA, BUILD_TASK_JAVA_CF, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_MTX,
@@ -12,6 +12,11 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
12
12
  constructor(logger) {
13
13
  super()
14
14
  this._logger = logger
15
+ // ensure that classic mtx is not used in combination with cds-dk@7
16
+ if (isClassicMtx()) {
17
+ throw new BuildError(`@sap/cds-mtx is deprecated and not supported by @sap/cds-dk >= 7.
18
+ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/docs/guides/multitenancy/old-mtx-migration or install @sap/cds-dk version 6 instead.`)
19
+ }
15
20
  }
16
21
  get logger() {
17
22
  return this._logger
@@ -65,7 +70,8 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
65
70
  this.logger.debug("No service module found")
66
71
  }
67
72
  }
68
- // create required build tasks
73
+
74
+ // order of creation is relevant
69
75
  if (addRequiredTasks) {
70
76
  const mtxTask = tasks.find(task => task.for === BUILD_TASK_MTX || task.for === BUILD_TASK_MTX_SIDECAR)
71
77
  if (mtxTask) {
@@ -80,12 +86,15 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
80
86
  let dbTask = tasks.find(task => task.for === BUILD_TASK_HANA)
81
87
  if (!dbTask) {
82
88
  // db task might be missing if mtx task is enforced by 'cds build --for mtx'
83
- dbTask = this._createDbTask(db, dbOptions)
89
+ dbTask = cds.env.build.tasks?.find(task => task.for === BUILD_TASK_HANA)
90
+ if (!dbTask) {
91
+ dbTask = this._createDbTask(db, dbOptions)
92
+ }
84
93
  if (dbTask) {
85
94
  tasks.push(dbTask)
86
95
  }
87
96
  }
88
- if (dbTask && (!mtxTask._uses || !mtxTask._uses.map(use => use.task).includes(dbTask))) {
97
+ if (dbTask && !mtxTask._uses?.map(use => use.task).includes(dbTask)) {
89
98
  const use = { task: dbTask }
90
99
  mtxTask._uses ? mtxTask._uses.push(use) : mtxTask._uses = [use]
91
100
  }
@@ -106,7 +115,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
106
115
  }
107
116
 
108
117
  // create mtx build task
109
- const mtxTask = this._createMtxTask(srv, tasks)
118
+ const mtxTask = this._createMtxTask(srv)
110
119
  if (mtxTask) {
111
120
  tasks.push(mtxTask)
112
121
  }
@@ -153,7 +162,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
153
162
  return userEnv && (userEnv.data?.model || userEnv.service?.model)
154
163
  }
155
164
 
156
- _createMtxTask(src, tasks) {
165
+ _createMtxTask(src) {
157
166
  this.logger.debug("determining MTX architecture")
158
167
 
159
168
  // preserve order of creation
@@ -177,26 +186,26 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
177
186
  throw new BuildError(`MTX sidecar directory '${sidecarPath}' not existing. Custom build task configuration required if the folder is named differently.`)
178
187
  }
179
188
  if (!sidecarEnv.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
180
- // correct profile might have been set, but couldn't be resolved
181
189
  if (sidecarEnv.profiles?.includes('mtx-sidecar')) {
182
- throw new BuildError('MTX configuration cannot be resolved. Make sure an up-to-date version of @sap/cds-mtxs is installed.')
190
+ // profile has been set, but cannot be resolved
191
+ throw new BuildError('MTX configuration cannot be resolved. Make sure up-to-date versions of @sap/cds-mtxs and @sap/cds are installed.')
183
192
  }
184
193
  throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
185
194
  }
186
195
  return { for: BUILD_TASK_MTX_SIDECAR }
187
196
  }
197
+
188
198
  if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
189
199
  throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`)
190
200
  }
191
-
192
201
  if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar || cds.env.profiles?.includes('mtx-sidecar')) {
193
202
  // cds build is executed in sidecar folder
194
203
  throw new BuildError(`Seems that you are executing 'cds build' in the 'mtx/sidecar' folder. Execute 'cds build' in the project root folder instead.`)
195
204
  }
196
205
 
197
- // mtxs with nodejs, but without sidecar
198
- if (!tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
199
- this.logger.debug("MTX app without sidecar")
206
+ // mtxs nodejs app without sidecar
207
+ if (!BuildTaskProviderInternal._hasJavaNature(src)) {
208
+ this.logger.debug("MTX Nodejs app without sidecar")
200
209
  return {
201
210
  for: BUILD_TASK_MTX,
202
211
  src
@@ -278,7 +287,6 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
278
287
  task.src = task.src || "."
279
288
  break
280
289
  case BUILD_TASK_MTX:
281
- require('../../util/version').checkMtx(cds)
282
290
  // mtxs with nodejs, but without sidecar
283
291
  task.src = task.src || this._normalizePath(cds.env.folders.srv)
284
292
  break
@@ -310,7 +318,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
310
318
  }
311
319
 
312
320
  // REVISIT: filter nodejs bootstrap service models for Java - issues/12770#issuecomment-1805719
313
- if (this._hasJavaNature(cds.env.folders.srv)) {
321
+ if (this._hasJavaNature()) {
314
322
  defaultModelPaths = defaultModelPaths.filter(p => !NODEJS_MODEL_EXCLUDE_LIST.includes(p))
315
323
  }
316
324
  // also add built-in models to custom build tasks already containing model options
@@ -323,7 +331,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
323
331
  * Returns whether this project is a java project or not.
324
332
  * @param {string} srv - the folder name of the service module
325
333
  */
326
- static _hasJavaNature(srv) {
334
+ static _hasJavaNature(srv = cds.env.folders.srv) {
327
335
  srv = this._getModuleFolder(Array.isArray(srv) ? srv : [srv])
328
336
  return hasJavaNature([cds.root, srv && path.join(cds.root, srv)])
329
337
  }
@@ -5,6 +5,7 @@ const cds = require('../../../cds')
5
5
  const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
6
6
  const ResourcesTarProvider = require('./resourcesTarBuilder')
7
7
  const { FOLDER_GEN } = require('../../constants')
8
+ const { BuildError } = require('../../util')
8
9
 
9
10
  class MtxModuleBuilder extends BuildTaskHandlerEdmx {
10
11
  get priority() {
@@ -13,12 +14,17 @@ class MtxModuleBuilder extends BuildTaskHandlerEdmx {
13
14
  }
14
15
  init() {
15
16
  super.init()
17
+
18
+ // this build task only supports nodejs mtxs apps without sidecar
19
+ if (cds.env['project-nature'] === 'java') {
20
+ throw new BuildError(`@sap/cds-mtx is deprecated and not supported by @sap/cds-dk >= 7. This also applies to the 'mtx' build task in Java projects.
21
+ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/docs/guides/multitenancy/old-mtx-migration or install @sap/cds-dk version 6 instead.`)
22
+ }
16
23
  }
17
24
 
18
25
  async build() {
19
- // Non-sidecar scenario: model-provider is implemented by the Nodejs app itself (default for Nodejs).
20
26
  // Only create resources TAR, compiled CSN(s) are created by the Nodejs app build task.
21
- const dest = cds.env.build.target !== "." ? this.task.dest : path.join(this.task.dest, FOLDER_GEN)
27
+ const dest = cds.env.build.target !== '.' ? this.task.dest : path.join(this.task.dest, FOLDER_GEN)
22
28
  const model = await this.model()
23
29
  if (!model) {
24
30
  return
@@ -83,12 +83,8 @@ class ResourcesTarBuilder {
83
83
  }
84
84
 
85
85
  async _getHanaTenantDbDest() {
86
- let hanaTask = this.handler.context.tasks.find(task => task.for === BUILD_TASK_HANA)
87
- if (!hanaTask) {
88
- this.handler.pushMessage(`Found SQLite database configuration. Ensure that productive cloud deployments requiring SAP HANA database are built with 'production' profile.`, WARNING)
89
- return null
90
- }
91
- return hanaTask.dest
86
+ const hanaTask = this.handler.context.tasks.find(task => task.for === BUILD_TASK_HANA)
87
+ return hanaTask?.dest
92
88
  }
93
89
  }
94
90
  module.exports = ResourcesTarBuilder
@@ -1,10 +1,10 @@
1
1
  const path = require('path')
2
2
  const cds = require('../../../cds')
3
3
  const fs = require('fs')
4
- const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
4
+ const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME, MTX_SIDECAR_DB_VALIDATION } = require('../../constants')
5
5
  const NodeCfModuleBuilder = require('../nodejs')
6
6
  const ResourcesTarProvider = require('../mtx/resourcesTarBuilder')
7
- const { WARNING, ERROR } = NodeCfModuleBuilder
7
+ const { ERROR } = NodeCfModuleBuilder
8
8
  const { relativePaths, BuildError, resolveRequiredSapModels, getI18nDefaultFolder } = require('../../util')
9
9
 
10
10
  const DEFAULT_MAIN_FOLDER = "_main"
@@ -61,13 +61,18 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
61
61
  if (!main) {
62
62
  throw new BuildError(`Invalid MTX sidecar configuration. Make sure that the profile 'mtx-sidecar' is configured and an up-to-date version of @sap/cds-mtxs is installed.`)
63
63
  }
64
- const profiles = cds.env.profiles || []
64
+ if (!this.hasBuildOption(MTX_SIDECAR_DB_VALIDATION, false)
65
+ && !cds.env.build.tasks?.length // disable if custom build tasks exist
66
+ && sidecarEnv.requires.db && cds.env.requires.db // ensure database is configured
67
+ && sidecarEnv.requires.db.kind !== cds.env.requires.db.kind) {
68
+ throw new BuildError(`Inconsistent database configuration found - application db '${cds.env.requires.db?.kind}', sidecar db '${sidecarEnv.requires.db?.kind}'`)
69
+ }
70
+ const profiles = cds.env.profiles ?? []
71
+ if (!profiles.includes("production") && !profiles.includes("prod")) {
72
+ // need to overwrite 'development' profile setting '../..' with '_main', although it is questionable why cds build is executed with development profile
73
+ main = DEFAULT_MAIN_FOLDER
74
+ }
65
75
 
66
- if (!profiles.includes("production") && !profiles.includes("prod")) {
67
- main = DEFAULT_MAIN_FOLDER
68
- // root should represent the production use case and not development
69
- this.pushMessage(`MTX sidecar build results are created in folder '${main}'. Enable 'production' or 'prod' profile if the folder is configured differently.`, WARNING)
70
- }
71
76
  const destRoot = this.task.dest
72
77
  const destMain = path.join(destRoot, main)
73
78
  const destMainSrv = path.join(destMain, cds.env.folders.srv)
package/lib/build/util.js CHANGED
@@ -177,6 +177,18 @@ function getI18nDefaultFolder() {
177
177
  return cds.env.i18n.folders[0] ?? 'i18n'
178
178
  }
179
179
 
180
+ function isClassicMtx() {
181
+ function _hasMtxDependency() {
182
+ try {
183
+ return !!require(path.join(cds.root, 'package.json'))?.dependencies?.['@sap/cds-mtx']
184
+ } catch (e) {
185
+ return false
186
+ }
187
+ }
188
+ return /true/i.test(process.env.OLD_MTX) || _hasMtxDependency()
189
+ }
190
+
191
+
180
192
  /**
181
193
  * Return gnu-style error string for location `loc`:
182
194
  * - 'File:Line:Col' without `loc.end`
@@ -266,6 +278,7 @@ module.exports = {
266
278
  flatten,
267
279
  copy,
268
280
  getI18nDefaultFolder,
281
+ isClassicMtx,
269
282
  BuildMessage,
270
283
  BuildError
271
284
  }
@@ -5,7 +5,7 @@ const axios = require('axios');
5
5
  const cds = require('../cds');
6
6
  const DEBUG = cds.debug('cli');
7
7
  const { collectFileContent, collectFiles, toUnixPath } = require('./helper/fs_helper');
8
- const { CliError } = require('./helper/errors');
8
+ const { getMessage } = require('./helper/logging');
9
9
 
10
10
  const AuthManager = require('./auth_manager');
11
11
  const BaseCommand = require('./base_command');
@@ -30,7 +30,7 @@ async function checkFinished(jobId, statusUrl, params, finisher) {
30
30
  finisher.finished = true;
31
31
  clearInterval(finisher.timer);
32
32
  if (status === JOB_STATUS.FAILED) {
33
- return finisher.reject(new CliError(`Job ${jobId} failed`, { error }));
33
+ return finisher.reject(getMessage(`Job ${jobId} failed`, { error }));
34
34
  }
35
35
  console.log('Extension active');
36
36
  finisher.resolve();
@@ -58,7 +58,7 @@ module.exports = class Activate extends BaseCommand {
58
58
  }
59
59
 
60
60
  if (!await this.checkProject(params.get('projectFolder'))) {
61
- throw new CliError(params.get('projectFolder') + ' is not a valid extension project');
61
+ throw params.get('projectFolder') + ' is not a valid extension project';
62
62
  }
63
63
 
64
64
  try {
@@ -133,7 +133,7 @@ module.exports = class Activate extends BaseCommand {
133
133
  try {
134
134
  pkgJson = await fs.readFile(path.join(absoluteFolder, 'package.json'), 'utf-8');
135
135
  } catch (error) {
136
- throw new CliError('No package.json file found', { error });
136
+ throw getMessage('No package.json file found', { error });
137
137
  }
138
138
  allFiles.set('package.json', pkgJson);
139
139
 
@@ -182,11 +182,11 @@ module.exports = class Activate extends BaseCommand {
182
182
  const { jobID: jobId, statusPath } = response.data;
183
183
 
184
184
  if (jobId === undefined) {
185
- throw new CliError('Activation failed: did not receive job ID');
185
+ throw 'Activation failed: did not receive job ID';
186
186
  }
187
187
 
188
188
  if (!statusPath) {
189
- throw new CliError(`Job ${jobId} failed: did not receive job-status path`);
189
+ throw `Job ${jobId} failed: did not receive job-status path`;
190
190
  }
191
191
 
192
192
  DEBUG?.(`Job ID: ${jobId}`);
@@ -199,7 +199,7 @@ module.exports = class Activate extends BaseCommand {
199
199
  finished: false,
200
200
  timer: null
201
201
  };
202
- finisher.timer = setInterval(checkFinished.bind(undefined, jobId, statusUrl, params, finisher), 250).unref();
202
+ finisher.timer = setInterval(checkFinished.bind(undefined, jobId, statusUrl, params, finisher), 250);
203
203
  });
204
204
  });
205
205
  }
@@ -4,7 +4,7 @@ const cds = require('../cds');
4
4
  const DEBUG = cds.debug('cli');
5
5
  const { SettingsManager } = require('./settings_manager');
6
6
  const { ParamCollection } = require('./params');
7
- const { CliError } = require('./helper/errors');
7
+ const { getMessage } = require('./helper/logging');
8
8
 
9
9
  function addTokenInfo(params, authData) {
10
10
  params.set('token', authData.access_token);
@@ -79,14 +79,14 @@ async function retrieveTokenOrPasscodeUrl(params) {
79
79
  if (error.response?.status === 404) { // may represent 405 in case of wrong method
80
80
  continue;
81
81
  }
82
- throw new CliError('Failed to get authentication token', { error });
82
+ throw getMessage('Failed to get authentication token', { error });
83
83
  }
84
84
  if (response) {
85
85
  break;
86
86
  }
87
87
  }
88
88
  if (!response) {
89
- throw new CliError('Failed to get authentication token');
89
+ throw 'Failed to get authentication token';
90
90
  }
91
91
  authData = response.data;
92
92
 
@@ -115,9 +115,9 @@ module.exports = class AuthManager {
115
115
  if (params.get('saveData')) {
116
116
  await SettingsManager.saveSettings(params);
117
117
  }
118
- throw new CliError(`Failed to login: no valid token or passcode provided. Get a passcode${
118
+ throw `Failed to login: no valid token or passcode provided. Get a passcode${
119
119
  params.get('passcodeUrl') ? ' from ' + params.get('passcodeUrl') : ''
120
- } and supply the code when re-running the command`);
120
+ } and supply the code when re-running the command`;
121
121
  }
122
122
  params.set('reqAuth', { headers: { Authorization: 'Bearer ' + params.get('token') } });
123
123
  }
@@ -1,7 +1,9 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs').promises;
3
3
 
4
- const { CliError } = require('./helper/errors');
4
+ require('../util/pruneAxiosErrors');
5
+
6
+ const { getMessage } = require('./helper/logging');
5
7
 
6
8
  const MTX_URL_PATH_PREFIX = 'mtx/v1/model/';
7
9
  const CUSTOM_FILE_URL_SUFFIX = 'customTenantContent';
@@ -20,23 +22,18 @@ module.exports = class BaseCommand {
20
22
  const prefix = `Request to ${url} failed`;
21
23
  switch (error.response?.status) {
22
24
  case 401:
23
- throw new CliError(`${prefix}: invalid authentication.\nRetry with valid passcode${
25
+ throw getMessage(`${prefix}: invalid authentication.\nRetry with valid passcode${
24
26
  params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : ''
25
27
  } or username/password (required if SaaS app is configured with mocked-auth).`, { error, command });
26
28
  case 403:
27
- throw new CliError(prefix + ': insufficient authorization. Ensure client has all necessary roles.', { error, command });
29
+ throw getMessage(prefix + ': insufficient authorization. Ensure client has all necessary roles.', { error, command });
28
30
  case 404:
29
- throw new CliError(prefix + ': resource not found.', { command });
31
+ throw getMessage(prefix + ': resource not found.', { error, command });
30
32
  case 422:
31
33
  // Compilation or native-extension error.
32
- throw new CliError(prefix + '.', { error, command });
34
+ throw getMessage(prefix + '.', { error, command });
33
35
  default:
34
36
  // Unexpected error should be transparent to user.
35
- if (!require('..').debug('axios')) {
36
- error.config?.auth && (error.config.auth = '...'); // TODO use &&= with Node > 14
37
- delete error.request;
38
- delete error.response;
39
- }
40
37
  throw error;
41
38
  }
42
39
  }
@@ -6,7 +6,7 @@ const AuthManager = require('./auth_manager');
6
6
  const BaseCommand = require('./base_command');
7
7
  const { normalizePath, isParent } = require('./helper/fs_helper');
8
8
  const Question = require('./helper/question');
9
- const { CliError } = require('./helper/errors');
9
+ const { getMessage } = require('./helper/logging');
10
10
 
11
11
  const COMMAND_URL_SUFFIX = 'content';
12
12
  const CUSTOM_FILE_FOLDERS = ['wsdl'];
@@ -87,7 +87,7 @@ module.exports = class Extend extends BaseCommand {
87
87
 
88
88
  const runOnExistingProject = await Extend.checkProject(params.get('projectFolder'));
89
89
  if (runOnExistingProject && !(params.get('force') || params.get('templates'))) {
90
- throw new CliError(folderExists(params.get('projectFolder')), { command: 'extend' });
90
+ throw getMessage(folderExists(params.get('projectFolder')), { command: 'extend' });
91
91
  }
92
92
 
93
93
  try {
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-console */
2
2
 
3
- function getMessage(errorText, { error, help } = {}) {
3
+ function getMessage(errorText, { error, command, help = command && getCommandHelp(command) } = {}) {
4
4
  let message = errorText ? errorText : '';
5
5
  if (error) {
6
6
  message += '\n' + (
@@ -8,7 +8,7 @@ function getMessage(errorText, { error, help } = {}) {
8
8
  || error.response?.data?.error?.message // response body == { error }
9
9
  || error.message // no response attached
10
10
  );
11
- if (process.env.DEBUG && error.stack) {
11
+ if (error.stack && require('../../cds').debug('cli')) {
12
12
  message += '\n' + error.stack;
13
13
  }
14
14
  }
@@ -18,6 +18,11 @@ function getMessage(errorText, { error, help } = {}) {
18
18
  return message;
19
19
  }
20
20
 
21
+ function getCommandHelp(command) {
22
+ const cli = require('../../../bin/cds');
23
+ return cli?.load(command, () => ({ help: '' }))?.help ?? '';
24
+ }
25
+
21
26
  module.exports = {
22
27
  getMessage
23
28
  };
@@ -1,7 +1,6 @@
1
1
  const cds = require('../cds');
2
2
  const DEBUG = cds.debug('cli');
3
3
  const { escapeRegex } = require('./helper/string_helper');
4
- const { CliError } = require('./helper/errors');
5
4
 
6
5
 
7
6
  // PARAM BASE TYPES
@@ -106,10 +105,10 @@ class Param {
106
105
  expectedType = typeof this.allowedValues[0];
107
106
  }
108
107
  if (typeof val !== expectedType) {
109
- throw new CliError(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${expectedType}`);
108
+ throw `Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${expectedType}`;
110
109
  }
111
110
  if (this.allowedValues && !(this.allowedValues).includes(val)) {
112
- throw new CliError(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected one of ${JSON.stringify(this.allowedValues)}`);
111
+ throw `Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected one of ${JSON.stringify(this.allowedValues)}`;
113
112
  }
114
113
  }
115
114
 
@@ -7,13 +7,13 @@ const axios = require('axios');
7
7
 
8
8
  const { login } = require('./auth_manager');
9
9
  const BaseCommand = require('./base_command');
10
- const { CliError } = require('./helper/errors');
10
+ const { getMessage } = require('./helper/logging');
11
11
 
12
12
  module.exports = class Pull extends BaseCommand {
13
13
 
14
14
  static async run(paramValues) {
15
15
  if (!tar) {
16
- throw new CliError(`cds pull requires @sap/cds version >= 6.2. Current version is ${cds.version}\n`);
16
+ throw `cds pull requires @sap/cds version >= 6.2. Current version is ${cds.version}\n`;
17
17
  }
18
18
 
19
19
  const params = await login(paramValues);
@@ -59,7 +59,7 @@ module.exports = class Pull extends BaseCommand {
59
59
  try {
60
60
  packageContents = JSON.parse(fs.readFileSync(packageJson, { encoding: 'utf-8' }));
61
61
  } catch (error) {
62
- throw new CliError('package.json missing or unreadable', { error });
62
+ throw getMessage('package.json missing or unreadable', { error });
63
63
  }
64
64
  fs.writeFileSync(packageJson, JSON.stringify({
65
65
  ...packageContents,
@@ -5,7 +5,7 @@ const axios = require('axios');
5
5
 
6
6
  const { login } = require('./auth_manager');
7
7
  const BaseCommand = require('./base_command');
8
- const { CliError } = require('./helper/errors');
8
+ const { getMessage } = require('./helper/logging');
9
9
  const { schemaRegex } = require('./helper/url_helper');
10
10
  const buildSystem = require('../build')
11
11
 
@@ -39,7 +39,7 @@ module.exports = class Push extends BaseCommand {
39
39
  src = local(src);
40
40
  } else {
41
41
  if (!schemaRegex.test(src)) {
42
- throw new CliError(`Nonexistent path: ${src}`, { command: 'push' });
42
+ throw getMessage(`Nonexistent path: ${src}`, { command: 'push' });
43
43
  }
44
44
  try {
45
45
  content = (await axios.get(src, { responseEncoding: 'base64' })).data;
@@ -54,7 +54,7 @@ module.exports = class Push extends BaseCommand {
54
54
  try {
55
55
  return require(join(projectFolder, 'package.json')).name;
56
56
  } catch (error) {
57
- throw new CliError(`Extension project at ${projectFolder} is missing package.json file`, { error, command: 'push' });
57
+ throw getMessage(`Extension project at ${projectFolder} is missing package.json file`, { error, command: 'push' });
58
58
  }
59
59
  }
60
60
 
@@ -9,7 +9,6 @@ const { requireGlobal } = require('./helper/dependency_helper');
9
9
  const { getAppFromSuggestions, getSubdomain } = require('./helper/cf_helper');
10
10
  const isBas = require('./helper/env_helper');
11
11
  const Question = require('./helper/question');
12
- const { CliError } = require('./helper/errors');
13
12
  const { httpSchema, httpsSchema, schemaRegex, localhostRegex } = require('./helper/url_helper');
14
13
  const { capitalize } = require('./helper/string_helper');
15
14
 
@@ -95,7 +94,7 @@ async function getTokenBaseUrl(params, renewUrl) {
95
94
  return params.get('tokenUrl');
96
95
  }
97
96
  if (!params.has('appUrl')) {
98
- throw new CliError('Failed to determine token URL: app URL not given');
97
+ throw 'Failed to determine token URL: app URL not given';
99
98
  }
100
99
  const baseUrl = new URL(params.get('appUrl'));
101
100
  const cleanPathname = pathname => baseUrl.pathname.replace(/\/?$/, pathname);
@@ -237,7 +236,7 @@ class SettingsManager {
237
236
 
238
237
  // Check required param: appUrl
239
238
  if (!params.has('appUrl')) {
240
- throw new CliError('App URL not given. Please specify it or log in to Cloud Foundry and repeat this command.');
239
+ throw 'App URL not given. Please specify it or log in to Cloud Foundry and repeat this command.';
241
240
  }
242
241
  }
243
242
  async function renewTokenUrl() {
@@ -273,8 +272,8 @@ class SettingsManager {
273
272
 
274
273
  if (params.get('tokenStorage') === 'keyring' && params.get('saveData')) {
275
274
  // Explicit login w/o plain-text storage enabled
276
- throw new CliError(capitalize(TOKEN_STORAGE_DESC.keyring) + ' requested but keytar not installed. ' +
277
- 'Run `npm install -g keytar` or switch to ' + TOKEN_STORAGE_DESC.plain + ' by adding `--plain` (discouraged).');
275
+ throw capitalize(TOKEN_STORAGE_DESC.keyring) + ' requested but keytar not installed. ' +
276
+ 'Run `npm install -g keytar` or switch to ' + TOKEN_STORAGE_DESC.plain + ' by adding `--plain` (discouraged).';
278
277
  }
279
278
 
280
279
  const doLog = params.get('saveData') || logout
@@ -296,14 +295,14 @@ class SettingsManager {
296
295
  params.set('clientsecret', await Question.askQuestion('clientsecret: ', undefined, true));
297
296
  console.log();
298
297
  if (params.get('clientsecret') === '') {
299
- throw new CliError('Clientsecret cannot be empty');
298
+ throw 'Clientsecret cannot be empty';
300
299
  }
301
300
  }
302
301
  async function addKey() {
303
302
  params.set('key', await Question.askQuestion('key: ', undefined, true));
304
303
  console.log();
305
304
  if (params.get('key') === '') {
306
- throw new CliError('Key cannot be empty');
305
+ throw 'Key cannot be empty';
307
306
  }
308
307
  }
309
308
  async function addPasscode() {
@@ -311,7 +310,7 @@ class SettingsManager {
311
310
  params.set('passcode', (await Question.askQuestion(prompt, undefined, true)).trim());
312
311
  console.log();
313
312
  if (params.get('passcode') === '') {
314
- throw new CliError('Passcode cannot be empty');
313
+ throw 'Passcode cannot be empty';
315
314
  }
316
315
  }
317
316
  function setRelevantAuth(auth) {
@@ -2,7 +2,7 @@ const cds = require('../cds');
2
2
  const DEBUG = cds.debug('cli');
3
3
 
4
4
  const BaseCommand = require('./base_command');
5
- const { CliError } = require('./helper/errors');
5
+ const { getMessage } = require('./helper/logging');
6
6
  const { ParamCollection } = require('./params');
7
7
  const AuthManager = require('./auth_manager');
8
8
  const { capitalize } = require('./helper/string_helper');
@@ -65,13 +65,13 @@ module.exports = class TenantAction extends BaseCommand {
65
65
  libPath = require.resolve(libId);
66
66
  DEBUG?.(`Using cds-mtxs from ${libPath}`);
67
67
  } catch (error) {
68
- throw new CliError('@sap/cds-mtxs not found or outdated. Have you changed into the directory of your SaaS app? If so, please upgrade to @sap/cds-mtxs@>=1.7.0', { error, command: this.command });
68
+ throw getMessage('@sap/cds-mtxs not found or outdated. Have you changed into the directory of your SaaS app? If so, please upgrade to @sap/cds-mtxs@>=1.7.0', { error, command: this.command });
69
69
  }
70
70
  }
71
71
 
72
72
  const { cds_mtx } = require(libPath);
73
73
  if (!cds_mtx) {
74
- throw new CliError('@sap/cds-mtxs outdated. Please upgrade to @sap/cds-mtxs@>=1.7.0 and re-run this command.');
74
+ throw '@sap/cds-mtxs outdated. Please upgrade to @sap/cds-mtxs@>=1.7.0 and re-run this command.';
75
75
  }
76
76
  return cds_mtx(this.command, tenant);
77
77
  }