@sap/cds-dk 4.9.5 → 6.0.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.
Files changed (144) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/bin/activate.js +4 -4
  3. package/bin/add.js +11 -5
  4. package/bin/bind.js +16 -28
  5. package/bin/build/all.js +2 -2
  6. package/bin/build/index.js +12 -19
  7. package/bin/cds.js +16 -17
  8. package/bin/compile/index.js +12 -3
  9. package/bin/deploy/index.js +14 -14
  10. package/bin/deploy/to-hana.js +4 -4
  11. package/bin/deploy/to-sqlite.js +1 -1
  12. package/bin/env.js +13 -4
  13. package/bin/eval.js +1 -1
  14. package/bin/fix-redirects.js +1 -1
  15. package/bin/import.js +44 -179
  16. package/bin/repl.js +1 -1
  17. package/bin/watch.js +10 -15
  18. package/lib/bind/BindingManager.js +87 -0
  19. package/lib/bind/bindingResolver.js +14 -85
  20. package/lib/bind/bindingUtil.js +18 -11
  21. package/lib/bind/cfKeyProvider.js +58 -29
  22. package/lib/bind/kubernetesCredentialsProvider.js +44 -21
  23. package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
  24. package/lib/cds.js +13 -0
  25. package/lib/client/base_command.js +3 -2
  26. package/lib/client/extend.js +6 -7
  27. package/lib/client/helper/fs_helper.js +7 -8
  28. package/lib/client/helper/string_helper.js +6 -1
  29. package/lib/client/settings_manager.js +38 -45
  30. package/lib/compile/index.js +9 -10
  31. package/lib/compile/openapi/csdl2openapi.js +151 -43
  32. package/lib/compile/openapi/index.js +2 -2
  33. package/lib/compile/to-xsuaa.js +3 -2
  34. package/lib/import/common.js +342 -27
  35. package/lib/import/importapi.js +81 -13
  36. package/lib/import/message.js +4 -4
  37. package/lib/import/odata/index.js +141 -0
  38. package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
  39. package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
  40. package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
  41. package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
  42. package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
  43. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
  44. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
  45. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
  46. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
  47. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
  48. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
  49. package/lib/import/openapi/importOpenAPI.js +521 -0
  50. package/lib/import/openapi/index.js +19 -0
  51. package/lib/import/openapi/utilities.js +38 -0
  52. package/lib/index.js +1 -1
  53. package/lib/init/constants.js +7 -2
  54. package/lib/init/index.js +10 -3
  55. package/lib/init/template/_merging/existences.js +79 -19
  56. package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
  57. package/lib/init/template/approuter/files/package.json +1 -1
  58. package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
  59. package/lib/init/template/approuter/index.js +61 -54
  60. package/lib/init/template/auditlog/index.js +2 -2
  61. package/lib/init/template/data/index.js +2 -2
  62. package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
  63. package/lib/init/template/hana/index.js +8 -4
  64. package/lib/init/template/helm/index.js +39 -0
  65. package/lib/init/template/helm/update/index.js +28 -0
  66. package/lib/init/template/java/index.js +7 -9
  67. package/lib/init/template/kibana-logging/index.js +5 -6
  68. package/lib/init/template/mta/index.js +4 -4
  69. package/lib/init/template/mtx/files/java/package.json +9 -9
  70. package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
  71. package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
  72. package/lib/init/template/mtx/index.js +50 -57
  73. package/lib/init/template/nodejs/files/package.json.hbs +2 -2
  74. package/lib/init/template/notebook/index.js +2 -0
  75. package/lib/init/template/templateBase.js +6 -4
  76. package/lib/init/template/xsuaa/index.js +9 -5
  77. package/lib/init/util/fsUtil.js +25 -10
  78. package/lib/init/util/logger.js +1 -1
  79. package/lib/init/util/mvnArchetypeUtil.js +4 -10
  80. package/lib/init/util/projectReader.js +16 -8
  81. package/lib/init/util/templateUtil.js +30 -34
  82. package/lib/k8s/ChartAdder.js +65 -0
  83. package/lib/k8s/ChartBuilder.js +186 -0
  84. package/lib/k8s/Context.js +71 -0
  85. package/lib/k8s/ImageUtil.js +21 -0
  86. package/lib/k8s/Model.d.ts +53 -0
  87. package/lib/k8s/auditlog/index.js +29 -0
  88. package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
  89. package/lib/k8s/buildModel.js +70 -0
  90. package/lib/k8s/connectivity/index.js +74 -0
  91. package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
  92. package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +16 -0
  93. package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
  94. package/lib/k8s/deployment/index.js +81 -0
  95. package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
  96. package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
  97. package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
  98. package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
  99. package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
  100. package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
  101. package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
  102. package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
  103. package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
  104. package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
  105. package/lib/k8s/deployment/subchart/values.schema.json +449 -0
  106. package/lib/k8s/deployment/subchart/values.yaml +76 -0
  107. package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
  108. package/lib/k8s/destinations/index.js +40 -0
  109. package/lib/k8s/destinations/lib.js +16 -0
  110. package/lib/k8s/destinations/templates/destinations.yaml +1 -0
  111. package/lib/k8s/docker/push.js +8 -0
  112. package/lib/k8s/event-mesh/index.js +53 -0
  113. package/lib/k8s/event-mesh/lib.js +9 -0
  114. package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
  115. package/lib/k8s/hana-deployer/index.js +38 -0
  116. package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
  117. package/lib/k8s/helm/Release.js +80 -0
  118. package/lib/k8s/html5-apps-deployer/index.js +43 -0
  119. package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
  120. package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
  121. package/lib/k8s/html5-apps-repo-host/index.js +26 -0
  122. package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
  123. package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
  124. package/lib/k8s/index.js +153 -0
  125. package/lib/k8s/modules.js +36 -0
  126. package/lib/k8s/multitenancy/index.js +24 -0
  127. package/lib/k8s/multitenancy/lib.js +25 -0
  128. package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
  129. package/lib/k8s/readProject.js +14 -0
  130. package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
  131. package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
  132. package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
  133. package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
  134. package/lib/k8s/util.js +186 -0
  135. package/lib/k8s/wrapFeature.js +55 -0
  136. package/lib/k8s/xsuaa/index.js +64 -0
  137. package/lib/k8s/xsuaa/lib.js +14 -0
  138. package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
  139. package/lib/util/exec.js +1 -1
  140. package/lib/util/write.js +1 -1
  141. package/lib/watch/node-watch.js +43 -14
  142. package/npm-shrinkwrap.json +1272 -2792
  143. package/package.json +5 -5
  144. 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', '--into', '--as', '--include-namespaces'],
