@sap/cds-dk 6.6.2 → 6.7.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 (104) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/bin/add.js +5 -3
  3. package/bin/build/all.js +1 -1
  4. package/bin/cds.js +10 -8
  5. package/bin/env.js +4 -6
  6. package/bin/login.js +6 -4
  7. package/bin/migrate.js +0 -1
  8. package/bin/repl.js +1 -1
  9. package/bin/serve.js +7 -0
  10. package/bin/subscribe.js +5 -5
  11. package/bin/unsubscribe.js +4 -5
  12. package/bin/upgrade.js +67 -0
  13. package/bin/watch.js +8 -5
  14. package/lib/bind/bindingManager.js +3 -2
  15. package/lib/bind/cfKeyProvider.js +7 -6
  16. package/lib/bind/userCdsrcJsonStorageProvider.js +5 -4
  17. package/lib/client/activate.js +4 -3
  18. package/lib/client/auth_manager.js +4 -3
  19. package/lib/client/base_command.js +7 -2
  20. package/lib/client/helper/cf_helper.js +12 -11
  21. package/lib/client/helper/dependency_helper.js +3 -2
  22. package/lib/client/helper/logging.js +0 -7
  23. package/lib/client/params.js +4 -2
  24. package/lib/client/settings_manager.js +35 -34
  25. package/lib/client/tenant_action.js +78 -0
  26. package/lib/init/constants.js +1 -1
  27. package/lib/init/index.js +4 -4
  28. package/lib/init/template/_merging/registry-mta.js +31 -46
  29. package/lib/init/template/approuter/files/mta.yaml.hbs +12 -8
  30. package/lib/init/template/approuter/files/xs-app.json.hbs +0 -3
  31. package/lib/init/template/approuter/index.js +26 -34
  32. package/lib/init/template/auditlog/files/mta.yaml.hbs +2 -1
  33. package/lib/init/template/auditlog/index.js +8 -21
  34. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +4 -4
  35. package/lib/init/template/cf-manifest/index.js +2 -1
  36. package/lib/init/template/connectivity/index.js +4 -5
  37. package/lib/init/template/data/index.js +19 -22
  38. package/lib/init/template/destinations/index.js +5 -17
  39. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +2 -1
  40. package/lib/init/template/enterprise-messaging/index.js +7 -19
  41. package/lib/init/template/extensibility/index.js +3 -1
  42. package/lib/init/template/hana/files/mta.yaml.hbs +4 -5
  43. package/lib/init/template/hana/index.js +49 -84
  44. package/lib/init/template/helm/chart/templates/_helpers.tpl +4 -1
  45. package/lib/init/template/helm/files/Chart.yaml.hbs +1 -4
  46. package/lib/init/template/helm/index.js +45 -13
  47. package/lib/init/template/html5-repo/index.js +6 -29
  48. package/lib/init/template/java/index.js +1 -1
  49. package/lib/init/template/kibana-logging/files/mta.yaml.hbs +2 -1
  50. package/lib/init/template/kibana-logging/index.js +5 -8
  51. package/lib/init/template/lint/index.js +8 -8
  52. package/lib/init/template/mta/files/mta.yaml.hbs +3 -2
  53. package/lib/init/template/mta/index.js +3 -2
  54. package/lib/init/template/multitenancy/files/mta.yaml.hbs +25 -31
  55. package/lib/init/template/multitenancy/files/{java/mtxs-configmap.yaml → mtxs-configmap-java.yaml} +1 -1
  56. package/lib/init/template/multitenancy/files/{mtxs-configmap.yaml → mtxs-configmap-nodejs.yaml} +5 -1
  57. package/lib/init/template/multitenancy/files/package.json.hbs +28 -0
  58. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +21 -0
  59. package/lib/init/template/multitenancy/files/values.yaml.hbs +15 -8
  60. package/lib/init/template/multitenancy/index.js +51 -99
  61. package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -2
  62. package/lib/init/template/nodejs/files/.vscode/launch.json +2 -2
  63. package/lib/init/template/nodejs/files/.vscode/tasks.json +2 -2
  64. package/lib/init/template/nodejs/files/package.json.hbs +2 -2
  65. package/lib/init/template/nodejs/index.js +4 -3
  66. package/lib/init/template/pipeline/index.js +3 -2
  67. package/lib/init/template/samples/index.js +5 -4
  68. package/lib/init/template/sqlite/files/package.json.hbs +19 -0
  69. package/lib/init/template/sqlite/index.js +11 -0
  70. package/lib/init/template/toggles/index.js +3 -5
  71. package/lib/init/template/xsuaa/files/mta.yaml.hbs +4 -5
  72. package/lib/init/template/xsuaa/files/values.yaml.hbs +0 -2
  73. package/lib/init/template/xsuaa/index.js +14 -35
  74. package/lib/init/util/commandUtil.js +3 -2
  75. package/lib/init/util/fsUtil.js +19 -2
  76. package/lib/init/util/md5Tracking.js +36 -63
  77. package/lib/init/util/projectReader.js +12 -26
  78. package/lib/lint/Linter.js +0 -3
  79. package/lib/util/exec.js +2 -1
  80. package/lib/util/term.js +1 -1
  81. package/lib/watch/livereload-connect.js +5 -4
  82. package/lib/watch/livereload-server.js +3 -6
  83. package/lib/watch/node-watch.js +1 -1
  84. package/npm-shrinkwrap.json +553 -466
  85. package/package.json +4 -4
  86. package/readme.md +1 -1
  87. package/lib/client/subscription.js +0 -84
  88. package/lib/init/template/approuter/files/chart.yaml +0 -4
  89. package/lib/init/template/auditlog/files/auditlog.yaml +0 -4
  90. package/lib/init/template/destinations/files/destination.yaml +0 -4
  91. package/lib/init/template/enterprise-messaging/files/event-mesh.yaml +0 -4
  92. package/lib/init/template/hana/files/hana-deployer.yaml +0 -4
  93. package/lib/init/template/hana/files/hana.yaml +0 -4
  94. package/lib/init/template/html5-repo/files/html5-apps-deployer.yaml +0 -4
  95. package/lib/init/template/html5-repo/files/html5-apps-repo-host.yaml +0 -4
  96. package/lib/init/template/multitenancy/files/cds.package.json +0 -7
  97. package/lib/init/template/multitenancy/files/cds.package.json.hbs +0 -9
  98. package/lib/init/template/multitenancy/files/dependencies.package.json +0 -5
  99. package/lib/init/template/multitenancy/files/java/.cdsrc.json +0 -22
  100. package/lib/init/template/multitenancy/files/java/package.json +0 -33
  101. package/lib/init/template/multitenancy/files/saas-registry.yaml +0 -4
  102. package/lib/init/template/multitenancy/files/service-manager.yaml +0 -4
  103. package/lib/init/template/multitenancy/files/sidecar.yaml +0 -4
  104. package/lib/init/template/xsuaa/files/xsuaa.yaml +0 -4
