@sap/cds-dk 6.2.2 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bin/bind.js +4 -4
  3. package/bin/deploy/to-h2.js +4 -0
  4. package/bin/deploy/to-sqlite.js +6 -4
  5. package/bin/help.js +1 -1
  6. package/bin/login.js +1 -0
  7. package/bin/mock.js +2 -2
  8. package/bin/push.js +29 -7
  9. package/lib/bind/userCdsrcJsonStorageProvider.js +4 -4
  10. package/lib/cds.js +6 -3
  11. package/lib/client/activate.js +1 -1
  12. package/lib/client/base_command.js +10 -10
  13. package/lib/client/helper/dependency_helper.js +10 -1
  14. package/lib/client/helper/logging.js +6 -2
  15. package/lib/client/pull.js +6 -6
  16. package/lib/client/push.js +33 -14
  17. package/lib/client/settings_manager.js +52 -21
  18. package/lib/compile/openapi/csdl2openapi.js +1 -1
  19. package/lib/compile/to-xsuaa.js +1 -1
  20. package/lib/import/odata/v2parser.js +37 -1
  21. package/lib/import/odata/v4parser.js +101 -23
  22. package/lib/init/constants.js +1 -1
  23. package/lib/init/index.js +23 -45
  24. package/lib/init/template/approuter/index.js +44 -48
  25. package/lib/init/template/auditlog/index.js +14 -10
  26. package/lib/init/template/cf-manifest/index.js +9 -11
  27. package/lib/init/template/connectivity/index.js +10 -10
  28. package/lib/init/template/data/index.js +20 -19
  29. package/lib/init/template/destinations/index.js +14 -9
  30. package/lib/init/template/enterprise-messaging/index.js +14 -9
  31. package/lib/init/template/extensibility/index.js +14 -9
  32. package/lib/init/template/hana/index.js +51 -57
  33. package/lib/init/template/helm/index.js +27 -59
  34. package/lib/init/template/helm/subchart/templates/_helpers.tpl +38 -0
  35. package/lib/init/template/helm/subchart/templates/deployment.yaml +12 -12
  36. package/lib/init/template/helm/subchart/values.md +565 -194
  37. package/lib/init/template/helm/subchart/values.schema.json +163 -8
  38. package/lib/init/template/helm/subchart/values.yaml +17 -1
  39. package/lib/init/template/html5-repo/index.js +15 -11
  40. package/lib/init/template/java/index.js +15 -11
  41. package/lib/init/template/kibana-logging/index.js +30 -31
  42. package/lib/init/template/lint/index.js +17 -18
  43. package/lib/init/template/mta/index.js +18 -40
  44. package/lib/init/template/mtx/index.js +2 -3
  45. package/lib/init/template/multitenancy/index.js +27 -20
  46. package/lib/init/template/nodejs/index.js +18 -20
  47. package/lib/init/template/notebook/index.js +5 -434
  48. package/lib/init/template/pipeline/index.js +10 -10
  49. package/lib/init/template/samples/index.js +7 -16
  50. package/lib/init/template/templateBase.js +17 -18
  51. package/lib/init/template/toggles/files/cdsrc.json +5 -0
  52. package/lib/init/template/toggles/files/cdsrc.json.hbs +7 -0
  53. package/lib/init/template/toggles/index.js +12 -6
  54. package/lib/init/template/xsuaa/index.js +44 -41
  55. package/lib/init/types.d.ts +0 -59
  56. package/lib/init/util/commandUtil.js +6 -7
  57. package/lib/init/util/fsUtil.js +23 -109
  58. package/lib/init/util/merge.js +319 -0
  59. package/lib/init/util/mvnArchetypeUtil.js +6 -5
  60. package/lib/init/util/projectReader.js +125 -300
  61. package/lib/init/util/spinner.js +10 -8
  62. package/lib/init/util/templateUtil.js +25 -368
  63. package/lib/lint/Linter.js +2 -3
  64. package/lib/lint/checks.js +0 -1
  65. package/lib/lint/io.js +18 -34
  66. package/npm-shrinkwrap.json +115 -261
  67. package/package.json +3 -3
  68. package/lib/init/template/notebook/cap_magic.py +0 -118
  69. package/lib/init/template/notebook/files/{{{name}}}.ipynb.hbs +0 -90
  70. package/lib/init/template/notebook/files/{{{name}}}.json +0 -102
  71. package/lib/init/template/notebook/notebook.js +0 -394
  72. package/lib/init/util/logger.js +0 -42