4
+ options: ['--out', '--as', '--include-namespaces', '--from'],
5
5
  flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace'],
6
- shortcuts: ['-o', '-in', '-as', '-ns', '-', '-f'],
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 only OData EDMX input is supported.
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 edmx or xml file into one of these supported formats "cds", "csn", "json"
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 cdsCompiler = require('../lib/compile').compile.to;
68
- const md5 = require('md5');
69
- let cwd;
70
- async function _import(file, { out, dry, into, as,
71
- 'include-namespaces': include_namespaces,
72
- 'no-save': no_save,
73
- 'no-copy': no_copy,
74
- 'keep-namespace': keep_namespace,
75
- force
76
- } = {}) {
77
- if (!as) {
78
- if (cds.env.import) as = cds.env.import.as;
79
- }
80
- if (!include_namespaces) {
81
- if(cds.env.import) include_namespaces = cds.env.import.include_namespaces;
82
- }
83
- cwd = into || process.cwd();
84
- if (as && !common.supportedFormats().includes(as))
85
- throw new Error(messages.INVALID_AS_OPTION);
86
- if (Array.isArray(file)) file = file[0]
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
- // if force flag enbabled
140
- if (force) {
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
- return [output, dest, message];
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
@@ -22,7 +22,7 @@ function repl (_, options={}) {
22
22
  )
23
23
  }
24
24
 
25
- const cds = require('@sap/cds/lib')
25
+ const cds = require('../lib/cds')
26
26
  const path = require('path')
27
27
  const {inspect} = require('util')
28
28
 
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 bindingManager = new require('../lib/bind/bindingResolver').bindingManager({cwd})
110
- const bindings = await bindingManager.resolve()
111
- if (bindings) env = {...env, 'CDS_REQUIRES': JSON.stringify(bindings.requires)}
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: bindings => {
127
- watcher.setEnv('CDS_REQUIRES', bindings && JSON.stringify(bindings.requires))
127
+ onAfterUpdate: ({env}) => {
128
+ Object.keys(env).forEach( name => watcher.setEnv(name, env[name]) )
128
129
  watcher.restart()
129
130
  }
130
131
  })
