@sap/cds-dk 4.9.7 → 6.0.3
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 +77 -0
- package/bin/activate.js +4 -4
- package/bin/add.js +11 -5
- package/bin/bind.js +16 -28
- package/bin/build/all.js +2 -2
- package/bin/build/index.js +12 -19
- package/bin/cds.js +22 -19
- package/bin/compile/index.js +12 -3
- package/bin/deploy/index.js +15 -9
- package/bin/deploy/to-hana.js +4 -4
- package/bin/deploy/to-sqlite.js +1 -1
- package/bin/env.js +13 -4
- package/bin/eval.js +1 -1
- package/bin/fix-redirects.js +1 -1
- package/bin/import.js +44 -179
- package/bin/repl.js +1 -1
- package/bin/watch.js +10 -15
- package/lib/bind/BindingManager.js +87 -0
- package/lib/bind/bindingResolver.js +15 -87
- package/lib/bind/bindingUtil.js +18 -11
- package/lib/bind/cfKeyProvider.js +58 -29
- package/lib/bind/kubernetesCredentialsProvider.js +44 -21
- package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
- package/lib/cds.js +13 -0
- package/lib/client/base_command.js +3 -2
- package/lib/client/extend.js +6 -7
- package/lib/client/helper/fs_helper.js +7 -8
- package/lib/client/settings_manager.js +14 -10
- package/lib/compile/index.js +9 -10
- package/lib/compile/openapi/csdl2openapi.js +151 -43
- package/lib/compile/openapi/index.js +2 -2
- package/lib/compile/to-xsuaa.js +3 -2
- package/lib/import/common.js +342 -27
- package/lib/import/importapi.js +81 -13
- package/lib/import/message.js +4 -4
- package/lib/import/odata/index.js +141 -0
- package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
- package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
- package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
- package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
- package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
- package/lib/import/openapi/importOpenAPI.js +521 -0
- package/lib/import/openapi/index.js +19 -0
- package/lib/import/openapi/utilities.js +38 -0
- package/lib/index.js +1 -1
- package/lib/init/constants.js +7 -2
- package/lib/init/index.js +10 -3
- package/lib/init/template/_merging/existences.js +79 -19
- package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
- package/lib/init/template/approuter/index.js +61 -54
- package/lib/init/template/auditlog/index.js +2 -2
- package/lib/init/template/data/index.js +2 -2
- package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
- package/lib/init/template/hana/index.js +8 -4
- package/lib/init/template/helm/index.js +39 -0
- package/lib/init/template/helm/update/index.js +28 -0
- package/lib/init/template/java/index.js +7 -9
- package/lib/init/template/kibana-logging/index.js +5 -6
- package/lib/init/template/lint/index.js +1 -1
- package/lib/init/template/mta/index.js +4 -4
- package/lib/init/template/mtx/files/java/package.json +9 -9
- package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
- package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
- package/lib/init/template/mtx/index.js +50 -57
- package/lib/init/template/nodejs/files/package.json.hbs +2 -2
- package/lib/init/template/notebook/index.js +2 -0
- package/lib/init/template/templateBase.js +8 -6
- package/lib/init/template/xsuaa/index.js +9 -5
- package/lib/init/util/fsUtil.js +25 -10
- package/lib/init/util/logger.js +1 -1
- package/lib/init/util/mvnArchetypeUtil.js +4 -10
- package/lib/init/util/projectReader.js +17 -9
- package/lib/init/util/templateUtil.js +30 -34
- package/lib/k8s/ChartAdder.js +65 -0
- package/lib/k8s/ChartBuilder.js +186 -0
- package/lib/k8s/Context.js +71 -0
- package/lib/k8s/ImageUtil.js +21 -0
- package/lib/k8s/Model.d.ts +53 -0
- package/lib/k8s/auditlog/index.js +29 -0
- package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
- package/lib/k8s/buildModel.js +70 -0
- package/lib/k8s/connectivity/index.js +80 -0
- package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
- package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +20 -0
- package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
- package/lib/k8s/deployment/index.js +81 -0
- package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
- package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
- package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
- package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
- package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
- package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
- package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
- package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
- package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
- package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
- package/lib/k8s/deployment/subchart/values.schema.json +449 -0
- package/lib/k8s/deployment/subchart/values.yaml +76 -0
- package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
- package/lib/k8s/destinations/index.js +40 -0
- package/lib/k8s/destinations/lib.js +16 -0
- package/lib/k8s/destinations/templates/destinations.yaml +1 -0
- package/lib/k8s/docker/push.js +8 -0
- package/lib/k8s/event-mesh/index.js +53 -0
- package/lib/k8s/event-mesh/lib.js +9 -0
- package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
- package/lib/k8s/hana-deployer/index.js +38 -0
- package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
- package/lib/k8s/helm/Release.js +80 -0
- package/lib/k8s/html5-apps-deployer/index.js +43 -0
- package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
- package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
- package/lib/k8s/html5-apps-repo-host/index.js +26 -0
- package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
- package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
- package/lib/k8s/index.js +153 -0
- package/lib/k8s/modules.js +36 -0
- package/lib/k8s/multitenancy/index.js +24 -0
- package/lib/k8s/multitenancy/lib.js +25 -0
- package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
- package/lib/k8s/readProject.js +14 -0
- package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
- package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
- package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
- package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
- package/lib/k8s/util.js +186 -0
- package/lib/k8s/wrapFeature.js +55 -0
- package/lib/k8s/xsuaa/index.js +64 -0
- package/lib/k8s/xsuaa/lib.js +17 -0
- package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
- package/lib/lint/generators.js +1 -1
- package/lib/util/exec.js +1 -1
- package/lib/util/write.js +1 -1
- package/lib/watch/node-watch.js +43 -14
- package/npm-shrinkwrap.json +923 -2705
- package/package.json +5 -5
- package/lib/import/index.js +0 -235
package/bin/import.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
|
|
2
2
|
module.exports = Object.assign(_import, {
|
|
3
3
|
_name: 'import',
|
|
4
|
-
options: ['--out', '--
|
|
4
|
+
options: ['--out', '--as', '--include-namespaces', '--from'],
|
|
5
5
|
flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace'],
|
|
6
|
-
shortcuts: ['-o', '-
|
|
6
|
+
shortcuts: ['-o', '-as', '-ns', undefined, '-', '-f'],
|
|
7
7
|
help: `
|
|
8
8
|
# SYNOPSIS
|
|
9
9
|
|
|
10
10
|
*cds import* <source> [<options>]
|
|
11
11
|
|
|
12
12
|
Imports the given source and converts it to CSN.
|
|
13
|
-
Currently
|
|
13
|
+
Currently following file formats are supported.
|
|
14
|
+
- EDMX and XML for OData V2 and V4
|
|
15
|
+
- JSON for OpenAPI
|
|
14
16
|
|
|
15
17
|
Without any options the source is copied to ./srv/external and the csn
|
|
16
18
|
output written next to it. Finally it will add an entry for the imported
|
|
@@ -25,13 +27,6 @@ module.exports = Object.assign(_import, {
|
|
|
25
27
|
*--no-save*
|
|
26
28
|
Skips updating ./package.json.
|
|
27
29
|
|
|
28
|
-
*--keep-namespace*
|
|
29
|
-
Keeps the original namespace from the edmx content instead of using
|
|
30
|
-
the filename by default.
|
|
31
|
-
|
|
32
|
-
*-in | --into* <filename>
|
|
33
|
-
Skips copying to ./srv/external and writes to the specified location.
|
|
34
|
-
|
|
35
30
|
*-o | --out* <filename>
|
|
36
31
|
Skips copying to ./srv/external and writes to the specified location.
|
|
37
32
|
|
|
@@ -39,194 +34,64 @@ module.exports = Object.assign(_import, {
|
|
|
39
34
|
Writes the converted csn to stdout only.
|
|
40
35
|
|
|
41
36
|
*-as | --as <output_file_format>*
|
|
42
|
-
Converts the input
|
|
37
|
+
Converts the input file into one of these supported formats "cds", "csn" or "json".
|
|
43
38
|
|
|
44
39
|
*-f | --force*
|
|
45
40
|
Forcefully overwrites the contents in the CDS file and updates the checksum
|
|
46
|
-
|
|
41
|
+
|
|
42
|
+
*--from*
|
|
43
|
+
Imports the model from the one of the given options "edmx" or "openapi".
|
|
44
|
+
|
|
45
|
+
*Below options are valid for OData only*
|
|
46
|
+
|
|
47
|
+
*--keep-namespace*
|
|
48
|
+
Keeps the original namespace from the edmx content instead of using
|
|
49
|
+
the filename by default.
|
|
50
|
+
|
|
47
51
|
*-ns | --include-namespaces <string_of_namespaces> or* "\x2A"
|
|
48
52
|
Imports only entities matching to the given list of namespaces.
|
|
49
53
|
For OData V2 attributes with the namespace "sap" & "m" are captured by default.
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
|
|
52
56
|
|
|
53
57
|
# EXAMPLES
|
|
54
58
|
|
|
55
59
|
cds import ~/Downloads/API_BUSINESS_PARTNER.edmx
|
|
56
60
|
cds import - ~/Downloads/API_BUSINESS_PARTNER.edmx
|
|
57
|
-
cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --as cds
|
|
61
|
+
cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --from edmx --as cds
|
|
58
62
|
cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --include-namespaces "sap,c4c"
|
|
59
63
|
cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --include-namespaces "\x2A"
|
|
64
|
+
cds import ~/Downloads/Petstore_OpenAPI.json
|
|
65
|
+
cds import ~/Downloads/Petstore_OpenAPI.json --from openapi
|
|
60
66
|
|
|
61
67
|
`})
|
|
62
|
-
const cds = require('@sap/cds/lib')
|
|
63
|
-
const { fs, path, write, mkdirp, exists, read} = cds.utils
|
|
64
|
-
const edm2csn = require('../lib/import')
|
|
65
68
|
const messages = require('../lib/import/message').getMessages();
|
|
66
69
|
const common = require('../lib/import/common');
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (!file) return this.help('import')
|
|
88
|
-
file = await edm2csn.isValidInputFile(path.resolve(cwd, file))
|
|
89
|
-
const external = path.resolve(cwd, 'srv/external')
|
|
90
|
-
const dst = path.join(external, path.basename(file))
|
|
91
|
-
if (!dry && !no_copy) file = await _copy_to_srv_external(file, dst)
|
|
92
|
-
const src = await edm2csn.getMetadataFromFile(file)
|
|
93
|
-
this.include_all_namespaces = false;
|
|
94
|
-
let namespaces = [];
|
|
95
|
-
if(include_namespaces) {
|
|
96
|
-
if(include_namespaces === "*") this.include_all_namespaces = true;
|
|
97
|
-
else if(typeof include_namespaces === 'string' && !include_namespaces.startsWith('-')) namespaces = include_namespaces.replace(/ /g, "").split(',');
|
|
98
|
-
else throw new Error(messages.INCORRECT_INPUT_NAMESPACES);
|
|
99
|
-
}
|
|
100
|
-
this.namespaces = namespaces;
|
|
101
|
-
let csn = await edm2csn.edmx2csn(src, !keep_namespace && file, this)
|
|
102
|
-
const service = _service_name_from(csn);
|
|
103
|
-
if (out) out = edm2csn.validateAndComputeOutputFilePath(out, as);
|
|
104
|
-
const result = await _getResult(csn, service, as, out || dst.replace(/\.[^.]+$/, ''), force);
|
|
105
|
-
const written = dry || await _write(result[0], result[1], result[2]);
|
|
106
|
-
const registered = dry || no_save || await _update_package_json(result[1].replace(/\.[^.]+$/, ''), service, this.oDataVersion);
|
|
107
|
-
if (dry) return console.log(result[0]);
|
|
108
|
-
return Promise.all([written, registered])
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function _generateChecksumValidate(dest, output, force) {
|
|
112
|
-
let fileExists = await exists(dest);
|
|
113
|
-
|
|
114
|
-
// force flag disabled and cds file already exists, then throw error
|
|
115
|
-
if (!force && fileExists) {
|
|
116
|
-
throw new Error(messages.FILE_MODIFIED);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const currentChecksum = md5(output);
|
|
120
|
-
let existingFileContent, existingFileContentWithoutChecksum;
|
|
121
|
-
let existingFileContentChecksum, existingChecksumValue;
|
|
122
|
-
|
|
123
|
-
if (fileExists) {
|
|
124
|
-
existingFileContent = await read(dest, "utf-8");
|
|
125
|
-
const checksumStartIndex = existingFileContent.indexOf(`/* checksum : `);
|
|
126
|
-
const checksumEndIndex = existingFileContent.indexOf(" */");
|
|
127
|
-
|
|
128
|
-
if (checksumStartIndex >= 0) {
|
|
129
|
-
existingFileContentWithoutChecksum = existingFileContent.substring(
|
|
130
|
-
checksumEndIndex + 4
|
|
131
|
-
);
|
|
132
|
-
existingChecksumValue = existingFileContent.substring(
|
|
133
|
-
checksumStartIndex + 14, checksumEndIndex
|
|
134
|
-
);
|
|
135
|
-
existingFileContentChecksum = md5(existingFileContentWithoutChecksum);
|
|
136
|
-
}
|
|
70
|
+
const cdsImport = require('../lib/index');
|
|
71
|
+
|
|
72
|
+
async function _import(file, options = {}) {
|
|
73
|
+
let csn = "";
|
|
74
|
+
let apiOptions = {};
|
|
75
|
+
// preprocess
|
|
76
|
+
let cwd = process.cwd();
|
|
77
|
+
if (Array.isArray(file)) file = file[0];
|
|
78
|
+
if (!file) return this.help('import');
|
|
79
|
+
//process options
|
|
80
|
+
await common.processOptions(options, apiOptions);
|
|
81
|
+
const [srcFilePath, destFilePath] = await common.preProcess(file, options, cwd);
|
|
82
|
+
|
|
83
|
+
// process - csn generation
|
|
84
|
+
// if --from option is given, call api directly accordingly
|
|
85
|
+
if (options.from) {
|
|
86
|
+
if (!cdsImport.import.from[options.from]) throw new Error(messages.INVALID_FROM_OPTION);
|
|
87
|
+
csn = await cdsImport.import.from[options.from](srcFilePath, apiOptions);
|
|
88
|
+
} else {
|
|
89
|
+
csn = await cdsImport.import(srcFilePath, apiOptions);
|
|
137
90
|
}
|
|
138
91
|
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
// current and existing file content are same, and the existing
|
|
142
|
-
// checksum is not modified, then return the existing file content
|
|
143
|
-
if (currentChecksum == existingFileContentChecksum &&
|
|
144
|
-
currentChecksum == existingChecksumValue) {
|
|
145
|
-
return existingFileContent;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/*
|
|
149
|
-
* 1. current and existing checksum are different.
|
|
150
|
-
* 2. checksum is missing in the existing file:
|
|
151
|
-
* a. existing and current file content are same.
|
|
152
|
-
* b. existing and current file content are different.
|
|
153
|
-
* 3. cds file doesn't exist.
|
|
154
|
-
* 4. checksum of the existing file is modified.
|
|
155
|
-
*/
|
|
156
|
-
return "/* checksum : " + currentChecksum + " */\n" + output;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function _getResult(output, service, as, dest, force) {
|
|
160
|
-
let using, message, extension;
|
|
161
|
-
as = (as === "cds") ? "cdl" : as;
|
|
162
|
-
|
|
163
|
-
if (!common.supportedFormats().includes(extension = dest.substring(dest.lastIndexOf(".") + 1))) {
|
|
164
|
-
// dest doesn't have any file extension, so get it
|
|
165
|
-
extension = common.getSuffix(as);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
dest = dest.replace('.' + extension, '');
|
|
169
|
-
using = `using { ${service} as external } from './${path.relative('srv', dest).replace(/\\/g, '/')}'`;
|
|
170
|
-
using = require('../lib/util/term').info(using);
|
|
171
|
-
message = `[cds] - imported API to ${path.relative(cwd, dest)}
|
|
172
|
-
> use it in your CDS models through the like of:
|
|
173
|
-
|
|
174
|
-
${using}
|
|
175
|
-
`;
|
|
176
|
-
|
|
177
|
-
dest += '.' + extension;
|
|
178
|
-
|
|
179
|
-
if (as && as != "csn" && as != "json") {
|
|
180
|
-
// cdsCompiler[as] will convert the csn output into various output formats
|
|
181
|
-
output = cdsCompiler[as](output);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
switch(as) {
|
|
185
|
-
case "cdl":
|
|
186
|
-
output = await _generateChecksumValidate(dest, output, force);
|
|
187
|
-
break;
|
|
188
|
-
case undefined:
|
|
189
|
-
case "csn":
|
|
190
|
-
case "json":
|
|
191
|
-
output = JSON.stringify(output, null, ' ');
|
|
192
|
-
break;
|
|
193
|
-
default:
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
92
|
+
//reads the file version
|
|
93
|
+
options.inputFileVersion = apiOptions.inputFileVersion;
|
|
196
94
|
|
|
197
|
-
|
|
95
|
+
// postprocess
|
|
96
|
+
return await common.postProcess(destFilePath, options, csn, cwd);
|
|
198
97
|
}
|
|
199
|
-
|
|
200
|
-
async function _write(output, dest, message) {
|
|
201
|
-
await edm2csn.saveCSNModel(output, dest);
|
|
202
|
-
console.log(message);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async function _copy_to_srv_external(file, dst) {
|
|
206
|
-
const dstDir = path.dirname(dst), srcDir = path.dirname(file)
|
|
207
|
-
if (!file.startsWith(dstDir + path.sep)) {
|
|
208
|
-
await mkdirp(dstDir)
|
|
209
|
-
const copy_or_move = srcDir === cwd || srcDir == process.cwd() ? fs.renameSync : fs.copyFileSync
|
|
210
|
-
await copy_or_move(file, dst)
|
|
211
|
-
return dst
|
|
212
|
-
}
|
|
213
|
-
return file
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function _service_name_from(csn) {
|
|
217
|
-
for (let each in csn.definitions) {
|
|
218
|
-
if (csn.definitions[each].kind === 'service') return each
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async function _update_package_json(dest, service, version) {
|
|
223
|
-
const package_json = path.resolve(cwd, 'package.json')
|
|
224
|
-
const conf = await exists(package_json) ? require(package_json) : {}
|
|
225
|
-
const requires = ['cds', 'requires'].reduce((p, n) => p[n] || (p[n] = {}), conf)
|
|
226
|
-
if (!requires[service]) {
|
|
227
|
-
const model = path.relative(cwd, dest)
|
|
228
|
-
cds.env.requires[service] = requires[service] = { kind: (version === "V2") ? 'odata-v2' : 'odata', model }
|
|
229
|
-
await write(package_json, JSON.stringify(conf, null, ' '))
|
|
230
|
-
console.log(`[cds] - updated ./package.json`)
|
|
231
|
-
}
|
|
232
|
-
}
|
package/bin/repl.js
CHANGED
package/bin/watch.js
CHANGED
|
@@ -106,9 +106,10 @@ async function watch ([cwd], {
|
|
|
106
106
|
log (`watching: ${ext}...`)
|
|
107
107
|
if (liveReload) log (`live reload enabled for browsers`)
|
|
108
108
|
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
109
|
+
const BindingManager = require('../lib/bind/BindingManager')
|
|
110
|
+
const bindingManager = new BindingManager({cwd})
|
|
111
|
+
const bindingEnv = await bindingManager.bindingEnv()
|
|
112
|
+
if (bindingEnv) env = {...env, ...bindingEnv}
|
|
112
113
|
|
|
113
114
|
const nodemon = require('../lib/watch/node-watch')
|
|
114
115
|
const watcher = nodemon ({
|
|
@@ -123,16 +124,14 @@ async function watch ([cwd], {
|
|
|
123
124
|
this.delayed = setTimeout(()=>{
|
|
124
125
|
bindingManager.update({
|
|
125
126
|
onBeforeUpdate: () => watcher.stop(),
|
|
126
|
-
onAfterUpdate:
|
|
127
|
-
|
|
127
|
+
onAfterUpdate: ({env}) => {
|
|
128
|
+
Object.keys(env).forEach( name => watcher.setEnv(name, env[name]) )
|
|
128
129
|
watcher.restart()
|
|
129
130
|
}
|
|
130
131
|
})
|
|
131
|
-
log (
|
|
132
|
+
log (`\n${t.bold} ___________________________\n\n`)
|
|
132
133
|
FileHandlers.changed (files,cwd)
|
|
133
134
|
}, 111)
|
|
134
|
-
}).on('quit', ()=>{
|
|
135
|
-
log (`${t.bold+t.green}\n[cds] - my watch has ended.\n`)
|
|
136
135
|
}).on('message', async (msg) => { switch (msg.code) { // messages from child process
|
|
137
136
|
case 'listening':
|
|
138
137
|
env.PORT = (msg.address && msg.address.port) || msg.port
|
|
@@ -144,6 +143,7 @@ async function watch ([cwd], {
|
|
|
144
143
|
default: console.error (msg)
|
|
145
144
|
}})
|
|
146
145
|
|
|
146
|
+
process.on('exit', ()=> log (`${t.bold+t.green}\n[cds] - my watch has ended. ${t.reset}`))
|
|
147
147
|
return watcher
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -191,15 +191,10 @@ function paths (dir) {
|
|
|
191
191
|
if (!module.parent) (async()=>{ // launched by nodemon
|
|
192
192
|
// run through cli in order to use its error handling
|
|
193
193
|
const bootstrap = require('./cds')(...JSON.parse(process.env._args))
|
|
194
|
-
const {cds} = global
|
|
194
|
+
const {cds} = global; cds.watched = true
|
|
195
195
|
|
|
196
|
-
cds.once('listening', ({server
|
|
196
|
+
cds.once('listening', ({server}) => {
|
|
197
197
|
process.send ({ code:'listening', port: server.address().port })
|
|
198
|
-
// Need to give bindings an explicit call to purge,
|
|
199
|
-
// as its .exit handler has no chance to execute on Windows.
|
|
200
|
-
process.on ('message', (msg)=> { if (msg && msg.exit) { // see node-watch
|
|
201
|
-
(new cds.service.bindings(url)).purge()
|
|
202
|
-
}})
|
|
203
198
|
})
|
|
204
199
|
|
|
205
200
|
try {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const cds = require('../cds')
|
|
2
|
+
const LOG = cds.log('bind');
|
|
3
|
+
const { resolveBindings, getUnresolvedBindings } = require('./bindingResolver')
|
|
4
|
+
|
|
5
|
+
class BindingManager {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.cache = {}
|
|
8
|
+
this.bindingsStr = ''
|
|
9
|
+
this.options = options
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getBindings() {
|
|
13
|
+
const bindings = getUnresolvedBindings(this.options)
|
|
14
|
+
return bindings
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async resolve(bindings) {
|
|
18
|
+
if (!bindings) {
|
|
19
|
+
bindings = this.getBindings(this.options)
|
|
20
|
+
this.bindingsStr = JSON.stringify(bindings)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return await resolveBindings({...this.options, bindings, cache: this.cache})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async bindingEnv(bindings) {
|
|
27
|
+
if (!bindings) bindings = await this.resolve()
|
|
28
|
+
if (bindings) {
|
|
29
|
+
const VCAP_SERVICES = JSON.stringify(this.toVcapServices(bindings))
|
|
30
|
+
return { VCAP_SERVICES }
|
|
31
|
+
} else {
|
|
32
|
+
return undefined
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async update({ onBeforeUpdate, onAfterUpdate } = {}) {
|
|
37
|
+
const oldBindingsStr = this.bindingsStr
|
|
38
|
+
const bindings = this.getBindings()
|
|
39
|
+
const bindingsStr = JSON.stringify(bindings)
|
|
40
|
+
this.bindingsStr = bindingsStr
|
|
41
|
+
|
|
42
|
+
if (this.bindingsStr === oldBindingsStr) return // No change
|
|
43
|
+
if (onBeforeUpdate) await onBeforeUpdate()
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
if (this.pendingUpdate) await this.pendingUpdate
|
|
47
|
+
} catch(error) {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
this.pendingUpdate = undefined
|
|
51
|
+
|
|
52
|
+
if (this.bindingsStr !== bindingsStr) return // Next update in progress
|
|
53
|
+
|
|
54
|
+
let resolvedBindings
|
|
55
|
+
try {
|
|
56
|
+
this.pendingUpdate = this.resolve(bindings)
|
|
57
|
+
resolvedBindings = await this.pendingUpdate
|
|
58
|
+
} catch(error) {
|
|
59
|
+
LOG.error(error.message)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.bindingsStr !== bindingsStr) return // Next update in progress
|
|
64
|
+
|
|
65
|
+
const env = Object.assign({ VCAP_SERVICES: '{}' }, await this.bindingEnv(resolvedBindings));
|
|
66
|
+
if (onAfterUpdate) onAfterUpdate({bindings: resolvedBindings, env})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
toVcapServices(bindings) {
|
|
70
|
+
const services = {}
|
|
71
|
+
|
|
72
|
+
for (const name in bindings) {
|
|
73
|
+
const binding = bindings[name]
|
|
74
|
+
const vcap = binding.binding.vcap || {}
|
|
75
|
+
const type = vcap.type || vcap.label
|
|
76
|
+
const serviceEntries = services[type] = services[type] || []
|
|
77
|
+
|
|
78
|
+
// setting the 'name' property is important to make CAP match the CDS service to the VCAP service
|
|
79
|
+
// it is also added to the 'vcap' property by 'cds bind'
|
|
80
|
+
serviceEntries.push({ ...vcap, name, credentials: binding.credentials})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return services
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = BindingManager
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
const
|
|
2
|
-
const cds = require('@sap/cds/lib');
|
|
1
|
+
const cds = require('../cds')
|
|
3
2
|
const LOG = cds.log('bind');
|
|
4
3
|
const resolveBinding = resolver(LOG);
|
|
5
4
|
const { platformText, bindingText } = require('./bindingUtil');
|
|
6
5
|
|
|
7
6
|
function getUnresolvedBindings({cwd = process.cwd(), env}) {
|
|
8
|
-
if (!env) env =
|
|
7
|
+
if (!env) env = cds.env.for('cds', cwd)
|
|
9
8
|
if (feature(env) === false || feature(env) === "false") return {}
|
|
10
9
|
|
|
10
|
+
const bindings = {}
|
|
11
11
|
const requires = env.requires || {}
|
|
12
|
-
let bindings = {}
|
|
13
12
|
for (const name in requires) {
|
|
14
13
|
const service = requires[name]
|
|
15
14
|
if (service && service.binding && !service.binding.resolved) {
|
|
@@ -21,27 +20,27 @@ function getUnresolvedBindings({cwd = process.cwd(), env}) {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
async function resolveBindings({bindings, cwd = process.cwd(), env, output = 'info', cache = {} }) {
|
|
24
|
-
const config = { requires: { } }
|
|
25
23
|
bindings = bindings || getUnresolvedBindings({cwd, env})
|
|
26
|
-
if (Object.keys(bindings).length===0) return
|
|
24
|
+
if (Object.keys(bindings).length===0) return undefined
|
|
27
25
|
LOG[output](`resolving cloud service bindings...`)
|
|
26
|
+
|
|
27
|
+
const resolvedBindings = {}
|
|
28
|
+
|
|
28
29
|
await Promise.all( Object.keys(bindings).map( async name => {
|
|
29
30
|
const binding = bindings[name]
|
|
30
31
|
const cacheKey = JSON.stringify(binding)
|
|
32
|
+
let resolvedBinding
|
|
31
33
|
if (!cache[cacheKey]) {
|
|
32
|
-
|
|
34
|
+
resolvedBinding = cache[cacheKey] = await resolveBinding(name, binding)
|
|
33
35
|
LOG[output](`bound ${name} to ${platformText(binding)} ${bindingText(binding)}`)
|
|
34
36
|
} else {
|
|
35
|
-
|
|
37
|
+
resolvedBinding = cache[cacheKey]
|
|
36
38
|
}
|
|
37
|
-
} ));
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (bindings[name].kind) config.requires[name].kind = bindings[name].kind
|
|
42
|
-
}
|
|
40
|
+
resolvedBindings[name] = resolvedBinding
|
|
41
|
+
} ))
|
|
43
42
|
|
|
44
|
-
return
|
|
43
|
+
return Object.keys(resolvedBindings).length > 0 ? resolvedBindings : undefined
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
let cfKeyProvider
|
|
@@ -68,83 +67,12 @@ function resolver(log) {
|
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
async function applyTo(options) {
|
|
72
|
-
const config = await resolveBindings(options)
|
|
73
|
-
if (!config) return
|
|
74
|
-
const {env} = options
|
|
75
|
-
for (const name in config.requires) {
|
|
76
|
-
const service = config.requires[name]
|
|
77
|
-
const { binding, credentials } = service
|
|
78
|
-
Object.assign(env.requires[name], { binding, credentials })
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (env._emulate_vcap_services) {
|
|
82
|
-
env._emulate_vcap_services()
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
class BindingManager {
|
|
87
|
-
constructor(options) {
|
|
88
|
-
this.cache = {}
|
|
89
|
-
this.bindingsStr = ''
|
|
90
|
-
this.options = options
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
getBindings() {
|
|
94
|
-
const bindings = getUnresolvedBindings(this.options)
|
|
95
|
-
return bindings
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async resolve(bindings) {
|
|
99
|
-
if (!bindings) {
|
|
100
|
-
bindings = this.getBindings(this.options)
|
|
101
|
-
this.bindingsStr = JSON.stringify(bindings)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const config = await resolveBindings({...this.options, bindings, cache: this.cache})
|
|
105
|
-
if (config && Object.keys(config.requires || {}).length > 0) return config;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async update({ onBeforeUpdate, onAfterUpdate } = {}) {
|
|
109
|
-
const oldBindingsStr = this.bindingsStr
|
|
110
|
-
const bindings = this.getBindings()
|
|
111
|
-
const bindingsStr = JSON.stringify(bindings)
|
|
112
|
-
this.bindingsStr = bindingsStr
|
|
113
|
-
|
|
114
|
-
if (this.bindingsStr === oldBindingsStr) return // No change
|
|
115
|
-
if (onBeforeUpdate) await onBeforeUpdate()
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
if (this.pendingUpdate) await this.pendingUpdate
|
|
119
|
-
} catch(error) {
|
|
120
|
-
// ignore
|
|
121
|
-
}
|
|
122
|
-
this.pendingUpdate = undefined
|
|
123
|
-
|
|
124
|
-
if (this.bindingsStr !== bindingsStr) return // Next update in progress
|
|
125
|
-
|
|
126
|
-
let resolvedBindings
|
|
127
|
-
try {
|
|
128
|
-
this.pendingUpdate = this.resolve(bindings)
|
|
129
|
-
resolvedBindings = await this.pendingUpdate
|
|
130
|
-
} catch(error) {
|
|
131
|
-
LOG.error(error.message)
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (this.bindingsStr !== bindingsStr) return // Next update in progress
|
|
136
|
-
if (onAfterUpdate) onAfterUpdate(resolvedBindings)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
70
|
function feature(env) {
|
|
141
71
|
return env.features && env.features.dk && env.features.dk.resolveBindingsForTesting
|
|
142
72
|
}
|
|
143
73
|
|
|
144
74
|
module.exports = {
|
|
145
75
|
resolver,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return new BindingManager(options)
|
|
149
|
-
}
|
|
76
|
+
resolveBindings,
|
|
77
|
+
getUnresolvedBindings
|
|
150
78
|
}
|
package/lib/bind/bindingUtil.js
CHANGED
|
@@ -38,29 +38,36 @@ function determineKinds(services) {
|
|
|
38
38
|
for (const service of services) {
|
|
39
39
|
service.kindCandidates = [ ]
|
|
40
40
|
|
|
41
|
-
const {
|
|
42
|
-
|
|
41
|
+
const { vcap = {} } = service.binding || {};
|
|
42
|
+
let { label, type } = vcap;
|
|
43
|
+
type = type || label;
|
|
44
|
+
if (!type) continue;
|
|
43
45
|
|
|
44
|
-
if (!vcapServices[
|
|
45
|
-
vcapServices[
|
|
46
|
+
if (!vcapServices[type]) vcapServices[type] = [];
|
|
47
|
+
vcapServices[type].push({ credentials: { dummy: 'dummy' }, ...vcap, service });
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
// Auto configure services based on VCAP_SERVICES
|
|
49
51
|
|
|
50
|
-
const cds = require('
|
|
52
|
+
const cds = require('../cds');
|
|
51
53
|
const env = cds.env.for('cds', process.cwd());
|
|
52
54
|
|
|
53
55
|
if (!env._find_credentials_for_required_service) {
|
|
54
56
|
throwError `Please provide a service kind or update @sap/cds version to use the kind detection.`;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
function findKindFor(kind, service) {
|
|
60
|
+
const vcapService = env._find_credentials_for_required_service(kind, service, vcapServices);
|
|
61
|
+
if (!vcapService) return;
|
|
62
|
+
if (!Object.values(requires).find( require => require !== service && require.kind == kind ))
|
|
63
|
+
vcapService.service.kindCandidates.push(kind);
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
const requires = env.requires.kinds || {};
|
|
58
67
|
for (const kind of Object.keys(requires)) {
|
|
59
68
|
const requireService = requires[kind];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (!Object.values(requires).find( require => require !== requireService && require.kind == kind ))
|
|
63
|
-
vcapService.service.kindCandidates.push(kind);
|
|
69
|
+
findKindFor(kind, requireService);
|
|
70
|
+
findKindFor(kind, { kind });
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
// Check if one "kind" per service was found
|
|
@@ -76,10 +83,10 @@ function determineKinds(services) {
|
|
|
76
83
|
}
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
function execAndExit(command, ...args) {
|
|
86
|
+
function execAndExit(options, command, ...args) {
|
|
80
87
|
const { spawnSync } = require('child_process');
|
|
81
88
|
// use shell with Windows only; without output is not visible and some commands cannot be run
|
|
82
|
-
const result = spawnSync(command, args, { stdio: 'inherit', shell: IS_WIN });
|
|
89
|
+
const result = spawnSync(command, args, { stdio: 'inherit', shell: IS_WIN, ...options });
|
|
83
90
|
process.exit(result.status);
|
|
84
91
|
}
|
|
85
92
|
|