@@ -0,0 +1,319 @@
1
+ const YAML = require('@sap/cds-foss').yaml
2
+ const { copy, read, write, exists } = require('../../cds').utils
3
+ const { readYAML, writeYAML, readJSON } = require('./fsUtil')
4
+
5
+ function _isObject(item) {
6
+ return item && typeof item === 'object' && !Array.isArray(item)
7
+ }
8
+
9
+ function _deepMerge(target, source) {
10
+ const unique = array => [...new Set(array.map(JSON.stringify))].map(JSON.parse)
11
+ if (_isObject(target) && _isObject(source)) {
12
+ for (const key in source) {
13
+ if (_isObject(source[key])) {
14
+ if (!target[key]) Object.assign(target, { [key]: source[key] })
15
+ else _deepMerge(target[key], source[key])
16
+ } else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
17
+ target[key] = unique([...source[key], ...target[key]])
18
+ } else {
19
+ Object.assign(target, { [key]: target[key] || source[key] })
20
+ }
21
+ }
22
+ } else if (Array.isArray(target) && Array.isArray(source)) {
23
+ target = unique([...source, ...target])
24
+ }
25
+ return target ?? source
26
+ }
27
+
28
+ async function sortDependencies(packageJSONPath) {
29
+ const json = await read(packageJSONPath);
30
+ json.dependencies = Object.fromEntries(
31
+ Object.entries(json.dependencies).sort(([a],[b]) => a>b ? 1 : -1)
32
+ )
33
+ await write(packageJSONPath, json, { spaces: 2 });
34
+ }
35
+
36
+ async function mergeJSON(into, from, projectDescriptor, semantics) {
37
+ const source = typeof from === 'string' ? await readJSON(from, projectDescriptor) : from
38
+
39
+ if (typeof into === 'string' && !exists(into)) {
40
+ return write(into, source, { spaces: 2 })
41
+ }
42
+
43
+ let result
44
+ if (semantics) {
45
+ const target = await read(into)
46
+ const targetYAML = YAML.parseDocument(YAML.stringify(target))
47
+ const sourceYAML = YAML.parseDocument(YAML.stringify(source))
48
+ const resultYAML = await mergeYAML(targetYAML, sourceYAML, projectDescriptor, semantics)
49
+ result = YAML.parse(YAML.stringify(resultYAML))
50
+ } else {
51
+ const target = typeof into === 'string' ? await read(into) : into
52
+ result = _deepMerge(target, source)
53
+ }
54
+ if (typeof into === 'string') await write(into, result, { spaces: 2 })
55
+ return result
56
+ }
57
+
58
+ async function mergeYAML(into, from, projectDescriptor, semantics) {
59
+ const target =
60
+ typeof into === 'string' ? await readYAML(into) :
61
+ into instanceof YAML.Document ? into :
62
+ new YAML.Document(into)
63
+ const source =
64
+ typeof from === 'string' ? await readYAML(from, projectDescriptor)
65
+ : from instanceof YAML.Document ? from :
66
+ new YAML.Document(from)
67
+
68
+ if (!target || !target.contents) {
69
+ if (typeof into === 'string' && typeof from === 'string') {
70
+ return projectDescriptor ? writeYAML(into, source) : copy(from, into)
71
+ }
72
+ return /* has no YAML */
73
+ }
74
+
75
+ const existenceMap = new Map, templateExistenceMap = new Map()
76
+
77
+ const { additions, overwrites, deletions, relationships } = semantics
78
+ additions?.forEach(existence => {
79
+ existenceMap.set(existence.ref, undefined)
80
+ templateExistenceMap.set(existence.ref, undefined)
81
+ })
82
+ deletions?.forEach(deletion => {
83
+ existenceMap.set(deletion.item.ref, undefined)
84
+ })
85
+ relationships?.forEach(relationship => {
86
+ const [existence] = relationship.into
87
+ const [insertExistence] = relationship.insert
88
+ const ref = insertExistence.ref + ' -> ' + existence.ref
89
+ existenceMap.set(ref, undefined)
90
+ })
91
+
92
+ let collectionStack = [target.contents]
93
+
94
+ const _getProperty = (object, keyPath) => keyPath.split('.').reduce((p, k) => p && p[k], object)
95
+ const _getYAMLProperty = (object, keyPath) => keyPath.split('.').reduce((p, k) => p && p.get(k), object)
96
+
97
+ const _validateWhere = (node, dict, index) => {
98
+ const _addToMap = existences => existences?.filter(existence => {
99
+ let where = existence.in
100
+ if (typeof where === 'string') return true // just a key path on the document root
101
+ else where = where[0] // a more complex list of constraints, a sequence/array is involved
102
+ const neededParent = dict.get(where.ref)?.node
103
+ return collectionStack.includes(neededParent) // lookbehind in parent collection stack
104
+ // REVISIT: Only look behind until sequence is reached?
105
+ }).forEach(({ where, ref }) => {
106
+ const json = JSON.parse(String(node))
107
+ const whereFulfilled = where.every(constraint =>
108
+ constraint.isEqualTo === _getProperty(json, constraint.property)
109
+ )
110
+ if (whereFulfilled) {
111
+ const [collection] = collectionStack
112
+ dict.set(ref, { json, node, index, collection })
113
+ }
114
+ })
115
+ if (additions) _addToMap(additions)
116
+ if (deletions) _addToMap(deletions.map(deletion => deletion.item))
117
+ if (relationships) _addToMap(relationships?.map(({into: [existence]}) => existence).filter(e => e))
118
+ }
119
+
120
+ function _traverseYAMLNode(node, index, actions, templateNode) {
121
+ let shifted = false
122
+ if (YAML.isMap(node)) {
123
+ actions?.visitMap && actions.visitMap(node, index)
124
+ const [collection] = collectionStack
125
+ if (YAML.isSeq(collection)) {
126
+ collectionStack.unshift(node)
127
+ shifted = true
128
+ }
129
+ } else if (YAML.isPair(node) && node.value?.items) {
130
+ collectionStack.unshift(node.value)
131
+ } else if (YAML.isScalar(node) && semantics.forceOverwrite && templateNode) {
132
+ node.value = templateNode.value ?? templateNode.items?.[0]?.value?.value
133
+ }
134
+
135
+ if (node.items) {
136
+ if (YAML.isSeq(node) && templateNode?.items) {
137
+ actions?.mergeCollection && actions.mergeCollection(node, templateNode)
138
+ _traverseYAMLCollection(node, actions, templateNode)
139
+ } else {
140
+ _traverseYAMLCollection(node, actions, templateNode)
141
+ }
142
+ } else if (node.value?.items || node.value && semantics.forceOverwrite) {
143
+ _traverseYAMLNode(node.value, index, actions, templateNode && templateNode.value)
144
+ }
145
+
146
+ if (node.value?.items || shifted) {
147
+ collectionStack.shift()
148
+ }
149
+ }
150
+ _traverseYAMLNode.bind(this)
151
+
152
+ function _traverseYAMLCollection (collection, actions, templateCollection) {
153
+ if (!collection) return
154
+
155
+ const keyToIndex = new Map, templateIndexToIndex = new Map
156
+
157
+ // Map collection items to their semantic counterpart(s)
158
+ collection.items.forEach((node, i) => {
159
+ if (node.key) keyToIndex.set(node.key.value, i)
160
+ const existence = [...existenceMap.entries()].find(([,value]) => value?.node === node)
161
+ if (existence) {
162
+ const [existenceKey] = existence
163
+ const templateExistence = existenceKey && templateExistenceMap.get(existenceKey)
164
+ const templateIndex = templateExistence?.index
165
+ if (templateIndex !== undefined) templateIndexToIndex.set(templateIndex, i)
166
+ }
167
+ if (!templateCollection) _traverseYAMLNode(node, i, actions)
168
+ })
169
+
170
+ if (!templateCollection) return
171
+
172
+ templateCollection.items.forEach((templateNode, templateIndex) => {
173
+ if (YAML.isScalar(templateNode)) {
174
+ if (!collection.items.map(item => item.value).includes(templateNode.value)) {
175
+ collection.add(templateNode)
176
+ }
177
+ } else if (YAML.isPair(templateNode)) {
178
+ const i = keyToIndex.get(templateNode.key.value)
179
+ const [collection] = collectionStack
180
+ const targetNode = collection.items && collection.items[i]
181
+ if (targetNode) {
182
+ _traverseYAMLNode(targetNode, templateIndex, actions, templateNode)
183
+ } else if (actions.mergePair) {
184
+ actions.mergePair(collection, templateNode)
185
+ }
186
+ } else if (YAML.isMap(templateNode)) {
187
+ const targetNode = collection.items[templateIndexToIndex.get(templateIndex)]
188
+ if (actions.mergeCollection) actions.mergeCollection(targetNode, templateNode)
189
+ if (targetNode) {
190
+ _traverseYAMLNode(targetNode, templateIndex, actions, templateNode)
191
+ }
192
+ }
193
+ })
194
+ }
195
+ _traverseYAMLCollection.bind(this)
196
+
197
+ // 1. Validate the existences in the template
198
+ _traverseYAMLNode(source.contents, null, {
199
+ visitMap: (node, index) => {
200
+ _validateWhere(node, templateExistenceMap, index)
201
+ }
202
+ })
203
+
204
+ // 2. Validate the existences in the project
205
+ _traverseYAMLNode(target.contents, null, {
206
+ visitMap: (node, index) => {
207
+ _validateWhere(node, existenceMap, index)
208
+ }
209
+ })
210
+
211
+ // 3. Apply overwrites to already found existences
212
+ overwrites?.forEach(({ item, withValue }) => {
213
+ const keyPath = Array.isArray(item) && typeof item[0] === 'string' ? item[0] : typeof item === 'string' ? item : item[1]
214
+ const inExistence = Array.isArray(item) && typeof item[0] === 'object' ? item[0] : typeof item === 'object' ? item : item[1]
215
+ const node = inExistence && existenceMap.get(inExistence.ref) ? existenceMap.get(inExistence.ref).node : collectionStack[collectionStack.length - 1]
216
+ const keys = keyPath.split('.')
217
+ if (!node.getIn(keys)) return
218
+ _getYAMLProperty(node, keys.slice(0, keys.length - 1).join('.'))
219
+ .set(keys[keys.length - 1], withValue)
220
+ })
221
+
222
+ // 4. Delete existences from the project (e.g. separate deployer module when adding mtx)
223
+ deletions?.forEach(({ item: { ref }, relationships }) => {
224
+ const existence = existenceMap.get(ref)
225
+ if (!existence) return
226
+ existence.collection.delete(existence.index)
227
+
228
+ relationships?.forEach(relationship => {
229
+ const [allWithinKeyPath, inKeyPath, into] = relationship.allWithin
230
+ const parent = _getYAMLProperty(target, allWithinKeyPath)
231
+ for (const child of parent.items) {
232
+ const grandchild = _getYAMLProperty(child, inKeyPath)
233
+ const i = grandchild?.items?.findIndex(node =>
234
+ node.get(into) === _getProperty(existence.json, relationship.removeProperty)
235
+ ) ?? - 1
236
+ if (i > -1) grandchild.delete(i)
237
+ }
238
+ })
239
+ })
240
+
241
+ // 5. Create missing existences and pairs
242
+ _traverseYAMLNode(target.contents, null, {
243
+ mergePair: (collection, templateNode) => {
244
+ if (YAML.isMap(collection)) {
245
+ collection.add(templateNode)
246
+ }
247
+ },
248
+ mergeCollection: (targetNode, templateNode) => {
249
+ if (YAML.isSeq(templateNode)) {
250
+ let [,parent] = collectionStack
251
+ additions?.filter(addition => {
252
+ if (typeof addition.in === 'string' || Array.isArray(addition.in) && typeof addition.in[0] === 'string') return true
253
+ const inExistence = Array.isArray(addition.in) ? existenceMap.get(addition.in[0].ref) : existenceMap.get(addition.in.ref)
254
+ return inExistence?.node === parent
255
+ })
256
+ .filter(addition => {
257
+ const keyPath = typeof addition.in === 'string' ? addition.in : Array.isArray(addition.in) && typeof addition.in[0] === 'string' ? addition.in[0] : addition.in[1]
258
+ const keys = keyPath.split('.')
259
+ if (keys.length > collectionStack.length + 1) return
260
+ if (keys.length > 1) parent = collectionStack[keys.length]
261
+ return _getYAMLProperty(parent, keyPath) === targetNode
262
+ })
263
+ .filter(({ ref }) => {
264
+ return !existenceMap.get(ref)
265
+ })
266
+ .forEach(({ ref, at }) => {
267
+ const templateNode = templateExistenceMap.get(ref).node
268
+ at !== undefined ? targetNode.items.splice(at, 0, templateNode) : targetNode.add(templateNode)
269
+ })
270
+ }
271
+ },
272
+ }, source.contents)
273
+
274
+ // 6. Re-validate the existences in the project
275
+ _traverseYAMLNode(target.contents, null, {
276
+ visitMap: (node, index) => {
277
+ _validateWhere(node, existenceMap, index)
278
+ }
279
+ })
280
+
281
+ // 7. Create missing relationships
282
+ _traverseYAMLNode(target.contents, null, {
283
+ mergeCollection: (targetNode, templateNode) => {
284
+ if (YAML.isSeq(templateNode)) {
285
+ const targetJSON = YAML.parse(String(targetNode))
286
+ const relationship = semantics.relationships?.filter(r => r)
287
+ .find(relationship => {
288
+ const [existence, keyPath] = relationship.into
289
+ if (!existenceMap.get(existence.ref)) return false
290
+ const existingNode = _getYAMLProperty(existenceMap.get(existence.ref).node, keyPath)
291
+ return targetNode === existingNode
292
+ })
293
+ if (!relationship) return
294
+
295
+ const intoKey = relationship.into[relationship.into.length - 1]
296
+ const [existence, existenceKeyPath] = relationship.insert
297
+ const missingPairs = [existence]
298
+ .filter(({ref}) =>
299
+ !targetJSON.some(item =>
300
+ _getProperty(existenceMap.get(ref).json, existenceKeyPath) === item[intoKey]
301
+ )
302
+ )
303
+ .map(({ref}) => existenceMap.get(ref).node.get(intoKey))
304
+ missingPairs.forEach(pair => {
305
+ targetNode.add({ [intoKey]: pair })
306
+ })
307
+ }
308
+ }
309
+ }, source.contents)
310
+
311
+ typeof into === 'string' && await writeYAML(into, target)
312
+ return target
313
+ }
314
+
315
+ module.exports = {
316
+ sortDependencies,
317
+ mergeJSON,
318
+ mergeYAML
319
+ }
@@ -2,10 +2,11 @@
2
2
  const { MAVEN_ARCHETYPE_VERSION, OPTION_JAVA_MVN,
3
3
  OPTION_JAVA_PACKAGE, OPTION_SAMPLES, REGEX_JAVA_PACKAGE, OPTION_HANA } = require('../constants');