131
- log (`${t.bold} _______________________\n`)
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, url}) => {
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,5 +1,5 @@
1
1
  const Config = require('@sap/cds/lib/env')
2
- const cds = require('@sap/cds/lib');
2
+ const cds = require('../cds')
3
3
  const LOG = cds.log('bind');
4
4
  const resolveBinding = resolver(LOG);
5
5
  const { platformText, bindingText } = require('./bindingUtil');
@@ -8,8 +8,8 @@ function getUnresolvedBindings({cwd = process.cwd(), env}) {
8
8
  if (!env) env = Config.for('cds', cwd)
9
9
  if (feature(env) === false || feature(env) === "false") return {}
10
10
 
11
+ const bindings = {}
11
12
  const requires = env.requires || {}
12
- let bindings = {}
13
13
  for (const name in requires) {
14
14
  const service = requires[name]
15
15
  if (service && service.binding && !service.binding.resolved) {
@@ -21,27 +21,27 @@ function getUnresolvedBindings({cwd = process.cwd(), env}) {
21
21
  }
22
22
 
23
23
  async function resolveBindings({bindings, cwd = process.cwd(), env, output = 'info', cache = {} }) {
24
- const config = { requires: { } }
25
24
  bindings = bindings || getUnresolvedBindings({cwd, env})
26
- if (Object.keys(bindings).length===0) return config;
25
+ if (Object.keys(bindings).length===0) return undefined
27
26
  LOG[output](`resolving cloud service bindings...`)
27
+
28
+ const resolvedBindings = {}
29
+
28
30
  await Promise.all( Object.keys(bindings).map( async name => {
29
31
  const binding = bindings[name]
30
32
  const cacheKey = JSON.stringify(binding)
33
+ let resolvedBinding
31
34
  if (!cache[cacheKey]) {
32
- cache[cacheKey] = config.requires[name] = await resolveBinding(name, binding)
35
+ resolvedBinding = cache[cacheKey] = await resolveBinding(name, binding)
33
36
  LOG[output](`bound ${name} to ${platformText(binding)} ${bindingText(binding)}`)
34
37
  } else {
35
- config.requires[name] = cache[cacheKey]
38
+ resolvedBinding = cache[cacheKey]
36
39
  }
37
- } ));
38
40
 
39
- // Add kind to result, so that it can be applied to old @sap/cds versions that do not read `.cdsrc-private.json`
40
- for (const name in config.requires) {
41
- if (bindings[name].kind) config.requires[name].kind = bindings[name].kind
42
- }
41
+ resolvedBindings[name] = resolvedBinding
42
+ } ))
43
43
 
44
- return config
44
+ return Object.keys(resolvedBindings).length > 0 ? resolvedBindings : undefined
45
45
  }
46
46
 
47
47
  let cfKeyProvider
@@ -68,83 +68,12 @@ function resolver(log) {
68
68
  }
69
69
  }
70
70
 
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
71
  function feature(env) {
141
72
  return env.features && env.features.dk && env.features.dk.resolveBindingsForTesting
142
73
  }
143
74
 
144
75
  module.exports = {
145
76
  resolver,
146
- applyTo,
147
- bindingManager: function(options) {
148
- return new BindingManager(options)
149
- }
77
+ resolveBindings,
78
+ getUnresolvedBindings
150
79
  }
@@ -38,29 +38,36 @@ function determineKinds(services) {
38
38
  for (const service of services) {
39
39
  service.kindCandidates = [ ]
40
40
 
41
- const { label, name, tags } = service.binding && service.binding.vcap || {};
42
- if (!label) continue;
41
+ const { vcap = {} } = service.binding || {};
42
+ let { label, type } = vcap;
43
+ type = type || label;
44
+ if (!type) continue;
43
45
 
44
- if (!vcapServices[label]) vcapServices[label] = [];
45
- vcapServices[label].push({ credentials: { dummy: 'dummy' }, label, name, tags, service });
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('@sap/cds/lib');
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
- const vcapService = env._find_credentials_for_required_service(kind, requireService, vcapServices);
61
- if (!vcapService) continue;
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