@sap/cds-dk 4.6.4 → 4.8.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 (74) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/bin/activate.js +5 -1
  3. package/bin/add.js +2 -1
  4. package/bin/bind.js +121 -48
  5. package/bin/cds.js +8 -0
  6. package/bin/env.js +0 -2
  7. package/bin/repl.js +5 -2
  8. package/bin/watch.js +10 -7
  9. package/lib/bind/bindingResolver.js +26 -14
  10. package/lib/bind/bindingUtil.js +144 -0
  11. package/lib/bind/cfKeyProvider.js +2 -80
  12. package/lib/bind/kubernetesCredentialsProvider.js +157 -0
  13. package/lib/bind/userCdsrcJsonStorageProvider.js +7 -7
  14. package/lib/client/activate.js +16 -13
  15. package/lib/client/base_command.js +5 -1
  16. package/lib/client/helper/cf_helper.js +5 -2
  17. package/lib/client/params.js +16 -2
  18. package/lib/client/settings_manager.js +15 -8
  19. package/lib/compile/openapi/csdl2openapi.js +6 -8
  20. package/lib/compile/to-xsuaa.js +0 -14
  21. package/lib/import/utils/MetadataConverter.js +6 -4
  22. package/lib/import/utils/MultiMap.js +1 -0
  23. package/lib/import/v2parser.js +150 -64
  24. package/lib/import/v4parser.js +118 -56
  25. package/lib/index.js +6 -5
  26. package/lib/init/constants.js +11 -5
  27. package/lib/init/index.js +14 -7
  28. package/lib/init/template/_merging/existences.js +161 -0
  29. package/lib/init/template/approuter/files/default-env.json +9 -0
  30. package/lib/init/template/approuter/files/mta.yaml.hbs +48 -0
  31. package/lib/init/template/approuter/files/package.json +9 -0
  32. package/lib/init/template/approuter/files/xs-app.json +8 -0
  33. package/lib/init/template/approuter/index.js +66 -0
  34. package/lib/init/template/auditlog/files/dependencies.package.json +5 -0
  35. package/lib/init/template/auditlog/files/mta.yaml.hbs +13 -0
  36. package/lib/init/template/auditlog/index.js +38 -0
  37. package/lib/init/template/cf-manifest/index.js +1 -1
  38. package/lib/init/template/data/index.js +18 -6
  39. package/lib/init/template/hana/files/common/undeploy.json +5 -0
  40. package/lib/init/template/hana/files/java/package.json +3 -0
  41. package/lib/init/template/hana/files/mta.yaml.hbs +42 -0
  42. package/lib/init/template/hana/files/nodejs/dependencies.package.json +5 -0
  43. package/lib/init/template/hana/index.js +53 -24
  44. package/lib/init/template/java/index.js +1 -5
  45. package/lib/init/template/lint/index.js +17 -1
  46. package/lib/init/template/mta/files/mta.yaml.hbs +27 -216
  47. package/lib/init/template/mta/index.js +25 -17
  48. package/lib/init/template/mtx/files/cds.package.json +24 -0
  49. package/lib/init/template/mtx/files/dependencies.package.json +5 -0
  50. package/lib/init/template/mtx/files/mta.yaml.hbs +74 -0
  51. package/lib/init/template/mtx/files/xs-security.json.hbs +23 -9
  52. package/lib/init/template/mtx/index.js +126 -29
  53. package/lib/init/template/templateBase.js +14 -0
  54. package/lib/init/template/xsuaa/files/cds.package.json +9 -0
  55. package/lib/init/template/xsuaa/files/dependencies.package.json +6 -0
  56. package/lib/init/template/xsuaa/files/mta.yaml.hbs +17 -0
  57. package/lib/init/template/xsuaa/index.js +75 -0
  58. package/lib/init/util/fsUtil.js +18 -4
  59. package/lib/init/util/logger.js +3 -1
  60. package/lib/init/util/mvnArchetypeUtil.js +3 -1
  61. package/lib/init/{template/mta → util}/projectReader.js +26 -11
  62. package/lib/init/util/templateUtil.js +267 -3
  63. package/lib/lint/Linter.js +1 -2
  64. package/lib/lint/io.js +3 -12
  65. package/lib/util/exec.d.ts +54 -0
  66. package/lib/util/exec.js +210 -0
  67. package/lib/watch/livereload-server.js +43 -31
  68. package/lib/watch/node-watch.js +46 -20
  69. package/lib/watch/open.js +1 -1
  70. package/npm-shrinkwrap.json +3382 -9106
  71. package/package.json +8 -9
  72. package/readme.md +1 -1
  73. package/lib/init/template/mtx/package/cds.package.json +0 -29
  74. package/lib/init/template/mtx/package/dependencies.package.json +0 -9
