@sap/cds-dk 6.3.1 → 6.4.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 (93) hide show
  1. package/CHANGELOG.md +37 -5
  2. package/bin/cds.js +2 -0
  3. package/bin/compile/index.js +2 -1
  4. package/bin/deploy/index.js +2 -2
  5. package/bin/deploy/to-sqlite.js +17 -15
  6. package/bin/help.js +7 -4
  7. package/bin/init.js +1 -1
  8. package/bin/login.js +3 -4
  9. package/bin/migrate.js +33 -0
  10. package/bin/pull.js +4 -4
  11. package/bin/push.js +1 -1
  12. package/bin/subscribe.js +58 -66
  13. package/bin/unsubscribe.js +67 -0
  14. package/bin/watch.js +4 -3
  15. package/lib/bind/bindingUtil.js +2 -0
  16. package/lib/bind/cfKeyProvider.js +5 -1
  17. package/lib/client/activate.js +3 -3
  18. package/lib/client/auth_manager.js +2 -2
  19. package/lib/client/base_command.js +7 -6
  20. package/lib/client/extend.js +2 -2
  21. package/lib/client/helper/errors.js +8 -2
  22. package/lib/client/helper/logging.js +13 -11
  23. package/lib/client/params.js +3 -0
  24. package/lib/client/pull.js +1 -1
  25. package/lib/client/push.js +4 -4
  26. package/lib/client/settings_manager.js +13 -6
  27. package/lib/client/subscription.js +81 -0
  28. package/lib/compile/to-xsuaa.js +25 -19
  29. package/lib/import/odata/metadataconverter/MetadataConverter.js +3 -3
  30. package/lib/import/odata/v2parser.js +37 -24
  31. package/lib/import/odata/v4parser.js +106 -37
  32. package/lib/init/constants.js +1 -1
  33. package/lib/init/index.js +2 -2
  34. package/lib/init/template/_merging/registry-mta.js +0 -3
  35. package/lib/init/template/approuter/files/approuter-configmap.yaml +10 -0
  36. package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +15 -0
  37. package/lib/init/template/approuter/files/chart.yaml +4 -0
  38. package/lib/init/template/approuter/files/package.json +1 -1
  39. package/lib/init/template/approuter/files/values.yaml.hbs +31 -0
  40. package/lib/init/template/approuter/index.js +60 -12
  41. package/lib/init/template/auditlog/index.js +16 -8
  42. package/lib/init/template/cf-manifest/index.js +3 -3
  43. package/lib/init/template/connectivity/index.js +19 -10
  44. package/lib/init/template/destinations/index.js +23 -7
  45. package/lib/init/template/enterprise-messaging/files/cds.package.json +1 -2
  46. package/lib/init/template/enterprise-messaging/files/cds.profile.package.json.hbs +1 -2
  47. package/lib/init/template/enterprise-messaging/files/cdsrc.json +1 -2
  48. package/lib/init/template/enterprise-messaging/files/cdsrc.json.hbs +1 -2
  49. package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +31 -0
  50. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +1 -0
  51. package/lib/init/template/enterprise-messaging/index.js +26 -13
  52. package/lib/init/template/extensibility/index.js +19 -10
  53. package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
  54. package/lib/init/template/hana/index.js +58 -22
  55. package/lib/init/template/helm/chart/templates/NOTES.txt +4 -3
  56. package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +25 -2
  57. package/lib/init/template/helm/chart/values.root.schema.json +5 -0
  58. package/lib/init/template/helm/chart/values.schema.json +27 -0
  59. package/lib/init/template/helm/files/values.yaml.hbs +1 -1
  60. package/lib/init/template/helm/index.js +25 -23
  61. package/lib/init/template/helm/subchart/templates/_helpers.tpl +29 -0
  62. package/lib/init/template/helm/subchart/templates/api-rule.yaml +5 -5
  63. package/lib/init/template/helm/subchart/templates/deployment.yaml +12 -9
  64. package/lib/init/template/helm/subchart/values.md +258 -88
  65. package/lib/init/template/helm/subchart/values.schema.json +96 -13
  66. package/lib/init/template/helm/subchart/values.yaml +6 -2
  67. package/lib/init/template/html5-repo/index.js +28 -11
  68. package/lib/init/template/java/index.js +5 -5
  69. package/lib/init/template/kibana-logging/index.js +5 -4
  70. package/lib/init/template/mta/index.js +2 -2
  71. package/lib/init/template/mtx/index.js +1 -1
  72. package/lib/init/template/multitenancy/files/chart.yaml +4 -0
  73. package/lib/init/template/multitenancy/files/java/mtxs-configmap.yaml +20 -0
  74. package/lib/init/template/multitenancy/files/java/package.json +6 -9
  75. package/lib/init/template/multitenancy/files/mtxs-configmap.yaml +15 -0
  76. package/lib/init/template/multitenancy/files/saas-registry.yaml +17 -19
  77. package/lib/init/template/multitenancy/files/values.yaml.hbs +47 -0
  78. package/lib/init/template/multitenancy/files/xs-security.json.hbs +0 -7
  79. package/lib/init/template/multitenancy/index.js +83 -37
  80. package/lib/init/template/nodejs/files/.eslintrc +26 -0
  81. package/lib/init/template/nodejs/files/package.json.hbs +0 -28
  82. package/lib/init/template/nodejs/index.js +7 -10
  83. package/lib/init/template/pipeline/index.js +4 -4
  84. package/lib/init/template/samples/index.js +5 -5
  85. package/lib/init/template/templateBase.js +1 -4
  86. package/lib/init/template/toggles/index.js +5 -5
  87. package/lib/init/template/xsuaa/files/values.yaml.hbs +21 -0
  88. package/lib/init/template/xsuaa/index.js +34 -24
  89. package/lib/init/util/md5Tracking.js +113 -0
  90. package/lib/init/util/merge.js +19 -2
  91. package/lib/init/util/projectReader.js +15 -12
  92. package/npm-shrinkwrap.json +70 -572
  93. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -7,6 +7,41 @@ 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.4.0 - 2022-12-15