4
4
 
5
- const { defaultLogger, nullLogger } = require('../util/logger');
6
-
7
5
  const DEFAULT_GROUP_ID = 'customer';
8
6
 
7
+ const LOG = console
8
+ const DEBUG = /\bcli\b/.test(process.env.DEBUG) && console.debug
9
+
9
10
  class MvnArchetypeUtil {
10
11
 
11
12
  _scanMvnParams(mvnParams, params) {
@@ -31,7 +32,7 @@ class MvnArchetypeUtil {
31
32
 
32
33
  params.set('-B');
33
34
 
34
- if (defaultLogger.isDebug) {
35
+ if (DEBUG) {
35
36
  params.set('-X'); params.set('-e');
36
37
  }
37
38
 
@@ -87,7 +88,7 @@ class MvnArchetypeUtil {
87
88
  return packageName.toLowerCase().replace(/-/g, '_').replace(/\.([0-9])/g, '._$1').replace(/^([0-9])/, '_$1').replace(/\.+/g, '.');
88
89
  }
89
90
 
90
- getGenerateCmdArgs(projectName, options, logger = nullLogger) {
91
+ getGenerateCmdArgs(projectName, options) {
91
92
  const params = new Map();
92
93
 
93
94
  // set header
@@ -133,7 +134,7 @@ class MvnArchetypeUtil {
133
134
 
134
135
  if (!packageName.match(REGEX_JAVA_PACKAGE)) {
135
136
  packageName = this.sanitizePackageName(packageName);
136
- logger.warn(`The derived package name '${groupId}.${projectName}' is not a valid Java package name. Using '${packageName}' instead.`);
137
+ LOG.warn(`The derived package name '${groupId}.${projectName}' is not a valid Java package name. Using '${packageName}' instead.`);
137
138
  }
138
139
  params.set(`-Dpackage`, packageName);
139
140
  }