package/CHANGELOG.md CHANGED
@@ -6,6 +6,73 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
8
8
 
9
+ ## Version 4.8.0 - 2022-01-28
10
+
11
+ ### Added
12
+
13
+ - `cds import` now reflects the entity set and entity container level annotations in the csn.
14
+ - `cds activate --sync` allows to use the synchronous server API for extension upload.
15
+
16
+ ### Changed
17
+
18
+ - The forked package `@mendix/sqlite3` is now used instead of `sqlite3` to overcome CVE-2021-32804. No code changes in applications are needed, as the new package installed by `npm` with the same name `sqlite3`.
19
+ - [beta] The templating for `cds init` and `cds add` has been rewritten from scratch. This will allow for some new, more complex commands, such as `cds add mtx` or `cds add xsuaa`.
20
+
21
+ ### Fixed
22
+
23
+ - `cds import` now omits function imports with `put/delete` kind.
24
+ - `cds import` has fixed the entity type to entity set mapping in OData V2.
25
+ - `cds import` now supports collection type.
26
+ - `cds watch` now picks a free livereload port if the standard port 35729 is already bound
27
+ - `cds extend`, `cds activate`, `cds login`, and `cds logout` now prioritize command-line options over saved settings
28
+ - MTX client now logs fewer characters of secrets in debug output
29
+ - MTX client now handles incomplete error responses better
30
+
31
+ ## Version 4.7.3 - 2022-01-14
32
+
33
+ ### Changed
34
+
35
+ - Bump `follow-redirects` package to 1.14.7 (CVE-2022-0155)
36
+
37
+ ## Version 4.7.2 - 2021-12-15
38
+
39
+ ### Fixed
40
+
41
+ - `cds add lint` uses JSONC parser to read ESLint config files of type `.eslintrc[.js,.cjs,.json]`.
42
+
43
+ ### Changed
44
+
45
+ - `cds init` uses latest Maven Java archetype version `1.21.0` for creating Java projects.
46
+
47
+ ## Version 4.7.1 - 2021-12-06
48
+
49
+ ### Fixed
50
+
51
+ - `cds activate` authentication no longer fails
52
+ - `cds.compile.to.openapi` does no longer crash if `cds.compile` was already initialized
53
+
54
+ ## Version 4.7.0 - 2021-12-06
55
+
56
+ ### Added
57
+
58
+ - `cds add data --for` can be passed a regular expression, allowing more flexible name filters. For example `--for '^Supplier$'` would only match entity `Supplier`, but not `SupplierAddress`. Note that as before, `--for Supplier` is interpreted as `--for '^Supplier.*'`, i.e. matches both entities.
59
+ - `cds init --add hana` and `cds add hana` now add a `undeploy.json` file containing wildcards. This ensures that SAP HANA artefacts are automatically cleaned-up in case views, unique keys or constraint definitions of associations have been changed in CDS model.
60
+
61
+ ### Changed
62
+
63
+ - `cds import` now adds annotations for missing ON conditions in associations instead of appending it in `doc`.
64
+ - `cds import` has updated mapping for OData V2 and V4
65
+ - `cds add lint` now configures `csv` files in `.vscode/settings.json`
66
+ - `cds add hana` for Java projects now adds an `engines.node` version of `^16` to the generated `db/package.json`, to pin the Node.js version. This will help in the future when runtime environments change their default to some version higher than the one supported by `@sap/hdi-deploy`.
67
+ - `cds add mta` for Node.js projects now adds `npm ci` commands instead of `npm install`. This makes use of `package-lock.json` to enforce reproducible builds.
68
+ - `cds watch` now ignores folders named `target`, to avoid restarts when Maven's build output changes
69
+
70
+ ### Fixed
71
+
72
+ - `cds import` throws warning messages for unsupported data types.
73
+ - `cds watch` allows the explicit `--with-mocks` option, although this is anyways included by default
74
+ - `cds watch` no longer runs into multiple restarts if many files are changed at once, like in git branch changes
75
+
9
76
  ## Version 4.6.4 - 2021-11-24
10
77
 
11
78
  ### Fixed
@@ -15,7 +82,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
15
82
 
16
83
  ### Changed
17
84
 
18
- - `cds init` uses latest `Maven Java archetype` version `1.20.3` for creating Java projects.
85
+ - `cds init` uses latest Maven Java archetype version `1.20.3` for creating Java projects.
19
86
 