11
+
12
+ ### Added
13
+
14
+ - `cds add helm` now supports approuter.
15
+ - `cds subscribe --local` starts @sap/cds-mtxs locally instead of contacting a running instance via URL.
16
+ - `cds unsubscribe` removes the subscription of a tenant from a SaaS app.
17
+
18
+ ### Changed
19
+
20
+ - `cds add multitenancy` will not compile internal roles into the `xs-security.json` any more.
21
+ - `cds add hana` does not create a `hdi-service-name: ${service-name}` for the database resource properties any more.
22
+ - MTX-related commands now print usage help in case of known errors.
23
+ - `cds import` now adds the annotation `@open` for all the Entities and ComplexTypes with attribute `Abstract` or `OpenType` and adds the same for those referred by the attribute `BaseType`.
24
+ - `cds import` now adds default value in the CSN for the optional parameters in action/function for OData V4 files.
25
+ - `cds init` uses latest Maven Java archetype version 1.30.0 for creating Java projects.
26
+
27
+ ### Fixed
28
+
29
+ - `cds init` no longer creates `engines` entry in package.json to avoid `unsupported engine` warnings.
30
+ - `cds subscribe` correctly exits the process after deploying to a HANA database.
31
+ - `cds add multitenancy` doesn't create a duplicate `saas-registry` resource if there's one with `service-plan: service` any more.
32
+ - `cds bind` now correctly maps the `hana-mt` and `sql-mt` shortcuts to the `cds.requires.db` entry.
33
+ - `cds add mtx` no longer fails if the `mtx/sidecar` folder doesn't exist.
34
+ - `cds compile --to xsuaa` now rejects expressions leading to invalid XSUAA attributes like `$user.foo-bar`, `$user.foo/bar`, `$user.foo.bar`
35
+ - `cds compile --to xsuaa -o <file>` now sets `.json` as file ending instead of `.xsuaa`
36
+
37
+ ## Version 6.3.2 - 2022-11-28
38
+
39
+ ### Changed
40
+
41
+ - Include `@sap/cds` 6.3.2
42
+ - Include `@sap/cds-compiler` 3.4.4
43
+ - Include `@sap/cds-mtxs` 1.3.2
44
+
10
45
  ## Version 6.3.1 - 2022-11-04
11
46
 
12
47
  ### Changed
@@ -15,10 +50,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
15
50
  - Use `@sap/cds` 6.3.1
16
51
  - Use `@sap/cds-mtxs` 1.3.1
17
52
 
18
- ### Fixed
19
-
20
- - MTX Client no longer reports 'Request failed with status code 401' when sending a passcode to old MTX server.
21
-
22
53
  ## Version 6.3.0 - 2022-11-02
23
54
 
24
55
  ### Added