@@ -1,7 +1,8 @@
1
1
  const os = require('os');
2
2
  const { execFile } = require('child_process');
3
3
 
4
- const log = require('./logging');
4
+ const cds = require('../../cds');
5
+ const DEBUG = cds.debug('cli');
5
6
  const Question = require('./question');
6
7
  const { formatObjectsFixedWidth } = require('./string_helper');
7
8
  const { schemaRegex } = require('./url_helper');
@@ -20,7 +21,7 @@ function getErrorMessage(error, stdout) {
20
21
  async function getAppFromSuggestions() {
21
22
  const apps = await getApps();
22
23
  if (apps.length === 0) {
23
- log.debug('Failed to obtain list of apps from CF');
24
+ DEBUG?.('Failed to obtain list of apps from CF');
24
25
  return undefined;
25
26
  }
26
27
 
@@ -36,7 +37,7 @@ Your choice: `);
36
37
  console.log();
37
38
 
38
39
  if (!choice) {
39
- log.debug('No app URL selected');
40
+ DEBUG?.('No app URL selected');
40
41
  return undefined;
41
42
  }
42
43
  choiceNr = Number(choice);
@@ -46,7 +47,7 @@ Your choice: `);
46
47
  }
47
48
 
48
49
  async function getApps(nameRegex = /(?<!-db)$/) {
49
- log.debug('Getting running apps from CF');
50
+ DEBUG?.('Getting running apps from CF');
50
51
 
51
52
  return new Promise(resolve => {
52
53
  runCf(['apps'], function(error, stdout) {
@@ -120,13 +121,13 @@ async function getApps(nameRegex = /(?<!-db)$/) {
120
121
  }
121
122
 
122
123
  function errorResult(message) {
123
- log.debug(`Failed to get apps: ${message}`);
124
+ DEBUG?.(`Failed to get apps: ${message}`);
124
125
  return [];
125
126
  }
126
127
  }
127
128
 
128
129
  async function getSubdomain(appName) {
129
- log.debug('Getting subdomain from CF env');
130
+ DEBUG?.('Getting subdomain from CF env');
130
131
 
131
132
  return new Promise(resolve => {
132
133
  runCf(['env', appName], function(error, stdout) {
@@ -155,13 +156,13 @@ async function getSubdomain(appName) {
155
156
  }
156
157
  }
157
158
 
158
- log.debug(`Subdomain determined from CF app environment: ${value}`);
159
+ DEBUG?.(`Subdomain determined from CF app environment: ${value}`);
159
160
  return resolve(value);
160
161
  });
161
162
  });
162
163
 
163
164
  function errorResult(message) {
164
- log.debug(`Failed to get subdomain: ${message}`);
165
+ DEBUG?.(`Failed to get subdomain: ${message}`);
165
166
  return undefined;
166
167
  }
167
168
  }
@@ -171,14 +172,14 @@ async function getAppName(appUrl) {
171
172
  return url1.replace(schemaRegex, '') === url2.replace(schemaRegex, '');
172
173
  }
173
174
 
174
- log.debug('Getting app name from CF apps');
175
+ DEBUG?.('Getting app name from CF apps');
175
176
  const apps = await getApps();
176
177
  const app = apps.find(app => equalIgnoringTls(app.url, appUrl));
177
178
  if (!app) {
178
- log.debug(`Failed to find app with URL ${appUrl}. Running apps: ${apps.map(app => app.name)}`);
179
+ DEBUG?.(`Failed to find app with URL ${appUrl}. Running apps: ${apps.map(app => app.name)}`);
179
180
  return;
180
181
  }
181
- log.debug(`App name determined from CF: ${app.name}`);
182
+ DEBUG?.(`App name determined from CF: ${app.name}`);
182
183
  return app.name;
183
184
  }
184
185
 
@@ -1,7 +1,8 @@
1
1
  const os = require('os');
2
2
  const { execFileSync } = require('child_process');
3
3
 
4
- const log = require('./logging');
4
+ const cds = require('../../cds');
5
+ const DEBUG = cds.debug('cli');
5
6
 
6
7
  let cachedDir;
7
8
 
@@ -21,7 +22,7 @@ function requireGlobal(packageId) {
21
22
  return require(packagePath);
22
23
  } catch (error) {
23
24
  // Ignore error.
24
- log.debug(`Failed to find module '${packageId}' in ${globalModulesDir()}`, error);
25
+ DEBUG?.(`Failed to find module '${packageId}' in ${globalModulesDir()}`, error);
25
26
  }
26
27
  }
27
28
 
@@ -1,11 +1,5 @@
1
1
  /* eslint-disable no-console */
2
2
 
3
- function debug(debugText, error) {
4
- if (process.env.DEBUG) {
5
- console.log(getMessage(debugText, { error }));
6
- }
7
- }
8
-
9
3
  function getMessage(errorText, { error, help } = {}) {
10
4
  let message = errorText ? errorText : '';
11
5
  if (error) {
@@ -25,6 +19,5 @@ function getMessage(errorText, { error, help } = {}) {
25
19
  }
26
20
 
27
21
  module.exports = {
28
- debug,
29
22
  getMessage
30
23
  };
@@ -1,4 +1,5 @@
1
- const log = require('./helper/logging');
1
+ const cds = require('../cds');
2
+ const DEBUG = cds.debug('cli');
2
3
  const { escapeRegex } = require('./helper/string_helper');
3
4
  const { CliError } = require('./helper/errors');
4
5
 
@@ -179,6 +180,7 @@ class BasicParams {
179
180
  this.url = this.appUrl.newAlias('url');
180
181
  this.from = this.appUrl.newAlias('from');
181
182
  this.to = this.appUrl.newAlias('to');
183
+ this.at = this.appUrl.newAlias('at');
182
184
  this.subdomain = new Param('subdomain', '', { persist: Persistence.setting }); // only needed to fetch token
183
185
  this.passcodeUrl = new Param('passcodeUrl', '', { persist: Persistence.setting, abbreviate: true });
184
186
  this.passcode_url = this.passcodeUrl.newAlias('passcode_url');
@@ -316,7 +318,7 @@ class ParamCollection {
316
318
  this.params[mainName] = ParamDefs[mainName].clone(val);
317
319
  }
318
320
  if (mainName !== name) {
319
- log.debug(`Migrated setting '${name}' to new key '${mainName}'`);
321
+ DEBUG?.(`Migrated setting '${name}' to new key '${mainName}'`);
320
322
  }
321
323
  }
322
324
 
@@ -3,10 +3,9 @@ const os = require('os');
3
3
  const path = require('path');
4
4
  const axios = require('axios');
5
5
 
6
+ const cds = require('../cds');
7
+ const DEBUG = cds.debug('cli');
6
8
  const { requireGlobal } = require('./helper/dependency_helper');
7
- let keytar;
8
-
9
- const log = require('./helper/logging');
10
9
  const { getAppFromSuggestions, getAppName, getSubdomain } = require('./helper/cf_helper');
11
10
  const isBas = require('./helper/env_helper');
12
11
  const Question = require('./helper/question');
@@ -49,6 +48,8 @@ const CONFIG = {
49
48
  keyringDesignation: KEYRING_DESC[os.platform()] || 'not supported'
50
49
  };
51
50
 
51
+ let keytar;
52
+
52
53
  function other(tokenStorage) {
53
54
  if (!tokenStorage) {
54
55
  return tokenStorage;
@@ -130,14 +131,14 @@ function getKeyringAccountName(params) {
130
131
  }
131
132
 
132
133
  function logConfigPaths() {
133
- log.debug(`Settings are stored in ${CONFIG.paths.settings}`);
134
- log.debug(`Authentication data is stored in ${CONFIG.paths.auth} (${TOKEN_STORAGE_DESC.plain}) or ${CONFIG.keyringDesignation} (${TOKEN_STORAGE_DESC.keyring})`);
134
+ DEBUG?.(`Settings are stored in ${CONFIG.paths.settings}`);
135
+ DEBUG?.(`Authentication data is stored in ${CONFIG.paths.auth} (${TOKEN_STORAGE_DESC.plain}) or ${CONFIG.keyringDesignation} (${TOKEN_STORAGE_DESC.keyring})`);
135
136
  }
136
137
 
137
138
  function notifyLoggedIn(params) {
138
139
  return params.get('renewLogin')
139
140
  ? console.log.bind(console)
140
- : log.debug;
141
+ : DEBUG;
141
142
  }
142
143
 
143
144
  function emptyData(all) {
@@ -156,7 +157,7 @@ class SettingsManager {
156
157
 
157
158
  // Call with 'projectFolder' param as a resolved path.
158
159
  static async saveSettings(params) {
159
- log.debug(`Saving settings for project ${params.get('projectFolder')}`);
160
+ DEBUG?.(`Saving settings for project ${params.get('projectFolder')}`);
160
161
 
161
162
  const paramsMap = params.toEntries(Persistence.setting);
162
163
  if (params.has('username')) {
@@ -164,7 +165,7 @@ class SettingsManager {
164
165
  if (localhostRegex.test(params.get('appUrl'))) {
165
166
  console.log(`Saving username${(params.get('isEmptyPassword') ? ' and empty-password hint' : '')} with project settings.`);
166
167
  } else {
167
- log.debug('Not saving username because app is not recognized to run on localhost.');
168
+ DEBUG?.('Not saving username because app is not recognized to run on localhost.');
168
169
  delete paramsMap.username;
169
170
  delete paramsMap.isEmptyPassword;
170
171
  }
@@ -196,7 +197,7 @@ class SettingsManager {
196
197
  params.set('projectFolder', projectFolder);
197
198
 
198
199
  const loadedSettings = await this._loadFromFile(projectFolder);
199
- log.debug(`Loaded project settings for ${params.get('projectFolder')}: ${loadedSettings.format()}`);
200
+ DEBUG?.(`Loaded project settings for ${params.get('projectFolder')}: ${loadedSettings.format()}`);
200
201
  const loadedAppUrl = loadedSettings.get('appUrl');
201
202
 
202
203
  if (params.has('passcode') && loadedSettings.has('username')) {
@@ -213,7 +214,7 @@ class SettingsManager {
213
214
  await this.updateUrls(params, logout, loadedAppUrl);
214
215
  await this.setKeytar(params, logout);
215
216
  await this.addAuth(params, logout);
216
- log.debug(`Effective project settings: ${params.format()}`);
217
+ DEBUG?.(`Effective project settings: ${params.format()}`);
217
218
  }
218
219
 
219
220
  static async updateUrls(params, logout, loadedAppUrl) {
@@ -232,20 +233,20 @@ class SettingsManager {
232
233
  } else {
233
234
  params.set('appUrl', httpsSchema + appUrl);
234
235
  }
235
- log.debug(`Added schema to app URL: ${params.get('appUrl')}`);
236
+ DEBUG?.(`Added schema to app URL: ${params.get('appUrl')}`);
236
237
  }
237
238
  }
238
239
 
239
240
  if (!logout) {
240
241
  // Get appUrl from CF
241
242
  if (!params.has('appUrl')) {
242
- log.debug('App URL not given');
243
+ DEBUG?.('App URL not given');
243
244
  await getAppUrlAndSubdomainFromSuggestions(params);
244
245
  }
245
246
 
246
247
  // Get subdomain from CF
247
248
  if (!params.has('subdomain') && needsSubdomain()) {
248
- log.debug('Subdomain not given');
249
+ DEBUG?.('Subdomain not given');
249
250
  const appName = await getAppName(params.get('appUrl'));
250
251
  if (appName) {
251
252
  params.set('subdomain', await getSubdomainFromCfAndNotify(appName));
@@ -307,8 +308,8 @@ class SettingsManager {
307
308
 
308
309
  const doLog = params.get('saveData') || logout
309
310
  ? console.log.bind(console) // Explicit login/logout call
310
- : log.debug;
311
- doLog(`Disabling ${TOKEN_STORAGE_DESC.keyring} functionality: keytar not found. Run \`npm install -g keytar\` to install it.`);
311
+ : DEBUG;
312
+ doLog?.(`Disabling ${TOKEN_STORAGE_DESC.keyring} functionality: keytar not found. Run \`npm install -g keytar\` to install it.`);
312
313
  }
313
314
 
314
315
  static async addAuth(params, logout) {
@@ -344,7 +345,7 @@ class SettingsManager {
344
345
  if (auth.keyring.has('tokenStorage') && auth.plain.has('tokenStorage')) {
345
346
  // Both storage places contain data: retrieve from selected or keyring.
346
347
  storage = storage || 'keyring';
347
- log.debug('WARNING: authentication data found in both kinds of storage. ' +
348
+ DEBUG?.('WARNING: authentication data found in both kinds of storage. ' +
348
349
  `Using data from ${TOKEN_STORAGE_DESC[storage]}; other storage will be cleared when next saving.`);
349
350
 
350
351
  params.merge(auth[storage]);
@@ -352,7 +353,7 @@ class SettingsManager {
352
353
 
353
354
  } else if (storage && auth[other(storage)].has('tokenStorage')) {
354
355
  // Selected storage contains no data, but other one does: retrieve from other storage, but earmark migration to selected one.
355
- log.debug(`Using authentication data from ${TOKEN_STORAGE_DESC[other(storage)]}; will be migrated to other storage on save.`);
356
+ DEBUG?.(`Using authentication data from ${TOKEN_STORAGE_DESC[other(storage)]}; will be migrated to other storage on save.`);
356
357
 
357
358
  params.merge(auth[other(storage)]);
358
359
  params.set('tokenStorage', storage);
@@ -361,7 +362,7 @@ class SettingsManager {
361
362
  } else {
362
363
  // One storage contains data, and there's no conflict with selection.
363
364
  const storage = auth.keyring.get('tokenStorage') || auth.plain.get('tokenStorage');
364
- log.debug(`Using authentication data from ${TOKEN_STORAGE_DESC[storage]}.`);
365
+ DEBUG?.(`Using authentication data from ${TOKEN_STORAGE_DESC[storage]}.`);
365
366
 
366
367
  params.merge(auth[storage]);
367
368
  }
@@ -376,7 +377,7 @@ class SettingsManager {
376
377
  } else {
377
378
  params.delete('isEmptyPassword');
378
379
  }
379
- log.debug('Ignoring any saved authentication data because username is given');
380
+ DEBUG?.('Ignoring any saved authentication data because username is given');
380
381
  return;
381
382
  } else {
382
383
  params.delete('isEmptyPassword');
@@ -410,7 +411,7 @@ class SettingsManager {
410
411
  let fromStorage = fromOtherStorage && other(params.get('tokenStorage'));
411
412
  const deleteBoth = !fromStorage && allParams.get('tokenStorage') && allParams.get('clearOtherTokenStorage');
412
413
 
413
- log.debug(`Deleting authentication data${fromOtherStorage ? ' from other storage' : ''} for ${target}`);
414
+ DEBUG?.(`Deleting authentication data${fromOtherStorage ? ' from other storage' : ''} for ${target}`);
414
415
 
415
416
  fromStorage = fromStorage || allParams.get('tokenStorage');
416
417
  if (!fromStorage) {
@@ -466,7 +467,7 @@ class SettingsManager {
466
467
  if (paramValues !== null) {
467
468
  paramValuesByFolder[projectFolder] = paramValues;
468
469
  await this._saveAllSettingsToFile(paramValuesByFolder);
469
- log.debug(`Saved project settings: ${JSON.stringify(paramValues)}`);
470
+ DEBUG?.(`Saved project settings: ${JSON.stringify(paramValues)}`);
470
471
  } else {
471
472
  delete paramValuesByFolder[projectFolder];
472
473
  await this._saveAllSettingsToFile(paramValuesByFolder);
@@ -475,10 +476,10 @@ class SettingsManager {
475
476
  }
476
477
 
477
478
  static async _saveAllSettingsToFile(paramValuesByFolder) {
478
- log.debug(`Saving all settings to ${CONFIG.paths.settings}`);
479
+ DEBUG?.(`Saving all settings to ${CONFIG.paths.settings}`);
479
480
  await fs.promises.mkdir(path.dirname(CONFIG.paths.settings), { recursive: true })
480
481
  await fs.promises.writeFile(CONFIG.paths.settings, JSON.stringify(paramValuesByFolder, null, 2));
481
- log.debug('Saved settings');
482
+ DEBUG?.('Saved settings');
482
483
  }
483
484
 
484
485
  static async _saveAuthToFile(params, authValues) {
@@ -490,7 +491,7 @@ class SettingsManager {
490
491
  (allAuthValues[appUrl] || (allAuthValues[appUrl] = {}))[params.get('subdomain')] = authValues;
491
492
  await fs.promises.mkdir(path.dirname(CONFIG.paths.auth), { recursive: true })
492
493
  await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
493
- notifyLoggedIn(params)(`Saved authentication data to ${location}`);
494
+ notifyLoggedIn(params)?.(`Saved authentication data to ${location}`);
494
495
  } else if (allAuthValues[appUrl]) {
495
496
  delete allAuthValues[appUrl][params.get('subdomain')];
496
497
  if (Object.keys(allAuthValues[appUrl]).length === 0) {
@@ -500,7 +501,7 @@ class SettingsManager {
500
501
  await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
501
502
  console.log('Deleted authentication data from', location);
502
503
  } else {
503
- notifyLoggedIn(params)(`No authentication data to delete from ${location}`);
504
+ notifyLoggedIn(params)?.(`No authentication data to delete from ${location}`);
504
505
  }
505
506
  }
506
507
 
@@ -513,7 +514,7 @@ class SettingsManager {
513
514
 
514
515
  if (auth !== null) {
515
516
  await keytar.setPassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(params), JSON.stringify(auth));
516
- notifyLoggedIn(params)(`Saved authentication data to ${location}`);
517
+ notifyLoggedIn(params)?.(`Saved authentication data to ${location}`);
517
518
  } else {
518
519
  await keytar.deletePassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(params));
519
520
  console.log('Deleted authentication data from', location);
@@ -524,7 +525,7 @@ class SettingsManager {
524
525
  const all = !projectFolder;
525
526
 
526
527
  if (!fs.existsSync(CONFIG.paths.settings)) {
527
- log.debug('Settings file not found');
528
+ DEBUG?.('Settings file not found');
528
529
  return emptyData(all);
529
530
  }
530
531
 
@@ -532,7 +533,7 @@ class SettingsManager {
532
533
  try {
533
534
  settingsByFolder = JSON.parse((await fs.promises.readFile(CONFIG.paths.settings)).toString());
534
535
  } catch (err) {
535
- log.debug('Empty settings file');
536
+ DEBUG?.('Empty settings file');
536
537
  return emptyData(all);
537
538
  }
538
539
 
@@ -541,7 +542,7 @@ class SettingsManager {
541
542
  }
542
543
 
543
544
  if (!settingsByFolder[projectFolder]) {
544
- log.debug(`No settings found for project ${projectFolder}`);
545
+ DEBUG?.(`No settings found for project ${projectFolder}`);
545
546
  return new ParamCollection();
546
547
  }
547
548
 
@@ -552,7 +553,7 @@ class SettingsManager {
552
553
  const all = !appUrl;
553
554
 
554
555
  if (!fs.existsSync(CONFIG.paths.auth)) {
555
- log.debug('Authentication-data file not found');
556
+ DEBUG?.('Authentication-data file not found');
556
557
  return emptyData(all);
557
558
  }
558
559
 
@@ -560,7 +561,7 @@ class SettingsManager {
560
561
  try {
561
562
  allAuth = JSON.parse((await fs.promises.readFile(CONFIG.paths.auth)).toString());
562
563
  } catch (err) {
563
- log.debug('Empty authentication-data file');
564
+ DEBUG?.('Empty authentication-data file');
564
565
  return emptyData(all);
565
566
  }
566
567
 
@@ -569,14 +570,14 @@ class SettingsManager {
569
570
  }
570
571
 
571
572
  if (!allAuth[appUrl]) {
572
- log.debug(`File contains no authentication data for app URL ${appUrl}`);
573
+ DEBUG?.(`File contains no authentication data for app URL ${appUrl}`);
573
574
  return new ParamCollection();
574
575
  }
575
576
 
576
577
  const authForSubdomain = allAuth[appUrl][subdomain];
577
578
 
578
579
  if (!authForSubdomain) {
579
- log.debug(`File contains no authentication data for subdomain ${subdomain} (app URL: ${appUrl})`);
580
+ DEBUG?.(`File contains no authentication data for subdomain ${subdomain} (app URL: ${appUrl})`);
580
581
  return new ParamCollection();
581
582
  }
582
583
 
@@ -595,7 +596,7 @@ class SettingsManager {
595
596
  const authString = await keytar.getPassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(new ParamCollection({ appUrl, subdomain })));
596
597
 
597
598
  if (!authString) {
598
- log.debug(`${capitalize(TOKEN_STORAGE_DESC.keyring)} contains no authentication data for URL ${appUrl} and subdomain '${subdomain}'`);
599
+ DEBUG?.(`${capitalize(TOKEN_STORAGE_DESC.keyring)} contains no authentication data for URL ${appUrl} and subdomain '${subdomain}'`);
599
600
  return new ParamCollection();
600
601
  }
601
602
 
@@ -0,0 +1,78 @@
1
+ const cds = require('../cds');
2
+ const DEBUG = cds.debug('cli');
3
+
4
+ const BaseCommand = require('./base_command');
5
+ const { CliError } = require('./helper/errors');
6
+ const { ParamCollection } = require('./params');
7
+ const AuthManager = require('./auth_manager');
8
+ const { capitalize } = require('./helper/string_helper');
9
+
10
+ const COMMANDS = {
11
+ subscribe: 'to',
12
+ upgrade: 'at',
13
+ unsubscribe: 'from'
14
+ };
15
+
16
+ module.exports = class TenantAction extends BaseCommand {
17
+
18
+ // TODO JSDoc
19
+ /**
20
+ * @param command {string} the command to execute: 'subscribe', 'upgrade' or 'unsubscribe'
21
+ */
22
+ constructor(command) {
23
+ if (!(command in COMMANDS)) {
24
+ throw new Error(`invalid command: ${command}`);
25
+ }
26
+ super();
27
+ this.command = command;
28
+ this.gerund = command.replace(/e$/, 'ing');
29
+ this.preposition = COMMANDS[command];
30
+ }
31
+
32
+ async run(paramValues, { tenant, local } = {}) {
33
+ let params = new ParamCollection(paramValues);
34
+ params.set('skipToken', true); // Token would have insufficient scopes for this command.
35
+ if (local) {
36
+ // Connect to locally-served MTXS
37
+ await this.runWithServer(params, tenant);
38
+ } else {
39
+ // Connect to running MTXS server
40
+ params = await AuthManager.login(params.toEntries());
41
+ await this.runClientOnly(params, tenant);
42
+ }
43
+ console.log(`${capitalize(this.command)} successful.`);
44
+ }
45
+
46
+ async runClientOnly(params, tenant) {
47
+ console.log(capitalize(this.gerund), { tenant }, this.preposition, { url: params.get('appUrl') });
48
+ const url = `${params.get('appUrl')}/-/cds/deployment/${this.command}`;
49
+ DEBUG?.(`Sending request to ${url}`);
50
+ try {
51
+ await require('axios').post(url, { tenant }, params.get('reqAuth'));
52
+ } catch (error) {
53
+ BaseCommand.handleHttpError(error, params, { url });
54
+ }
55
+ }
56
+
57
+ async runWithServer(params, tenant) {
58
+ const libId = '@sap/cds-mtxs/bin/cds-mtx';
59
+ let libPath;
60
+ try {
61
+ libPath = require.resolve(libId, { paths: [process.cwd()] });
62
+ DEBUG?.(`Using local @sap/cds-mtxs from ${libPath}`);
63
+ } catch (error) {
64
+ try {
65
+ libPath = require.resolve(libId);
66
+ DEBUG?.(`Using cds-mtxs from ${libPath}`);
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 });
69
+ }
70
+ }
71
+
72
+ const { cds_mtx } = require(libPath);
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.');
75
+ }
76
+ return cds_mtx(this.command, tenant);
77
+ }
78
+ }
@@ -1,6 +1,6 @@
1
1
  const os = require('os');
2
2
 
3
- const MAVEN_ARCHETYPE_VERSION = '1.32.0';
3
+ const MAVEN_ARCHETYPE_VERSION = '1.32.1';
4
4
 
5
5
  const constants = {
6
6
  IS_WIN: (os.platform() === 'win32'),
package/lib/init/index.js CHANGED
@@ -10,7 +10,7 @@ const { OPTIONS_PRIORITY, COMMAND_ADD, COMMAND_INIT,
10
10
 
11
11
  const cds = require('../cds'), { exists, readdir } = cds.utils
12
12
  const LOG = console
13
- const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) && console.debug
13
+ const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) ? console.debug : undefined
14
14
 
15
15
  module.exports = class CDSGenerator {
16
16
  constructor() {
@@ -196,7 +196,7 @@ module.exports = class CDSGenerator {
196
196
  }
197
197
 
198
198
  async _process() {
199
- DEBUG && DEBUG(`Project path: ${cds.root}`);
199
+ DEBUG?.(`Project path: ${cds.root}`);
200
200
 
201
201
  await this._validateOptions();
202
202
  await this._fillTemplateList();
@@ -240,7 +240,7 @@ module.exports = class CDSGenerator {
240
240
  const template = await this._createTemplate(option);
241
241
 
242
242
  if (await template.canRun()) {
243
- DEBUG && DEBUG(`Adding feature '${template.name}'`);
243
+ DEBUG?.(`Adding feature '${template.name}'`);
244
244
 
245
245
  templates.set(option, template);
246
246
  const deps = await template.getDependencies();
@@ -256,7 +256,7 @@ module.exports = class CDSGenerator {
256
256
  }).map((entry) => entry[1]);
257
257
 
258
258
  // check debug to avoid lengthy computation when not in debug mode
259
- DEBUG && DEBUG(`Features: ${this.templateList.map(({name}) => name).join(', ')}`);
259
+ DEBUG?.(`Features: ${this.templateList.map(({name}) => name).join(', ')}`);
260
260
  }
261
261
 
262
262
  async stepEnd() {