@sap/cds-dk 4.8.1 → 4.9.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 (83) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/bin/activate.js +8 -7
  3. package/bin/bind.js +12 -12
  4. package/bin/build/all.js +3 -5
  5. package/bin/build/index.js +1 -1
  6. package/bin/cds.js +1 -1
  7. package/bin/deploy/index.js +7 -1
  8. package/bin/deploy/to-hana.js +5 -4
  9. package/bin/deploy/to-sqlite.js +2 -1
  10. package/bin/extend.js +5 -5
  11. package/bin/help.js +1 -0
  12. package/bin/import.js +45 -31
  13. package/bin/login.js +5 -6
  14. package/bin/logout.js +2 -2
  15. package/bin/parse.js +19 -0
  16. package/lib/bind/bindingUtil.js +41 -49
  17. package/lib/bind/cfKeyProvider.js +9 -17
  18. package/lib/bind/userCdsrcJsonStorageProvider.js +1 -0
  19. package/lib/client/activate.js +3 -2
  20. package/lib/client/extend.js +5 -6
  21. package/lib/client/helper/fs_helper.js +8 -8
  22. package/lib/client/settings_manager.js +12 -9
  23. package/lib/compile/openapi/csdl2openapi.js +213 -165
  24. package/lib/compile/openapi/index.js +1 -2
  25. package/lib/compile/to-xsuaa.js +1 -1
  26. package/lib/import/index.js +22 -15
  27. package/lib/import/message.js +3 -1
  28. package/lib/import/utils/MetadataConverter.js +1 -1
  29. package/lib/import/v2parser.js +112 -56
  30. package/lib/import/v4parser.js +152 -99
  31. package/lib/init/constants.js +6 -2
  32. package/lib/init/index.js +3 -2
  33. package/lib/init/template/_merging/existences.js +80 -4
  34. package/lib/init/template/approuter/files/cds.cdsrc.json +7 -0
  35. package/lib/init/template/approuter/files/cds.cdsrc.json.hbs +9 -0
  36. package/lib/init/template/approuter/files/cds.package.json +9 -0
  37. package/lib/init/template/approuter/files/cds.package.json.hbs +11 -0
  38. package/lib/init/template/approuter/files/index.html +1 -0
  39. package/lib/init/template/approuter/files/mta.yaml.hbs +35 -21
  40. package/lib/init/template/approuter/files/package.json +3 -0
  41. package/lib/init/template/approuter/files/xs-app.json.hbs +19 -0
  42. package/lib/init/template/approuter/index.js +47 -18
  43. package/lib/init/template/auditlog/index.js +5 -5
  44. package/lib/init/template/hana/files/java/cdsrc.json.hbs +7 -0
  45. package/lib/init/template/hana/files/java/{package.json → db/package.json} +0 -0
  46. package/lib/init/template/hana/files/java/profile.cdsrc.json.hbs +12 -0
  47. package/lib/init/template/hana/files/mta.yaml.hbs +10 -17
  48. package/lib/init/template/hana/files/nodejs/cds.package.json.hbs +9 -0
  49. package/lib/init/template/hana/files/nodejs/cds.profile.package.json.hbs +14 -0
  50. package/lib/init/template/hana/files/nodejs/dependencies.package.json +1 -1
  51. package/lib/init/template/hana/index.js +60 -86
  52. package/lib/init/template/java/index.js +5 -2
  53. package/lib/init/template/kibana-logging/files/cds.package.json +7 -0
  54. package/lib/init/template/kibana-logging/files/cds.profile.package.json.hbs +9 -0
  55. package/lib/init/template/kibana-logging/files/mta.yaml.hbs +13 -0
  56. package/lib/init/template/kibana-logging/index.js +64 -0
  57. package/lib/init/template/mta/files/mta.yaml.hbs +3 -8
  58. package/lib/init/template/mta/index.js +8 -2
  59. package/lib/init/template/mtx/files/cds.package.json +0 -12
  60. package/lib/init/template/mtx/files/cds.package.json.hbs +14 -0
  61. package/lib/init/template/mtx/files/java/.cdsrc.json +17 -0
  62. package/lib/init/template/mtx/files/java/package.json +43 -0
  63. package/lib/init/template/mtx/files/java/server.js +30 -0
  64. package/lib/init/template/mtx/files/mta.yaml.hbs +6 -6
  65. package/lib/init/template/mtx/index.js +41 -22
  66. package/lib/init/template/templateBase.js +4 -1
  67. package/lib/init/template/xsuaa/files/cds.cdsrc.json +7 -0
  68. package/lib/init/template/xsuaa/files/cds.cdsrc.json.hbs +9 -0
  69. package/lib/init/template/xsuaa/files/cds.package.json +1 -1
  70. package/lib/init/template/xsuaa/files/cds.package.json.hbs +11 -0
  71. package/lib/init/template/xsuaa/files/mta.yaml.hbs +2 -2
  72. package/lib/init/template/xsuaa/index.js +43 -27
  73. package/lib/init/types.d.ts +43 -19
  74. package/lib/init/util/fsUtil.js +15 -4
  75. package/lib/init/util/projectReader.js +21 -18
  76. package/lib/init/util/templateUtil.js +78 -28
  77. package/lib/util/exec.js +2 -2
  78. package/lib/watch/livereload-server.js +3 -0
  79. package/lib/watch/node-watch.js +8 -1
  80. package/npm-shrinkwrap.json +604 -286
  81. package/package.json +3 -3
  82. package/lib/init/template/approuter/files/xs-app.json +0 -8
  83. package/lib/init/template/nodejs/files/.vscode/settings.json +0 -7