@@ -37,7 +68,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
37
68
 
38
69
  - MTX Client now ignores a username potentially saved for the current project, if a passcode is given
39
70
  - MTX Client now treats keytar as optional unless explicitly running `cds login`
40
- - `cds import` now caputres the documentation properly for all the EntitySet referring to same EntityType.
71
+ - `cds import` now captures the documentation properly for all the EntitySet referring to same EntityType.
41
72
  - `cds deploy --to sql` now produces 'plain' SQL again, suitable for e.g. H2. In 6.2 it produced 'sqlite' dialect, erroneously.
42
73
  - `cds compile --to openapi` now fixes the duplication of fields in `required` section.
43
74
  - `cds lint` now recognizes ESLint configurations from _package.json_.
@@ -247,6 +278,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
247
278
 
248
279
  ### Fixed
249
280
 
281
+ - `cds add html5-repo` and `cds add destinations` throw an error if applied for MTA deployments, for which they are currently unsupported.
250
282
  - `cds compile --to openapi` fixes annotations on bound action overloads.
251
283
  - `cds add cf-manifest` now uses the correct `application` plan for the `xsuaa` service
252
284
  - A combination of `cds add approuter`, `cds add mtx`, and `cds add mta` will now use a safer approach to determine the subscription URL.
package/bin/cds.js CHANGED
@@ -94,7 +94,9 @@ function _dieForUnknownCommand(cmd) {
94
94
 
95
95
  const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js'];
96
96
  const { readdirSync } = require('fs');
97
+ const { resolve } = require('path');
97
98
  const commands = readdirSync(__dirname, { withFileTypes: true })
99
+ .concat(readdirSync(resolve(_local('@sap/cds'), '..', '..', 'bin'), { withFileTypes: true }))
98
100
  .filter((f) => f.isFile() && !excludeList.includes(f.name))
99
101
  .map((f) => f.name.split('.')[0]).sort();
100
102
 
@@ -273,7 +273,8 @@ function compile (models, options={}) {
273
273
  cdl: '.cds',
274
274
  ddl: '.sql',
275
275
  sql: '.sql',
276
- edm: '.json'
276
+ edm: '.json',
277
+ xsuaa: '.json'
277
278
  }[x] || '.'+x) }
278
279
 
279
280
  }