20
87
  ## Version 4.6.3 - 2021-11-16
21
88
 
package/bin/activate.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = Object.assign(activate, {
2
- options: ['--subdomain', '--passcode', '--username', '--clientid', '--to'],
2
+ options: ['--subdomain', '--passcode', '--username', '--clientid', '--to', '--sync'],
3
3
  flags: ['--undeploy', '--wsdl'],
4
4
  shortcuts: ['-s', '-p', '-u', '-c'],
5
5
  help: `
@@ -39,6 +39,10 @@ module.exports = Object.assign(activate, {
39
39
 
40
40
  Undeploy a previously deployed extension.
41
41
 
42
+ *--sync*
43
+
44
+ Use the synchronous server API to upload extensions.
45
+
42
46
  `})
43
47
 
44
48
 
package/bin/add.js CHANGED
@@ -36,7 +36,8 @@ module.exports = Object.assign(add, {
36
36
 
37
37
  *-4* | *--for* <entity name or prefix>
38
38
 
39
- Filters the CSN model with the given name or prefix
39
+ Filters entity names with the given string. If it contains meta characters like
40
+ '^' or '*', it is treated like a regular expression, otherwise as a prefix.
40
41
 
41
42
  *-o* | *--out* <target folder>
42
43
 
package/bin/bind.js CHANGED
@@ -1,24 +1,27 @@
1
-
2
-
3
1
  module.exports = Object.assign(cds_bind, {
4
- options: ['--to', '--profile', '--kind', '--output-file'],
5
- flags: [],
6
- shortcuts: ['-2', '-p', '-k', '-o'],
2
+ options: ['--to', '--on', '--profile', '--kind', '--output-file'],
3
+ flags: ['--exec'],
4
+ shortcuts: ['-2', '-n', '-p', '-k', '-o'],
7
5
  help: `
8
6
  # SYNOPSIS
9
7
 
10
- *cds bind* <service> [<options>] -- beta feature
8
+ *cds bind* <service> [<options>]
9
+ *cds bind --exec* [--profile <profile>] [--] <command> <args ...>
11
10
 
12
11
  Binds the given service to a service instance by storing connection information in *.cdsrc-private.json*
13
12
  in your project directory. Credentials are not stored in the file but rather retrieved dynamically during *cds watch*.
14
13
  Use option *--output-file* to specify a different path or file.
15
14
 
16
- Currently *cds bind* only supports Node.js projects.
15
+ With *--exec* you can execute arbitrary commands with your service bindings. The service bindings are provided
16
+ in the *VCAP_SERVICES* env variable to the command.
17
17
 
18
18
  # OPTIONS
19
19
 
20
- *-2 | --to* <instance>[:<key>]
21
- bind to a given instance
20
+ *-2 | --to* <instance>[:<key>] | <service-binding> | <secret>
21
+ bind to a given Cloud Foundry instance, Kubernetes service binding or Kubernetes secret.
22
+
23
+ *-n | --on* cf | k8s
24
+ bind to service on Cloud Foundry or Kubernetes, defaults to Cloud Foundry
22
25
 
23
26
  *-p | --profile* <profile>
24
27
  add the given profile, defaults to *hybrid*
@@ -26,7 +29,7 @@ module.exports = Object.assign(cds_bind, {
26
29
  *-k | --kind* <kind>
27
30
  the kind of service
28
31
 
29
- *-o | --output-file <path>*
32
+ *-o | --output-file* <path>
30
33
  save bindings to the given *.cdsrc.json* or *package.json* file. Default is *.cdsrc-private.json*.
31
34
  If *path* is a directory, then it will save it to the *.cdsrc.json* file in that directory.
32
35
 
@@ -36,13 +39,12 @@ module.exports = Object.assign(cds_bind, {
36
39
  cds bind --to my-hdi-container,my-xsuaa
37
40
  cds bind uaa --to my-xsuaa:my-xsuaa-key --kind xsuaa --profile myprofile
38
41
 
39
- cds bind --to my-hdi-container --output-file ~
42
+ cds bind --to my-hdi-container --output-file .
40
43
  cds bind --to my-hdi-container --output-file package.json
41
44
 
42
- `});
43
-
44
- const { constants, promises: fsp } = require('fs');
45
- const path = require('path');
45
+ cds bind --to my-hdi-container --on k8s
46
+ `
47
+ });
46
48
 
47
49
  const cds = require('@sap/cds/lib');
48
50
  const LOG = cds.log('bind');
@@ -50,19 +52,25 @@ const LOG = cds.log('bind');
50
52
  LOG.isDebug = LOG._debug;
51
53
  LOG.isTTY = process.stdout.isTTY;
52
54
 
53
- const { bold, info } = require('../lib/util/term');
55
+ const { highlight, bold, info } = require('../lib/util/term');
54
56
 
55
57
  const Spinner = require('../lib/init/util/spinner');
58
+ const resolveBinding = require('../lib/bind/bindingResolver').resolver(LOG);
59
+ const { determineKinds, getRequiresNameFromKind, execAndExit, checkIsJavaProject, throwError, platformText, bindingText } = require('../lib/bind/bindingUtil');
56
60
 
61
+ const VCAP_LABEL_PLACEHOLDER = '<replace with service label>';
57
62
 
58
- async function cds_bind(args, options) { // eslint-disable-line
59
-
60
- options.profile = options.profile || 'hybrid';
63
+ async function cds_bind(args, options) {
64
+ options.profile = parseProfile(options.profile);
65
+ setProfile(options.profile);
61
66
 
62
- if (await isJavaProject('.')) {
63
- throw new Error(`Found a 'pom.xml' file in your project root folder indicating a Java project. Currently, 'cds bind' only supports Node.js projects.`);
67
+ if (options.exec) {
68
+ cds_bind_exec(args, options)
69
+ return;
64
70
  }
65
71
 
72
+ const env = cds.env.for('cds', process.cwd());
73
+
66
74
  if (!options.to) {
67
75
  throw new Error(`use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`);
68
76
  }
@@ -71,6 +79,8 @@ async function cds_bind(args, options) { // eslint-disable-line
71
79
  throw new Error(`Too many arguments: Please specify only one or no service.`);
72
80
  }
73
81
 
82
+ const isJavaProject = await checkIsJavaProject(process.cwd());
83
+
74
84
  let serviceArg = args[0]
75
85
  const targets = options.to.split(/,/g);
76
86
 
@@ -82,31 +92,72 @@ async function cds_bind(args, options) { // eslint-disable-line
82
92
  throw new Error(`The option '--kind' cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the '--kind' option.`);
83
93
  }
84
94
 
85
- // prepare for different providers
86
- const keyProvider = require('../lib/bind/cfKeyProvider');
87
- keyProvider.setLogger(LOG);
88
95
  const spinner = new Spinner(LOG);
89
- const resolvedServices = await spinner.run(`Retrieving data from Cloud Foundry...`, async () => { // NOSONAR
90
- const resolvedServices = [];
91
- await Promise.all( targets.map( async target => {
92
- const [instance, key] = target.split(/:/);
93
- const resolvedService = await keyProvider.resolve(undefined, { instance, key });
94
- // if (!options["save-credentials"]) {
95
- delete resolvedService.credentials;
96
- resolvedService.binding.resolved = false;
97
- // }
98
- resolvedServices.push(resolvedService);
96
+
97
+ let on;
98
+ if (options.on === 'k8s' || options.on === 'kubernetes') {
99
+ on = 'k8s';
100
+ } else if (!options.on || options.on === 'cf' || options.on === 'cloudfoundry') {
101
+ on = 'cf';
102
+ } else {
103
+ throw new Error(`Invalid value '${options.on}' for option --on.`);
104
+ }
105
+
106
+ const onText = { 'cf': 'Cloud Foundry', 'k8s': 'Kubernetes' }[on];
107
+ const resolvedServices = await spinner.run(`Retrieving data from ${onText}...`, async () => { // NOSONAR
108
+ return await Promise.all( targets.map( async target => {
109
+
110
+ let binding;
111
+ if (on === 'k8s') {
112
+ binding = { type: on, name: target };
113
+ } else if (on === 'cf') {
114
+ const [instance, key] = target.split(/:/);
115
+ binding = { type: on, instance, key };
116
+ } else {
117
+ throwError();
118
+ }
119
+
120
+ const resolvedService = await resolveBinding(undefined, binding);
121
+ resolvedService._target = target;
122
+ delete resolvedService.credentials;
123
+ resolvedService.binding.resolved = false;
124
+ return resolvedService;
99
125
  }));
100
- return resolvedServices;
101
126
  });
102
127
 
103
- if (!options.kind) {
104
- keyProvider.determineKinds(resolvedServices);
128
+ if (options.kind) {
129
+ resolvedServices.forEach( service => service.kind = options.kind );
130
+ } else {
131
+ determineKinds(resolvedServices);
105
132
  }
106
133
 
107
134
  for (const resolvedService of resolvedServices) {
108
- resolvedService.kind = options.kind || resolvedService.kind;
109
- resolvedService.name = serviceArg || keyProvider.getRequiresNameFromKind(resolvedService.binding.instance, resolvedService.kind);
135
+ if (serviceArg) {
136
+ resolvedService.name = serviceArg;
137
+ } else if (resolvedService.kind) {
138
+ resolvedService.name = getRequiresNameFromKind(resolvedService.binding.instance, resolvedService.kind);
139
+ }
140
+
141
+ if (!resolvedService.name) {
142
+ if (isJavaProject) {
143
+ const { binding } = resolvedService;
144
+ // Generate a custom service entry for Java projects w/o the need to specify a name manually
145
+ resolvedService.name = `custom-service:${binding.instance || binding.secret}`
146
+ } else {
147
+ throw new Error(`Unknown CDS service name for service "${resolvedService._target}". Please specify as argument for "cds bind".`)
148
+ }
149
+ }
150
+
151
+ const { kind, name, binding } = resolvedService;
152
+ const kindText = kind && `with kind ${highlight(kind)}`;
153
+ const nameText = !isJavaProject && highlight(name);
154
+
155
+ LOG.log(['Binding', nameText, 'to', platformText(binding), bindingText(binding, highlight), kindText].filter( t => t ).join(' '));
156
+
157
+ if (isJavaProject && !hasVcapLabel(binding) && !hasVcapLabel(env.requires[resolvedService.name]) && !(kind && hasVcapLabel(env.requires[kind]))) {
158
+ resolvedService.vcap = { label: VCAP_LABEL_PLACEHOLDER };
159
+ LOG.log(`${info('INFO:')} Please add a "vcap.label" property to the binding for consumption in CAP Java projects`);
160
+ }
110
161
  }
111
162
 
112
163
  // prepare for different providers
@@ -118,16 +169,38 @@ async function cds_bind(args, options) { // eslint-disable-line
118
169
  });
119
170
 
120
171
  LOG.log();
121
- LOG.log(`${info("TIP:")} Run with cloud bindings: ${bold(`cds watch --profile ${options.profile}`)}`);
122
- // LOG.log();
172
+ LOG.log(`${info('TIP:')} Run with cloud bindings: ${bold(runCmd(options, isJavaProject))}`);
173
+ }
174
+
175
+ async function cds_bind_exec(command) { // eslint-disable-lint
176
+ const cds = require('@sap/cds/lib');
177
+ const env = cds.env.for('cds', process.cwd());
178
+ env._emulate_vcap_services();
179
+ await require('../lib/bind/bindingResolver').applyTo({env, output: 'info'});
180
+ execAndExit(...command);
181
+ }
182
+
183
+ function hasVcapLabel(service) {
184
+ return service && service.vcap && service.vcap.label && service.vcap.label != VCAP_LABEL_PLACEHOLDER;
123
185
  }
124
186
 
125
- async function isJavaProject(projectPath) {
126
- const pomXmlPath = path.join(projectPath, 'pom.xml');
127
- try {
128
- await fsp.access(pomXmlPath, constants.F_OK);
129
- return true;
130
- } catch (err) {
131
- return false;
187
+ function runCmd({profile}, isJavaProject) {
188
+ if (isJavaProject) {
189
+ const p = profile === 'hybrid' ? '' : ` --profile ${profile}`;
190
+ return `cds bind exec${p} mvn spring-boot:run`;
191
+ } else {
192
+ const p = profile === 'development' ? '' : ` --profile ${profile}`;
193
+ return `cds watch${p}`;
132
194
  }
133
195
  }
196
+
197
+ function setProfile(profile) {
198
+ profile = parseProfile(profile);
199
+ if (profile) process.env.CDS_ENV = profile;
200
+ }
201
+
202
+ function parseProfile(profile) {
203
+ if (!profile) return 'hybrid';
204
+ else if (profile === '-') return 'development';
205
+ else return profile;
206
+ }
package/bin/cds.js CHANGED
@@ -39,7 +39,15 @@ const cli = { //NOSONAR
39
39
 
40
40
  const task = this.load (cmd, /*else*/ (f)=> argv.unshift(cmd) && _defaultCompile (argv,f))
41
41
  if (task) {
42
+ // REVISIT: '--' handling for cds bind --exec. Should be moved to cds_cli
43
+ const endOfOptions = argv.indexOf('--')
44
+ let appendArgs = []
45
+ if (endOfOptions>=0) {
46
+ appendArgs = argv.slice(endOfOptions+1)
47
+ argv = argv.slice(0, endOfOptions)
48
+ }
42
49
  const args = cds_cli.args(task,argv)
50
+ args[0].push(...appendArgs)
43
51
  return task.apply (this, args)
44
52
  }
45
53
  },
package/bin/env.js CHANGED
@@ -50,8 +50,6 @@ async function env ([cmd, key, value], options={}) {
50
50
  throw new Error (`Don't understand '${cmd}' here...`)
51
51
  })