@@ -1,3 +1,4 @@
1
+ const fs = require('fs').promises;
1
2
  const path = require('path');
2
3
 
3
4
  const {fse, yaml} = require('@sap/cds-foss')
@@ -5,6 +6,10 @@ const JSONC = require('../../util/jsonc');
5
6
 
6
7
  const UTF_8 = 'utf-8';
7
8
 
9
+ /**
10
+ * this util implements FsUtil declared in ../types.d.ts
11
+ * the interface FsUtil is also used by @sap/generator-cap-project
12
+ */
8
13
  module.exports = class FsUtil {
9
14
 
10
15
  constructor() {
@@ -30,10 +35,16 @@ module.exports = class FsUtil {
30
35
  }
31
36
 
32
37
  async readFile(src) {
33
- return await fse.readFile(src, UTF_8);
38
+ return await fs.readFile(src, UTF_8);
34
39
  }
35
40
 
36
- async readJSON(src) {
41
+ async readJSON(src, projectDescriptor) {
42
+ const file = await this.readFile(src);
43
+ if (projectDescriptor) {
44
+ const Mustache = require('mustache');
45
+ const renderedFile = Mustache.render(file, projectDescriptor);
46
+ return JSON.parse(renderedFile);
47
+ }
37
48
  return await fse.readJSON(src);
38
49
  }
39
50
 
@@ -64,7 +75,7 @@ module.exports = class FsUtil {
64
75
  }
65
76
 
66
77
  async readdir(filePath) {
67
- return await fse.readdir(filePath);
78
+ return await fs.readdir(filePath);
68
79
  }
69
80
 
70
81
  getTouchedFiles() {
@@ -76,7 +87,7 @@ module.exports = class FsUtil {
76
87
  }
77
88
 
78
89
  async stat(filePath) {
79
- return await fse.stat(filePath);
90
+ return await fs.stat(filePath);
80
91
  }
81
92
 
82
93
  async hasContent(folderPath) {
@@ -1,6 +1,7 @@
1
1
  const path = require('path')
2
2
  const { nullLogger } = require('../util/logger')
3
3
  const { BuildTaskFactory } = require('@sap/cds/bin/build')
4
+ const { fs } = require('@sap/cds/lib/utils')
4
5
  const P_LANGUAGE_JAVA = 'java'
5
6
  const P_LANGUAGE_NODEJS = 'nodejs'
6
7
  const DEFAULT_P_LANGUAGE = P_LANGUAGE_NODEJS
@@ -60,12 +61,17 @@ module.exports = class ProjectReader {
60
61
 
61
62
  async _getCapDescriptor(env, options) {
62
63
  let capDescriptor = {
64
+ for: options && options.for,
63
65
  pLanguage: DEFAULT_P_LANGUAGE,
64
66
  srv: {},
65
67
  db: [],
66
68
  requires: [],
67
69
  get isMultitenant() {
68
- return options && options.add.has('mtx') || env.requires.multitenancy || env.requires.db && env.requires.db.multiTenant || false
70
+ return options && options.add && options.add.has('mtx') ||
71
+ env.requires.multitenancy ||
72
+ env.requires.db && env.requires.db.multiTenant ||
73
+ this.isJava && fs.existsSync('mtx-sidecar') ||
74
+ false
69
75
  },
70
76
  get isNodejs() {
71
77
  return this.pLanguage === P_LANGUAGE_NODEJS
@@ -87,6 +93,9 @@ module.exports = class ProjectReader {
87
93
  },
88
94
  get needsSidecar() {
89
95
  return (options && options.add.has('hana') || this.db.length > 0) && (this.isJava || !this.isMultitenant)
96
+ },
97
+ get hasApprouter() {
98
+ return !!env.requires.approuter
90
99
  }
91
100
  }
92
101
 
@@ -179,14 +188,6 @@ module.exports = class ProjectReader {
179
188
  uiDescriptor.appPath = env.folders.app
180
189
  }
181
190
 
182
- const packageJson = await (async () => { try {
183
- return await this.fsUtil.readFile(absoluteAppPath+'package.json')
184
- } catch(error) { /* ignore */ }
185
- })()
186
-
187
- if (packageJson && packageJson.dependencies) {
188
- uiDescriptor.appRouterEnabled = packageJson.dependencies['@sap/approuter'] !== undefined
189
- }
190
191
  return uiDescriptor
191
192
  }
192
193
 
@@ -316,25 +317,27 @@ module.exports = class ProjectReader {
316
317
  const hanaResources = new Map()
317
318
  const otherResources = new Map()
318
319
 
319
- for (let key of Object.keys(env.requires)) {
320
+ for (const key of Object.keys(env.requires)) {
320
321
  // ignore abstract helper definitions like sql
321
322
  if (key === 'sql' || key === 'sqlite' || key === 'hana') continue
322
- if (env.requires[key].kind === 'hana') {
323
+ const { kind } = env.requires[key]
324
+ if (kind === 'hana' || kind === 'hana-mt' || kind === 'hana-cloud') {
323
325
  hanaResources.set(key, env.requires[key])
324
326
  } else {
325
327
  otherResources.set(key, env.requires[key])
326
328
  }
327
329
  }
328
- for (let [key, value] of hanaResources) {
329
- let resource = this._getReqResource(key, value, capDescriptor)
330
+
331
+ for (const [key, value] of hanaResources) {
332
+ const resource = this._getReqResource(key, value, capDescriptor)
330
333
  if (resource) {
331
334
  // using production profile
332
335
  capDescriptor.requires.push({ resource })
333
336
  }
334
337
  }
335
338
  // add after hana resource dependencies have been handled as multitenant information is required
336
- for (let [key, value] of otherResources) {
337
- let resource = this._getReqResource(key, value, capDescriptor)
339
+ for (const [key, value] of otherResources) {
340
+ const resource = this._getReqResource(key, value, capDescriptor)
338
341
  if (resource) {
339
342
  // using production profile
340
343
  capDescriptor.requires.push({ resource })
@@ -391,9 +394,9 @@ module.exports = class ProjectReader {
391
394
  break
392
395
 
393
396
  case 'xsuaa':
394
- if (capDescriptor.isMultitenant) {
395
- resource.name += '-mt'
396
- }
397
+ // if (capDescriptor.isMultitenant) {
398
+ // resource.name += '-mt'
399
+ // }
397
400
  resource.service = reqEntry.kind
398
401
  resource.vcap.plan = 'application'
399
402
  break
@@ -32,6 +32,14 @@ module.exports = class TemplateUtil {
32
32
  }
33
33
  }
34
34
 
35
+ async sortDependencies(packageJSONPath) {
36
+ const json = await this.fsUtil.readJSON(packageJSONPath);
37
+ json.dependencies = Object.fromEntries(
38
+ Object.entries(json.dependencies).sort(([a],[b]) => a>b ? 1 : -1)
39
+ )
40
+ await this.fsUtil.writeJSON(packageJSONPath, json);
41
+ }
42
+
35
43
  async _writeTemplateFile(src, destination, templateValues, overwrite = false) {
36
44
  const destinationPath = destination.replace(TEMPLATE_EXTENSION_REGEX, '');
37
45
  if (overwrite || !(await this.fsUtil.pathExists(destinationPath))) {
@@ -80,8 +88,8 @@ module.exports = class TemplateUtil {
80
88
  return target || source;
81
89
  }
82
90
 
83
- async mergeJSON(targetPath, sourcePath, projectDescriptor, semantics) {
84
- const source = await this.fsUtil.readJSON(sourcePath, projectDescriptor)
91
+ async mergeJSON(targetPath, from, projectDescriptor, semantics) {
92
+ const source = typeof from === 'string' ? await this.fsUtil.readJSON(from, projectDescriptor) : from
85
93
 
86
94
  if (! await this.fsUtil.pathExists(targetPath)) {
87
95
  await this.fsUtil.writeJSON(targetPath, source);
@@ -97,7 +105,7 @@ module.exports = class TemplateUtil {
97
105
  await this.fsUtil.writeJSON(targetPath, result)
98
106
  } else {
99
107
  const target = await this.fsUtil.readJSON(targetPath);
100
- const sourceFile = await this._processTemplateFile(sourcePath, projectDescriptor);
108
+ const sourceFile = await this._processTemplateFile(from, projectDescriptor);
101
109
  const source = JSON.parse(sourceFile);
102
110
  const result = target && source ? this._deepMerge(target, source) : target || source;
103
111
  await this.fsUtil.writeJSON(targetPath, result);
@@ -120,11 +128,16 @@ module.exports = class TemplateUtil {
120
128
  templateExistenceMap.set(existence.ref, undefined)
121
129
  });
122
130
 
131
+ const { deletions } = semantics
132
+ deletions && deletions.forEach(deletion => {
133
+ existenceMap.set(deletion.existence.ref, undefined)
134
+ })
135
+
123
136
  const missingRelationshipsMap = new Map()
124
137
 
125
138
  const relationships = semantics.relationships && semantics.relationships.filter(r => r)
126
139
  relationships && relationships.forEach(relationship =>
127
- missingRelationshipsMap.set(relationship.in, undefined)
140
+ missingRelationshipsMap.set(relationship.in.ref, undefined)
128
141
  )
129
142
 
130
143
  let collectionStack = [target.contents]
@@ -133,22 +146,35 @@ module.exports = class TemplateUtil {
133
146
  const _getYAMLProperty = (object, keyPath) => keyPath.reduce((p, n) => p && p.get(n), object)
134
147
 
135
148
  const _validateConstraints = (node, dict, index) => {
136
- existences.forEach(existence => {
137
- const json = JSON.parse(String(node))
138
- const constraintsFulfilled = existence.constraints.every(constraint =>
139
- constraint.value === _getProperty(json, constraint.comparisonKeyPath)
140
- )
141
- if (constraintsFulfilled) dict.set(existence.ref, { json, node, index })
149
+ const _addToMap = existences => existences
150
+ .filter(existence => {
151
+ if (!existence.in) return true
152
+ const neededParent = existence.in && dict.get(existence.in.ref) && dict.get(existence.in.ref).node
153
+ return collectionStack.includes(neededParent)
154
+ })
155
+ .forEach(existence => {
156
+ const json = JSON.parse(String(node));
157
+ const constraintsFulfilled = existence.constraints.every(constraint =>
158
+ constraint.value === _getProperty(json, constraint.comparisonKeyPath)
159
+ )
160
+ if (constraintsFulfilled) {
161
+ const [collection] = collectionStack
162
+ dict.set(existence.ref, { json, node, index, collection })
163
+ }
142
164
  })
165
+ _addToMap(existences)
166
+ deletions && _addToMap(deletions.map(deletion => deletion.existence))
143
167
  }
144
168
 
145
169
  function _traverseYAMLNode(node, index, actions, templateNode) {
146
170
  let shifted = false
147
171
  switch (node.type) {
148
- case 'MAP':
172
+ case 'MAP': {
149
173
  actions && actions.visitMap && actions.visitMap(node, index)
150
- if (collectionStack[0] && collectionStack[0].type === 'SEQ') { collectionStack.unshift(node); shifted = true}
174
+ const [collection] = collectionStack
175
+ if (collection && collection.type === 'SEQ') { collectionStack.unshift(node); shifted = true}
151
176
  break;
177
+ }
152
178
  case 'SEQ':
153
179
  break;
154
180
  case 'PAIR':
@@ -235,15 +261,35 @@ module.exports = class TemplateUtil {
235
261
  })
236
262
 
237
263
  // 3. Apply overwrites to already found existences
238
- semantics.overwrites && semantics.overwrites.forEach(({ in: ref, keyPath, replacement }) => {
239
- const existence = existenceMap.get(ref)
264
+ semantics.overwrites && semantics.overwrites.forEach(({ in: inExistence, keyPath, replacement }) => {
265
+ const existence = existenceMap.get(inExistence.ref)
240
266
  if (existence) {
241
267
  _getYAMLProperty(existence.node, keyPath.slice(0, keyPath.length - 1))
242
268
  .set(keyPath[keyPath.length - 1], replacement)
243
269
  }
244
270
  })
245
271
 
246
- // 4. Create missing existences and pairs
272
+ // 4. Delete existences from the project (e.g. separate deployer module when adding mtx)
273
+ semantics.deletions && semantics.deletions.forEach(({ existence: { ref }, relationships }) => {
274
+ const existence = existenceMap.get(ref)
275
+ if (!existence) return
276
+ existence.collection.delete(existence.index)
277
+
278
+ relationships && relationships.forEach(relationship => {
279
+ const parent = target.get(relationship.allWithinKeyPath[0])
280
+ for (const child of parent.items) {
281
+ const c = child.get(relationship.inKeyPath[0])
282
+ const i = c.items.findIndex(node =>
283
+ node.get(relationship.into) === _getProperty(existence.json, relationship.existenceKeyPath)
284
+ )
285
+ if (i > -1) {
286
+ c.delete(i)
287
+ }
288
+ }
289
+ })
290
+ })
291
+
292
+ // 5. Create missing existences and pairs
247
293
  _traverseYAMLNode(target.contents, null, {
248
294
  mergePair: (collection, templateNode) => {
249
295
  const childIsArray = false//templateNode.value.type !== 'MAP' && !!templateNode.value.items
@@ -257,30 +303,34 @@ module.exports = class TemplateUtil {
257
303
  const [,parent] = collectionStack
258
304
  const missingExistences = semantics.existences
259
305
  .filter(e => e)
260
- .filter(existence =>
261
- parent.get(existence.keyPath[0]) === targetNode
262
- ).filter(existence =>
263
- !existenceMap.get(existence.ref)
264
- )
306
+ .filter(existence => {
307
+ const requiredParent = existence.in &&
308
+ existenceMap.get(existence.in.ref) &&
309
+ existenceMap.get(existence.in.ref).node
310
+ return !existence.in || requiredParent === parent
311
+
312
+ })
313
+ .filter(({keyPath}) => parent.get(keyPath[0]) === targetNode)
314
+ .filter(({ref}) => !existenceMap.get(ref))
265
315
  missingExistences.forEach(existence => {
266
- const templateExistence = YAML.createNode(templateExistenceMap.get(existence.ref).node)
316
+ const templateNode = templateExistenceMap.get(existence.ref).node
317
+ const templateExistence = YAML.createNode(templateNode)
267
318
  templateExistence.spaceBefore = existence.spaceBefore
268
319
  targetNode.add(templateExistence)
269
- existenceMap.set(existence.ref, templateExistence)
270
320
  //this.fsUtil.writeYAML(targetPath, target)
271
321
  })
272
322
  }
273
323
  },
274
324
  }, source.contents)
275
325
 
276
- // 5. Re-validate the existences in the project
326
+ // 6. Re-validate the existences in the project
277
327
  _traverseYAMLNode(target.contents, null, {
278
328
  visitMap: (node, index) => {
279
329
  _validateConstraints(node, existenceMap, index)
280
330
  }
281
331
  })
282
332
 
283
- // 6. Create missing relationships
333
+ // 7. Create missing relationships
284
334
  _traverseYAMLNode(target.contents, null, {
285
335
  mergeCollection: (targetNode, templateNode) => {
286
336
  if (templateNode.type === 'SEQ') {
@@ -288,18 +338,18 @@ module.exports = class TemplateUtil {
288
338
  const relationship = semantics.relationships && semantics.relationships
289
339
  .filter(r => r)
290
340
  .find(relationship => {
291
- const existingNode = _getYAMLProperty(existenceMap.get(relationship.in).node, relationship.inKeyPath)
341
+ const existingNode = _getYAMLProperty(existenceMap.get(relationship.in.ref).node, relationship.inKeyPath)
292
342
  return targetNode === existingNode
293
343
  })
294
344
  if (!relationship) return
295
345
 
296
346
  const missingPairs = relationship.existences
297
- .filter(existenceKey =>
347
+ .filter(({ref}) =>
298
348
  !targetJSON.some(item =>
299
- _getProperty(existenceMap.get(existenceKey).json, relationship.existenceKeyPath) === item[relationship.into]
349
+ _getProperty(existenceMap.get(ref).json, relationship.existenceKeyPath) === item[relationship.into]
300
350
  )
301
351
  )
302
- .map(existenceKey => existenceMap.get(existenceKey).node.get(relationship.into))
352
+ .map(({ref}) => existenceMap.get(ref).node.get(relationship.into))
303
353
  missingPairs.forEach(pair => {
304
354
  targetNode.add(YAML.createNode({ [relationship.into]: pair }))
305
355
  //await this.fsUtil.writeYAML(targetPath, target)
package/lib/util/exec.js CHANGED
@@ -3,7 +3,7 @@ const { spawn, spawnSync } = require('child_process');
3
3
  const cds = require('@sap/cds/lib');
4
4
  const log = cds.log('exec');
5
5
 
6
- function* commandIterator(strs, ...args) {
6
+ function* commandIterator(strs, args) {
7
7
  for (let i=0; i<strs.length; i++) {
8
8
  const str = strs[i];
9
9
  const regex = /((\s+)|(["'])|$)/g;
@@ -38,7 +38,7 @@ function* commandIterator(strs, ...args) {
38
38
  }
39
39
  }
40
40
 
41
- function processTemplate(strs, args) {
41
+ function processTemplate(strs, ...args) {
42
42
  let quote;
43
43
  const result = [];
44
44
  let index = 0;
@@ -66,6 +66,9 @@ module.exports = class LiveReload {
66
66
  this.wsServer.on('close', () => { debug && debug('Socket closed') })
67
67
  this.wsServer.on('error', (e) => debug && debug(e))
68
68
  }
69
+ const close = () => { this.wsServer.close(); server.close(); debug && debug('Reload server closed') }
70
+ process.once('SIGTERM', close)
71
+ process.once('SIGINT', close)
69
72
  })
70
73
  }
71
74
 
@@ -7,7 +7,14 @@ module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, option
7
7
  // Phoning home...
8
8
  const EventEmitter = require('events')
9
9
  const emitter = new EventEmitter
10
- process.on('exit', () => { _kill (child); emitter.emit('quit') })
10
+
11
+ const killAndExit = (sig) => {
12
+ DEBUG && DEBUG('Signal:', sig)
13
+ emitter.emit('quit')
14
+ _kill (child, () => process.exit())
15
+ }
16
+ process.on('SIGTERM', killAndExit)
17
+ process.on('SIGINT', killAndExit)
11
18
 
12
19
  // Re-starting child process...
13
20
  const { fork } = require('child_process')