@@ -1,6 +1,6 @@
1
1
  module.exports = Object.assign(deploy, {
2
2
  options: ['--to', '--tunnel-address', '--vcap-file'],
3
- flags: [ '--no-save', '--auto-undeploy', '--dry', '--with-mocks', '--store-credentials', '--bind' ],
3
+ flags: [ '--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--bind' ],
4
4
  shortcuts: ['-2'],
5
5
  help: `
6
6
  # SYNOPSIS
@@ -79,7 +79,7 @@ async function deploy (_model, options) { // NOSONAR
79
79
 
80
80
  cds deploy --to sql
81
81
  cds deploy --to sqlite
82
- cds deploy --to sqlite:db/my.db
82
+ cds deploy --to sqlite:db/my-db.sqlite
83
83
  `)
84
84
 
85
85
  const conf = cds.env.requires.db || {}
@@ -1,27 +1,29 @@
1
1
  exports.deploy = function deploy_to_sqlite (_model, _db, options) {
2
- const cds = require('../../lib/cds')
3
- const {to:_dbrl} = options
4
- const conf = cds.env.requires.db || {}
5
- const fts = cds.env.features.folders
6
- const model = _model || conf.model || cds.resolve(!fts ? '*' : ['*',fts], false)
7
- const file = _database4 (_db, _dbrl, conf)
8
2
 
9
- // current 'sqlite' default is bad, as we want to default to 'plain', so ignore it
10
- const env_dialect = cds.env.sql.dialect !== 'sqlite' ? cds.env.sql.dialect : 'plain'
11
- const to_dialect = _dbrl && _dbrl.match(/^(\w+)/) && RegExp.$1
12
- if (to_dialect) options.dialect = (to_dialect === 'sql')
13
- ? conf.dialect || env_dialect
14
- : to_dialect
15
3
  if (options.dialect === 'postgres' && !options.dry) console.error('\nDeploying to PostgreSQL is not yet supported\n')
16
4
 
17
- return cds.deploy(model) .to ('sqlite:'+file, options)
18
- .then (db => db && db.disconnect()) // REVISIT: we should NOT require to disconnect explicitly
5
+ const cds = require('../../lib/cds')
6
+ const conf = cds.env.requires.db ?? {}
7
+ const fts = cds.env.features.folders
8
+ const model = _model || conf.model || cds.resolve(!fts ? '*' : ['*',fts], false)
9
+ let file = 'db'
10
+ if (options.to) {
11
+ file = 'sqlite:'+ _database4 (_db, options.to, conf)
12
+ // current 'sqlite' default is bad, as we want to default to 'plain', so ignore it
13
+ const env_dialect = cds.env.sql.dialect !== 'sqlite' ? cds.env.sql.dialect : 'plain'
14
+ const to_dialect = options.to && options.to.match(/^(\w+)/) && RegExp.$1
15
+ if (to_dialect) options.dialect = (to_dialect === 'sql')
16
+ ? conf.dialect ?? env_dialect
17
+ : to_dialect
18
+ }
19
+ return cds.deploy(model) .to (file, options)
19
20
  }
20
21
 
21
22
  function _database4 (_db,_dbrl,conf) {
22
23
  if (_db) return _db
24
+ if (_dbrl === 'sqlite') return 'db.sqlite'
23
25
  if (conf.credentials?.database) return conf.credentials.database
24
26
  if (conf.credentials?.url) return conf.credentials.url
25
- if (_dbrl === 'sqlite') return 'db.sqlite'
26
27
  if (_dbrl === 'sql') return ':memory:'
28
+ else return conf.credentials?.database ?? conf.credentials?.url
27
29
  }
package/bin/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-console */
2
- module.exports = exports = Object.assign (help, {help:`
2
+ module.exports = exports = Object.assign (help, {formatHelp, help:`
3
3
  # USAGE
4
4
 
5
5
  *cds* <command> [<args>]
@@ -39,10 +39,13 @@ module.exports = exports = Object.assign (help, {help:`
39
39
  function help (topic) {
40
40
  if (Array.isArray(topic)) topic = topic[0]
41
41
  const task = !topic ? help : this.load && this.load (this.Shortcuts[topic] || topic)
42
- if (task && task.help) console.log (task.help
42
+ if (task && task.help) console.log (formatHelp (task.help))
43
+ else throw new Error(`Didn't find a help topic for '${ topic.name || topic }'.`)
44
+ }
45
+
46
+ function formatHelp (text) {
47
+ return text
43
48
  .replace(/\n# ([^\n]*)\n/g, `\n\x1b[1m$1\x1b[0m\n`)
44
49
  .replace(/ \*([^*]+)\*/g, ` \x1b[1m$1\x1b[0m`)
45
50
  .replace(/ _([^_]+)_/g, ` \x1b[4m$1\x1b[0m`)
46
- )
47
- else throw new Error(`Didn't find a help topic for '${ topic.name || topic }'.`)
48
51
  }
package/bin/init.js CHANGED
@@ -29,7 +29,7 @@ module.exports = Object.assign(init, {
29
29
  This feature uses the 'Java Maven archetype' to create a project
30
30
  using the *Java* programming language.
31
31
 
32
- *mtx* - prepares a multi-tenant project
32
+ *mtx* - prepares a multitenant project
33
33
 
34
34
  This version only supports Node.js projects.
35
35
 
package/bin/login.js CHANGED
@@ -74,8 +74,6 @@ module.exports = Object.assign(login, {
74
74
  *-u* | *--user* <name>[:[<password>]]
75
75
 
76
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
77
 
80
78
  When typing the colon but omitting <password>, the password is assumed empty.
81
79
  When omitting both, password will be queried interactively.
@@ -117,8 +115,9 @@ module.exports = Object.assign(login, {
117
115
  # SEE ALSO
118
116
 
119
117
  *cds logout* to remove any authentication data
120
- `
121
- }});
118
+ https://cap.cloud.sap/docs/guides/extensibility/customization?q=mock#mock-users for information on suitable mock users.
119
+
120
+ `}});
122
121
 
123
122
 
124
123
  async function login ([url], options = {}) {
package/bin/migrate.js ADDED
@@ -0,0 +1,33 @@
1
+ module.exports = Object.assign(migrate, {
2
+ options: ['--directory'],
3
+ shortcuts: ['-d'],
4
+ flags: ['--dry'],
5
+ help: `
6
+ # SYNOPSIS
7
+
8
+ *cds migrate* <tenant1|"*">[,<tenant2>] -d <directory> --dry
9
+
10
+ Migrate extensions created with @sap/cds-mtx to extensions for @sap/cds-mtxs
11
+
12
+ This command only runs on the root directory of an application.
13
+
14
+ # OPTIONS
15
+
16
+ *-d* | *--directory* <extension-projects-directory>
17
+
18
+ Specify the target directory of your migrated extension projects. By default, the migrated extension projects will not be stored.
19
+
20
+ *--dry*
21
+
22
+ Do a dry-run only. Only run a verification of the extension migration.
23
+
24
+ # SEE ALSO
25
+ `})
26
+
27
+
28
+ async function migrate (param, options = {}) {
29
+ // eslint-disable-next-line cds-dk/no-missing-dependencies
30
+ await require('@sap/cds-mtxs/bin/cds-mtx-migrate').run(param[0], options)
31
+ }
32
+
33
+ /* eslint no-console: off */
package/bin/pull.js CHANGED
@@ -39,7 +39,7 @@ module.exports = Object.assign(pull, {
39
39
 
40
40
  *cds push* to push an extension to the SaaS app.
41
41
  *cds login* for more information on authentication options.
42
- *cds login* to save authentication data, simplifying multiple runs of *cds pull* and *cds push* against the same app.
42
+ *cds login* to save project settings and authentication data, simplifying multiple runs of this command.
43
43
 
44
44
  `});
45
45
 
@@ -49,8 +49,8 @@ async function pull([url], options = {}) {
49
49
  delete options.user;
50
50
  const [clientid, clientsecret] = options.client?.split(':') ?? [];
51
51
  delete options.client;
52
- if (options.from) url = options.from
53
- delete options.from
52
+ if (options.from) url = options.from;
53
+ delete options.from;
54
54
 
55
55
  await require('../lib/client/pull').run({
56
56
  ...options,
@@ -59,5 +59,5 @@ async function pull([url], options = {}) {
59
59
  password,
60
60
  clientid,
61
61
  clientsecret
62
- })
62
+ });
63
63
  }
package/bin/push.js CHANGED
@@ -46,7 +46,7 @@ module.exports = Object.assign(push, {
46
46
 
47
47
  *cds pull* to get the CDS model of the SaaS app.
48
48
  *cds login* for more information on authentication options.
49
- *cds login* to save authentication data, simplifying multiple runs of *cds pull* and *cds push* against the same app.
49
+ *cds login* to save project settings and authentication data, simplifying multiple runs of this command.
50
50
 
51
51
  `});
52
52
 
package/bin/subscribe.js CHANGED
@@ -1,75 +1,67 @@
1
1
  module.exports = Object.assign(subscribe, {
2
- options: ['--to', '--user'],
3
- flags: [],
4
- shortcuts: ['-2', '-u'],
5
- help: `
2
+ init: on => new (require('../lib/client/subscription'))(on),
3
+ options: ['--to', '--user'],
4
+ flags: ['--local'],
5
+ shortcuts: ['-2', '-u', '-l'],
6
+ help: `
6
7
  # SYNOPSIS
7
8
 
8
- *cds subscribe* <tenant>
9
+ *cds subscribe* <tenant>
9
10
 
10
- Subscribe a tenant to a multi-tenant SaaS app.
11
+ Subscribe a tenant to a multitenant SaaS app.
11
12
 
12
- *cds subscribe* only supports Basic Authentication and is intended for non-productive use,
13
- including SaaS apps running on localhost with mock authentication.
13
+ Running *cds subscribe* with a URL is intended for non-productive use only, including against
14
+ SaaS apps running on localhost with mock authentication.
14
15
 
15
16
  # OPTIONS
16
17
 
17
- *-2* | *--to* <url>
18
-
19
- Specify the URL of the extensible SaaS app. Required setting.
20
-
21
- *-u* | *--user* <name>[:<password>]
22
-
23
- Username and optionally password for authentication. Required setting.
24
- See https://cap.cloud.sap/docs/guides/multitenancy/#send-subscribe-events for information on suitable mock users.
25
-
26
- If password is omitted, it is assumed empty.
27
- Note: productive passwords should not be specified on the command line.
28
-
29
- `})
30
-
31
-
32
-
33
- async function subscribe([tenant], { to: url, user } = {}) {
34
-
35
- const cds = require('../lib/cds')
36
-
37
- // check for application environment
38
- // fails for edge cases (ignored for now):
39
- // @sap/cds-mtx application with missing modules (no npm install)
40
- // dev scenario with both @sap/cds-mtx and @sap/cds-mtxs installed
41
- function _hasMtEnv() {
42
- return cds.requires.multitenancy && !cds.mtx
43
- }
44
-
45
-
46
- if (!tenant) {
47
- console.error('Must provide a tenant')
48
- return this.help('subscribe')
49
- }
50
- if (!url) {
51
- // check whether we are in a mt(xs) environment
52
- if (!_hasMtEnv()) throw new Error('Local cds subscribe operation can only be run inside a multitenant application environment using @sap/cds-mtxs. Please specify application url (--to <url>) if you want to subscribe from remote')
53
- console.log ('\nSubscribing', {tenant})
54
- const { 'cds.xt.DeploymentService':ds } = await cds.serve ([
55
- '@sap/cds-mtxs/srv/deployment-service',
56
- '@sap/cds-mtxs/srv/model-provider'
57
- ])
58
- cds.emit('served')
59
- // await cds.connect.to('db')
60
- await ds.subscribe(tenant)
61
- } else {
62
- // Connecting to a deployment service in a subscribed application
63
- if (typeof user !== 'string') throw new Error('Must provide a username')
64
- const [username, password = ''] = user.split(':');
65
- console.log ('\nSubscribing', {tenant}, 'to', {url})
66
- await require('axios').post (url+'/-/cds/deployment/subscribe', {
67
- tenant
68
- }, {
69
- auth: { username, password }
70
- });
71
- }
72
- console.log('Subscription succeeded.\n')
73
- }
18
+ *-2* | *--to* <url>
19
+
20
+ Specify the URL of the SaaS app. Determined from the current user's active CDS or
21
+ Cloud Foundry login by default.
22
+
23
+ *-u* | *--user* <name>[:[<password>]]
24
+
25
+ Username and optionally password for authentication with Basic Auth in test scenarios.
26
+ Required setting if URL is given. (Tokens saved with *cds login* are not used because they
27
+ are unsuitable for this command.)
28
+
29
+ *-l* | *--local*
30
+
31
+ Run a locally-installed @sap/cds-mtxs and connect to it instead of a URL. Ignore settings
32
+ saved with *cds login*.
33
+
74
34
 
75
- // cds subscribe t1 --to http://localhost:4004 -u yves
35
+ # SEE ALSO
36
+
37
+ *cds login* for more information on authentication options.
38
+ *cds login* to save project settings, simplifying multiple runs of this command.
39
+
40
+ `});
41
+
42
+
43
+ async function subscribe([tenant], options = {}) {
44
+ const { CliError } = require('../lib/client/helper/errors');
45
+ if (!tenant) {
46
+ throw new CliError('Tenant not given.', { command: 'subscribe' });
47
+ }
48
+ const libOptions = { tenant };
49
+ if (options.local) {
50
+ libOptions.local = true;
51
+ delete options.local;
52
+ if (options.user) {
53
+ throw new CliError('Username cannot be specified when running @sap/cds-mtxs locally.', { command: 'subscribe' });
54
+ }
55
+ if (options.to) {
56
+ throw new CliError('URL cannot be specified when running @sap/cds-mtxs locally.', { command: 'subscribe' });
57
+ }
58
+ }
59
+ const [username, password] = options.user?.split(':') ?? [];
60
+ delete options.user;
61
+
62
+ await subscribe.init(true).run({
63
+ ...options,
64
+ username,
65
+ password
66
+ }, libOptions);
67
+ }
@@ -0,0 +1,67 @@
1
+ module.exports = Object.assign(unsubscribe, {
2
+ init: on => new (require('../lib/client/subscription'))(on),
3
+ options: ['--from', '--user'],
4
+ flags: ['--local'],
5
+ shortcuts: ['-f', '-u', '-l'],
6
+ help: `
7
+ # SYNOPSIS
8
+
9
+ *cds unsubscribe* <tenant>
10
+
11
+ Unsubscribe a tenant from a multitenant SaaS app.
12
+
13
+ Running *cds unsubscribe* with a URL is intended for non-productive use only, including against
14
+ SaaS apps running on localhost with mock authentication.
15
+
16
+ # OPTIONS
17
+
18
+ *-f* | *--from* <url>
19
+
20
+ Specify the URL of the SaaS app. Determined from the current user's active CDS or
21
+ Cloud Foundry login by default.
22
+
23
+ *-u* | *--user* <name>[:[<password>]]
24
+
25
+ Username and optionally password for authentication with Basic Auth in test scenarios.
26
+ Required setting if URL is given. (Tokens saved with *cds login* are not used because they
27
+ are unsuitable for this command.)
28
+
29
+ *-l* | *--local*
30
+
31
+ Run a locally-installed @sap/cds-mtxs and connect to it instead of a URL. Ignore settings
32
+ saved with *cds login*.
33
+
34
+
35
+ # SEE ALSO
36
+
37
+ *cds login* for more information on authentication options.
38
+ *cds login* to save project settings, simplifying multiple runs of this command.
39
+
40
+ `});
41
+
42
+
43
+ async function unsubscribe([tenant], options = {}) {
44
+ const { CliError } = require('../lib/client/helper/errors');
45
+ if (!tenant) {
46
+ throw new CliError('Tenant not given.', { command: 'unsubscribe' });
47
+ }
48
+ const libOptions = { tenant };
49
+ if (options.local) {
50
+ libOptions.local = true;
51
+ delete options.local;
52
+ if (options.user) {
53
+ throw new CliError('Username cannot be specified when running @sap/cds-mtxs locally.', { command: 'subscribe' });
54
+ }
55
+ if (options.from) {
56
+ throw new CliError('URL cannot be specified when running @sap/cds-mtxs locally.', { command: 'unsubscribe' });
57
+ }
58
+ }
59
+ const [username, password] = options.user?.split(':') ?? [];
60
+ delete options.user;
61
+
62
+ await unsubscribe.init(false).run({
63
+ ...options,
64
+ username,
65
+ password
66
+ }, libOptions);
67
+ }
package/bin/watch.js CHANGED
@@ -11,6 +11,9 @@ module.exports = Object.assign ( watch, {
11
11
  project or the current work directory. Compiles and (re-)runs the server
12
12
  on every change detected.
13
13
 
14
+ Actually, cds watch is just a convenient shortcut for:
15
+ *cds serve all --with-mocks --in-memory?*
16
+
14
17
  # OPTIONS
15
18
 
16
19
  *--port* <number>
@@ -36,9 +39,7 @@ module.exports = Object.assign ( watch, {
36
39
 
37
40
  # SEE ALSO
38
41
 
39
- Actually, *cds watch* is just a convenient shortcut for:
40
- *cds serve* all --with-mocks --in-memory? ...
41
- Check out *cds serve ?* to learn more.
42
+ *cds serve --help* for the different start options
42
43
 
43
44
  `})
44
45
 
@@ -6,6 +6,8 @@ const KindToRequiresNameMap = {
6
6
  xsuaa: 'auth',
7
7
  'hana-cloud': 'db',
8
8
  hana: 'db',
9
+ 'hana-mt': 'db',
10
+ 'sql-mt': 'db',
9
11
  destinations: 'destinations',
10
12
  connectivity: 'connectivity',
11
13
  'audit-log': 'audit-log',
@@ -6,6 +6,7 @@ const util = require('util');
6
6
  const axios = require('axios');
7
7
  const { URL } = require('url');
8
8
  const path = require('path');
9
+ const term = require('../util/term');
9
10
 
10
11
  const execAsync = util.promisify(cp.exec);
11
12
  const IS_WIN = os.platform() === 'win32';
@@ -150,7 +151,10 @@ class CFKeyProvider {
150
151
  name = name || `${instance}:${key}`;
151
152
 
152
153
  if (apiEndpoint && apiEndpoint !== target.apiEndpoint) {
153
- throw new Error(`Current Cloud Foundry api endpoint "${target.apiEndpoint}" different than api endpoint "${apiEndpoint}" for service binding "${name}".`);
154
+ let message = `Current Cloud Foundry API endpoint "${target.apiEndpoint}" differs from API endpoint "${apiEndpoint}" for service binding "${name}".\n`;
155
+ message += `Use ${term.bold(`cf login -a ${apiEndpoint}`)} to log in to the target API endpoint.`;
156
+ console.log(message);
157
+ process.exit(1);
154
158
  }
155
159
 
156
160
  let spaceGuid;
@@ -29,7 +29,7 @@ async function checkFinished(jobId, statusUrl, params, finisher) {
29
29
  finisher.finished = true;
30
30
  clearInterval(finisher.timer);
31
31
  if (status === JOB_STATUS.FAILED) {
32
- return finisher.reject(new CliError(`Job ${jobId} failed`, error));
32
+ return finisher.reject(new CliError(`Job ${jobId} failed`, { error }));
33
33
  }
34
34
  console.log('Extension active');
35
35
  finisher.resolve();
@@ -66,7 +66,7 @@ module.exports = class Activate extends BaseCommand {
66
66
  await this.runCustomFileUpload(params);
67
67
  }
68
68
  } catch (error) {
69
- this.handleHttpError(error, params);
69
+ this.handleHttpError(error, params, { command: 'activate' });
70
70
  }
71
71
  }
72
72
 
@@ -132,7 +132,7 @@ module.exports = class Activate extends BaseCommand {
132
132
  try {
133
133
  pkgJson = await fs.readFile(path.join(absoluteFolder, 'package.json'), 'utf-8');
134
134
  } catch (error) {
135
- throw new CliError('No package.json file found', error);
135
+ throw new CliError('No package.json file found', { error });
136
136
  }
137
137
  allFiles.set('package.json', pkgJson);
138
138
 
@@ -60,7 +60,7 @@ async function retrieveTokenOrPasscodeUrl(params) {
60
60
  try {
61
61
  response = await axios.get(tokenUrl);
62
62
  } catch (error) {
63
- throw new CliError('Failed to get authentication token', error);
63
+ throw new CliError('Failed to get authentication token', { error });
64
64
  }
65
65
  authData = response.data;
66
66
 
@@ -83,7 +83,7 @@ module.exports = class AuthManager {
83
83
  await SettingsManager.loadAndMergeSettings(params);
84
84
  if (params.has('username')) {
85
85
  params.set('reqAuth', { auth: { username: params.get('username'), password: params.get('password') } });
86
- } else {
86
+ } else if (!params.get('skipToken')) {
87
87
  await retrieveTokenOrPasscodeUrl(params);
88
88
  if (!params.has('token')) {
89
89
  if (params.get('saveData')) {
@@ -15,20 +15,21 @@ module.exports = class BaseCommand {
15
15
  return exists;
16
16
  }
17
17
 
18
- static handleHttpError(error, params, url) {
18
+ static handleHttpError(error, params, { url, command } = {}) {
19
19
  url = url ?? params.get('appUrl');
20
20
  const prefix = `Request to ${url} failed`;
21
21
  switch (error.response?.status) {
22
22
  case 401:
23
- throw new CliError(prefix + ': invalid authentication. Retry with valid passcode' +
24
- params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : '', error);
23
+ throw new CliError(`${prefix}: invalid authentication. Retry with valid passcode${
24
+ params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : ''
25
+ }.`, { error, command });
25
26
  case 403:
26
- throw new CliError(prefix + ': insufficient authorization. Ensure client has all necessary roles', error);
27
+ throw new CliError(prefix + ': insufficient authorization. Ensure client has all necessary roles.', { error, command });
27
28
  case 404:
28
- throw new CliError(prefix + ': resource not found.');
29
+ throw new CliError(prefix + ': resource not found.', { command });
29
30
  case 422:
30
31
  // Compilation or native-extension error.
31
- throw new CliError(prefix, error);
32
+ throw new CliError(prefix + '.', { error, command });
32
33
  default:
33
34
  // Unexpected error should be transparent to user.
34
35
  throw error;
@@ -87,13 +87,13 @@ 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')));
90
+ throw new CliError(folderExists(params.get('projectFolder')), { command: 'extend' });
91
91
  }
92
92
 
93
93
  try {
94
94
  await Extend.runExtend(params);
95
95
  } catch (error) {
96
- await Extend.handleHttpError(error, params);
96
+ await Extend.handleHttpError(error, params, { command: 'extend' });
97
97
  }
98
98
  }
99
99
 
@@ -2,11 +2,17 @@ const { getMessage } = require('./logging');
2
2
 
3
3
  class CliError extends Error {
4
4
  // noinspection JSAnnotator
5
- constructor(message, error) {
6
- throw getMessage(message, error);
5
+ constructor(message, { error, command } = {}) {
6
+ const help = command && getCommandHelp(command);
7
+ throw getMessage(message, { error, help });
7
8
  }
8
9
  }
9
10
 
11
+ function getCommandHelp(command) {
12
+ const cli = require('../../../bin/cds');
13
+ return cli?.load(command, () => ({ help: '' }))?.help ?? '';
14
+ }
15
+
10
16
  module.exports = {
11
17
  CliError
12
18
  };