52
52
 
53
- if (options.profile) process.env.CDS_ENV = options.profile
54
-
55
53
  const env = options.for ? cds.env.for (options.for) : cds.env
56
54
  env['_home_cds-dk'] = path.resolve(__dirname, '..') // to help tools find the DK
57
55
 
package/bin/repl.js CHANGED
@@ -31,7 +31,8 @@ function repl (_, options={}) {
31
31
  const info = colors ? require('../lib/util/term').info : s => s
32
32
  log(info('Welcome to cds repl v'+ cds.version))
33
33
 
34
- const cdsr = cds.repl = require('repl').start ({...options, writer:_writer, ignoreUndefined:true })
34
+ const cdsr = require('repl').start ({...options, writer:_writer, ignoreUndefined:true })
35
+ Object.defineProperty (cds,'repl',{value:cdsr})
35
36
 
36
37
  cds.lazify (Object.assign (cdsr.context,{ cds,
37
38
  Foo: lazy => new cds.entity ({name:'Foo'}),
@@ -39,7 +40,9 @@ function repl (_, options={}) {
39
40
  }))
40
41
 
41
42
  // cosmetics: don't show cds.env when inspecting cds
42
- cds.env && Reflect.defineProperty (cds,'env',{ value:cds.env, enumerable:false })
43
+ if (Reflect.getOwnPropertyDescriptor(cds,'env')) {
44
+ Reflect.defineProperty (cds,'env',{ value:cds.env, enumerable:false })
45
+ }
43
46
 
44
47
  inspect.defaultOptions.depth = 11
45
48
  function _writer (o) {
package/bin/watch.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const watchOnlyOptions = ['--ext', '--livereload', '--open']
2
2
  module.exports = Object.assign ( watch, {
3
- flags: [ '--debug', '--in-memory', '--in-memory?' ],
3
+ flags: [ '--debug', '--in-memory', '--in-memory?', '--with-mocks' ],
4
4
  options: [ '--port', ...watchOnlyOptions ],
5
5
  help: `
6
6
  # SYNOPSIS
@@ -55,16 +55,19 @@ const t = module.exports.codes = {
55
55
  orange: '\x1b[38;2;255;140;0m' // darker orange, works with bright and dark background
56
56
  }
57
57
  const extDefaults = 'cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml,env'
58
- let ignore = /node_modules|\.cds-services\.json$/
58
+ let ignore = /node_modules|target|\.cds-services\.json$/
59
59
 
60
60
  async function watch ([cwd], {
61
- args = ['serve', 'all', '--with-mocks'],
61
+ args = ['serve', 'all'],
62
62
  ext = extDefaults,
63
63
  ...options
64
64
  }={}) {
65
- args.push (`--${Object.keys(options).find(a => /^in-memory\??$/.test(a)) || 'in-memory?'}`)
66
- args.push (...process.argv.slice(3)
65
+ if (!Object.keys(options).includes('with-mocks')) args.push ('--with-mocks')
66
+ if (!Object.keys(options).find(a => /^in-memory\??$/.test(a))) args.push ('--in-memory?')
67
+ args
68
+ .push (...(process.argv.slice(3).filter(a => !args.includes(a))) // add deduped command line args
67
69
  .filter(removeArgsIn(watchOnlyOptions)) // remove all watch-only options, as `cds serve` would not accept them
70
+ .filter(a => a !== '--watch') // from `cds serve --watch`
68
71
  .filter(a => a !== cwd))
69
72
 
70
73
  let env = Object.assign (process.env, {
@@ -105,7 +108,7 @@ async function watch ([cwd], {
105
108
 
106
109
  const bindingManager = new require('../lib/bind/bindingResolver').bindingManager({cwd})
107
110
  const bindings = await bindingManager.resolve()
108
- if (bindings) env = {...env, CDS_CONFIG: JSON.stringify(bindings)}
111
+ if (bindings) env = {...env, 'CDS_REQUIRES': JSON.stringify(bindings.requires)}
109
112
 
110
113
  const nodemon = require('../lib/watch/node-watch')
111
114
  const watcher = nodemon ({
@@ -121,7 +124,7 @@ async function watch ([cwd], {
121
124
  bindingManager.update({
122
125
  onBeforeUpdate: () => watcher.stop(),
123
126
  onAfterUpdate: bindings => {
124
- watcher.setEnv('CDS_CONFIG', bindings && JSON.stringify(bindings))
127
+ watcher.setEnv('CDS_REQUIRES', bindings && JSON.stringify(bindings.requires))
125
128
  watcher.restart()
126
129
  }
127
130
  })
@@ -1,6 +1,8 @@
1
1
  const Config = require('@sap/cds/lib/env')
2
2
  const cds = require('@sap/cds/lib');
3
3
  const LOG = cds.log('bind');
4
+ const resolveBinding = resolver(LOG);
5
+ const { platformText, bindingText } = require('./bindingUtil');
4
6
 
5
7
  function getUnresolvedBindings({cwd = process.cwd(), env}) {
6
8
  if (!env) env = Config.for('cds', cwd)
@@ -28,7 +30,7 @@ async function resolveBindings({bindings, cwd = process.cwd(), env, output = 'in
28
30
  const cacheKey = JSON.stringify(binding)
29
31
  if (!cache[cacheKey]) {
30
32
  cache[cacheKey] = config.requires[name] = await resolveBinding(name, binding)
31
- LOG[output](`bound ${name} to Cloud Foundry service instance ${binding.instance}`)
33
+ LOG[output](`bound ${name} to ${platformText(binding)} ${bindingText(binding)}`)
32
34
  } else {
33
35
  config.requires[name] = cache[cacheKey]
34
36
  }
@@ -43,17 +45,26 @@ async function resolveBindings({bindings, cwd = process.cwd(), env, output = 'in
43
45
  }
44
46
 
45
47
  let cfKeyProvider
46
-
47
- async function resolveBinding(name, binding) {
48
- switch (binding.type) {
49
- case 'cf':
50
- if (!cfKeyProvider) {
51
- cfKeyProvider = require('./cfKeyProvider');
52
- cfKeyProvider.setLogger(LOG);
53
- }
54
- return cfKeyProvider.resolve(name, binding);
55
- default:
56
- throw new Error(`Unsupported binding type "${binding.type}" for service "${name}".`)
48
+ let kubernetesCredentialsProvider
49
+
50
+ function resolver(log) {
51
+ return function resolveBinding(name, binding) {
52
+ switch (binding.type) {
53
+ case 'cf':
54
+ if (!cfKeyProvider) {
55
+ cfKeyProvider = require('./cfKeyProvider');
56
+ cfKeyProvider.setLogger(log);
57
+ }
58
+ return cfKeyProvider.resolve(name, binding)
59
+ case 'k8s':
60
+ if (!kubernetesCredentialsProvider) {
61
+ kubernetesCredentialsProvider = require('./kubernetesCredentialsProvider')
62
+ kubernetesCredentialsProvider.setLogger(log)
63
+ }
64
+ return kubernetesCredentialsProvider.resolve(name, binding)
65
+ default:
66
+ throw new Error(`Unsupported binding type "${binding.type}" for service "${name}".`)
67
+ }
57
68
  }
58
69
  }
59
70
 
@@ -67,8 +78,8 @@ async function applyTo(options) {
67
78
  Object.assign(env.requires[name], { binding, credentials })
68
79
  }
69
80
 
70
- if (Config._emulate_vcap_services) {
71
- Config._emulate_vcap_services()
81
+ if (env._emulate_vcap_services) {
82
+ env._emulate_vcap_services()
72
83
  }
73
84
  }
74
85
 
@@ -131,6 +142,7 @@ function feature(env) {
131
142
  }
132
143
 
133
144
  module.exports = {
145
+ resolver,
134
146
  applyTo,
135
147
  bindingManager: function(options) {
136
148
  return new BindingManager(options)
@@ -0,0 +1,144 @@
1
+ const KindToRequiresNameMap = {
2
+ xsuaa: 'uaa',
3
+ hana: 'db',
4
+ destinations: 'destinations',
5
+ connectivity: 'connectivity',
6
+ 'audit-log': 'audit-log',
7
+ 'enterprise-messaging': 'messaging',
8
+ 'enterprise-messaging-amqp': 'messaging',
9
+ 'enterprise-messaging-http': 'messaging',
10
+ 'enterprise-messaging-shared': 'messaging'
11
+ }
12
+
13
+ function getRequiresNameFromKind(service, kind) {
14
+ const requiresName = KindToRequiresNameMap[kind];
15
+ if (!requiresName) throw new Error(`Unknown CDS service name for service "${service}". Please specify as argument for "cds bind".`)
16
+ return requiresName;
17
+ }
18
+
19
+ /**
20
+ * Determine kind value for services based on VCAP binding
21
+ * information in the configuration.
22
+ */
23
+ function determineKinds(services) {
24
+ const vcapServices = {}
25
+
26
+ const getIndex = service => {
27
+ const { credentials } = service;
28
+ return credentials && credentials.binding && credentials.binding.index;
29
+ }
30
+
31
+ // Build VCAP_SERVICES
32
+ for (let index = 0; index<services.length; index++) {
33
+ const service = services[index];
34
+ service.kindCandidates = [ ]
35
+ service.nameCandidates = [ ]
36
+
37
+ const { label, name, tags } = service.binding && service.binding.vcap || {};
38
+ if (!label) continue;
39
+
40
+ if (!vcapServices[label]) vcapServices[label] = [];
41
+ vcapServices[label].push({ credentials: { binding: { index } }, label, name, tags });
42
+ }
43
+
44
+ // Auto configure services based on VCAP_SERVICES
45
+ const cds = require('@sap/cds/lib');
46
+ const VCAP_SERVICES = process.env.VCAP_SERVICES;
47
+ try {
48
+ process.env.VCAP_SERVICES = JSON.stringify(vcapServices);
49
+ const env = cds.env.for('cds', '.');
50
+
51
+ // Extract "kind" from auto configured services
52
+ for (const kind of Object.keys(env.requires)) {
53
+ const requireService = env.requires[kind];
54
+ const superKind = requireService.kind;
55
+ const index = getIndex(requireService);
56
+ if (!Number.isInteger(index)) continue;
57
+
58
+ const superRequireService = env.requires[superKind];
59
+ if (superRequireService) {
60
+ const superIndex = getIndex(superRequireService);
61
+ if (index === superIndex) continue;
62
+ }
63
+
64
+ services[index].kindCandidates.push(kind);
65
+ }
66
+
67
+ // Check if one "kind" per service was found
68
+ for (const service of services) {
69
+ if (service.kindCandidates.length === 1) {
70
+ service.kind = service.kindCandidates[0];
71
+ }
72
+
73
+ delete service.kindCandidates;
74
+ }
75
+ } finally {
76
+ if (VCAP_SERVICES)
77
+ process.env.VCAP_SERVICES = VCAP_SERVICES;
78
+ else
79
+ delete process.env.VCAP_SERVICES;
80
+ }
81
+ }
82
+
83
+ function execAndExit(command, ...args) {
84
+ const { spawnSync } = require('child_process');
85
+ const result = spawnSync(command, args, { stdio: 'inherit', shell: false });
86
+ process.exit(result.status);
87
+ }
88
+
89
+ async function checkIsJavaProject(projectPath) {
90
+ const { constants, promises: fsp } = require('fs');
91
+ const path = require('path');
92
+ const pomXmlPath = path.join(projectPath, 'pom.xml');
93
+ try {
94
+ await fsp.access(pomXmlPath, constants.F_OK);
95
+ return true;
96
+ } catch (err) {
97
+ return false;
98
+ }
99
+ }
100
+
101
+
102
+ /**
103
+ *
104
+ * @param {string} msg
105
+ * @returns {never}
106
+ */
107
+ function throwError(msg = 'Internal Error') {
108
+ throw new Error(msg);
109
+ }
110
+
111
+
112
+ const PLATFORM_TYPES = {
113
+ 'cf': 'Cloud Foundry',
114
+ 'k8s': 'Kubernetes'
115
+ }
116
+
117
+ function platformText(binding) {
118
+ return PLATFORM_TYPES[binding.type] || throwError(`Unexpected binding type "${binding.type}"`);
119
+ }
120
+
121
+ function bindingText(binding, highlight = t => t) {
122
+ switch (binding.type) {
123
+ case 'cf':
124
+ return `service ${highlight(binding.instance)} key ${highlight(binding.key)}`
125
+ case 'k8s':
126
+ if (binding.name) {
127
+ return `service binding ${highlight(binding.name)}`
128
+ } else {
129
+ return `secret ${highlight(binding.secret)}`
130
+ }
131
+ default:
132
+ throwError();
133
+ }
134
+ }
135
+
136
+ module.exports = {
137
+ getRequiresNameFromKind,
138
+ determineKinds,
139
+ execAndExit,
140
+ checkIsJavaProject,
141
+ throwError,
142
+ platformText,
143
+ bindingText
144
+ }