@sap/cds-dk 4.6.4 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/bin/add.js +2 -1
- package/bin/bind.js +121 -48
- package/bin/cds.js +8 -0
- package/bin/env.js +0 -2
- package/bin/repl.js +5 -2
- package/bin/watch.js +10 -7
- package/lib/bind/bindingResolver.js +26 -14
- package/lib/bind/bindingUtil.js +144 -0
- package/lib/bind/cfKeyProvider.js +2 -80
- package/lib/bind/kubernetesCredentialsProvider.js +157 -0
- package/lib/bind/userCdsrcJsonStorageProvider.js +7 -7
- package/lib/client/activate.js +8 -10
- package/lib/import/internalDoc/edm2cdsTypeMapping.md +38 -0
- package/lib/import/utils/MultiMap.js +1 -0
- package/lib/import/v2parser.js +95 -33
- package/lib/import/v4parser.js +57 -18
- package/lib/index.js +6 -5
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +1 -0
- package/lib/init/template/data/index.js +18 -6
- package/lib/init/template/hana/files/common/undeploy.json +5 -0
- package/lib/init/template/hana/files/java/package.json +3 -0
- package/lib/init/template/hana/index.js +5 -5
- package/lib/init/template/lint/index.js +17 -1
- package/lib/init/template/mta/files/mta.yaml.hbs +12 -5
- package/lib/init/util/fsUtil.js +1 -1
- package/lib/lint/Linter.js +1 -1
- package/lib/lint/io.js +1 -10
- package/lib/watch/node-watch.js +46 -20
- package/npm-shrinkwrap.json +8750 -1978
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,35 @@ 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.7.1 - 2021-12-06
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `cds activate` authentication no longer fails
|
|
14
|
+
- `cds.compile.to.openapi` does no longer crash if `cds.compile` was already initialized
|
|
15
|
+
|
|
16
|
+
## Version 4.7.0 - 2021-12-06
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `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.
|
|
21
|
+
- `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.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `cds import` now adds annotations for missing ON conditions in associations instead of appending it in `doc`.
|
|
26
|
+
- `cds import` has updated mapping for OData V2 and V4
|
|
27
|
+
- `cds add lint` now configures `csv` files in `.vscode/settings.json`
|
|
28
|
+
- `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`.
|
|
29
|
+
- `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.
|
|
30
|
+
- `cds watch` now ignores folders named `target`, to avoid restarts when Maven's build output changes
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- `cds import` throws warning messages for unsupported data types.
|
|
35
|
+
- `cds watch` allows the explicit `--with-mocks` option, although this is anyways included by default
|
|
36
|
+
- `cds watch` no longer runs into multiple restarts if many files are changed at once, like in git branch changes
|
|
37
|
+
|
|
9
38
|
## Version 4.6.4 - 2021-11-24
|
|
10
39
|
|
|
11
40
|
### Fixed
|
|
@@ -15,7 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
15
44
|
|
|
16
45
|
### Changed
|
|
17
46
|
|
|
18
|
-
- `cds init` uses latest
|
|
47
|
+
- `cds init` uses latest Maven Java archetype version `1.20.3` for creating Java projects.
|
|
19
48
|
|
|
20
49
|
## Version 4.6.3 - 2021-11-16
|
|
21
50
|
|
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
|
|
39
|
+
Filters entity names with the given string. If it contains meta characters like ^ or *
|
|
40
|
+
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>]
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
|
59
|
-
|
|
60
|
-
options.profile
|
|
63
|
+
async function cds_bind(args, options) {
|
|
64
|
+
options.profile = parseProfile(options.profile);
|
|
65
|
+
setProfile(options.profile);
|
|
61
66
|
|
|
62
|
-
if (
|
|
63
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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 (
|
|
104
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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(
|
|
122
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return
|
|
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 =
|
|
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
|
-
|
|
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'
|
|
61
|
+
args = ['serve', 'all'],
|
|
62
62
|
ext = extDefaults,
|
|
63
63
|
...options
|
|
64
64
|
}={}) {
|
|
65
|
-
|
|
66
|
-
|
|
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,
|
|
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('
|
|
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
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
cfKeyProvider
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 (
|
|
71
|
-
|
|
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
|
+
}
|