@sap/cds-dk 7.7.2 → 7.8.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 (58) hide show
  1. package/bin/add.js +40 -17
  2. package/bin/bind.js +21 -12
  3. package/bin/build.js +14 -2
  4. package/bin/cds.js +8 -0
  5. package/bin/deploy/to-sqlite.js +1 -1
  6. package/bin/help.js +4 -1
  7. package/bin/version.js +54 -22
  8. package/lib/bind/userCdsrcJsonStorageProvider.js +1 -1
  9. package/lib/build/buildTaskFactory.js +2 -2
  10. package/lib/build/provider/hana/index.js +1 -1
  11. package/lib/build/provider/internalBuildTaskProvider.js +23 -11
  12. package/lib/build/provider/mtx/resourcesTarBuilder.js +16 -4
  13. package/lib/build/provider/mtx-extension/extensionCompilation.js +13 -2
  14. package/lib/build/provider/mtx-extension/index.js +6 -2
  15. package/lib/build/provider/nodejs/index.js +4 -4
  16. package/lib/build/util.js +7 -18
  17. package/lib/compile/openapi/index.js +2 -1
  18. package/lib/index.js +2 -2
  19. package/lib/init/constants.js +3 -2
  20. package/lib/init/index.js +9 -23
  21. package/lib/init/plugin.js +8 -0
  22. package/lib/init/registries/mta.js +6 -0
  23. package/lib/init/template/data/as-csv.js +60 -0
  24. package/lib/init/template/data/as-json.js +441 -0
  25. package/lib/init/template/data/index.js +116 -86
  26. package/lib/init/template/enterprise-messaging/files/package.json.hbs +2 -1
  27. package/lib/init/template/enterprise-messaging/index.js +16 -3
  28. package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +2 -1
  29. package/lib/init/template/enterprise-messaging-shared/index.js +16 -3
  30. package/lib/init/template/helm/files/content-deployment/Chart.yaml +1 -1
  31. package/lib/init/template/helm/files/content-deployment/templates/job.yaml +7 -0
  32. package/lib/init/template/helm/files/content-deployment/values.schema.json +17 -0
  33. package/lib/init/template/helm/files/content-deployment/values.yaml +6 -1
  34. package/lib/init/template/helm/files/service-instance/Chart.yaml +1 -1
  35. package/lib/init/template/helm/files/service-instance/templates/serviceinstance.yaml +4 -0
  36. package/lib/init/template/helm/files/service-instance/values.schema.json +17 -1
  37. package/lib/init/template/helm/files/service-instance/values.yaml +4 -1
  38. package/lib/init/template/helm/files/values.yaml.hbs +2 -2
  39. package/lib/init/template/helm/files/web-application/Chart.yaml +1 -1
  40. package/lib/init/template/helm/files/web-application/templates/api-rule.yaml +4 -0
  41. package/lib/init/template/helm/files/web-application/templates/network-policy.yaml +15 -0
  42. package/lib/init/template/helm/files/web-application/values.schema.json +41 -0
  43. package/lib/init/template/helm/files/web-application/values.yaml +19 -1
  44. package/lib/init/template/helm/index.js +5 -2
  45. package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +2 -2
  46. package/lib/init/template/helm-unified-runtime/index.js +13 -7
  47. package/lib/init/template/html5-repo/index.js +0 -1
  48. package/lib/init/template/http/index.js +182 -0
  49. package/lib/init/template/kafka/files/mta.yaml.hbs +14 -0
  50. package/lib/init/template/kafka/files/package.json.hbs +22 -0
  51. package/lib/init/template/kafka/files/values.yaml +10 -0
  52. package/lib/init/template/kafka/index.js +39 -0
  53. package/lib/init/template/lint/index.js +4 -4
  54. package/lib/init/template/mta/files/mta.yaml.hbs +4 -0
  55. package/lib/init/template/multitenancy/files/values.yaml.hbs +2 -2
  56. package/lib/util/cf.js +6 -2
  57. package/npm-shrinkwrap.json +269 -293
  58. package/package.json +1 -1
package/lib/build/util.js CHANGED
@@ -117,9 +117,10 @@ function resolveRequiredSapModels(modelPaths) {
117
117
  })
118
118
  }
119
119
 
120
- async function getDefaultModelOptions(ws = false) {
120
+ async function getDefaultModelOptions(ws = false, filter) {
121
+ // Note: requires.toggles holds the actual kind representation and not the value 'true'
121
122
  const wildcards = cds.env.requires.toggles && cds.env.features.folders ? ['*', cds.env.features.folders] : '*'
122
- let paths = _filterModelPaths(cds.resolve(wildcards, false))
123
+ let paths = _filterModelPaths(cds.resolve(wildcards, false), filter)
123
124
 
124
125
  if (ws) {
125
126
  const workspaces = await getWorkspaces()
@@ -129,7 +130,7 @@ async function getDefaultModelOptions(ws = false) {
129
130
  workspaces.forEach(workspace => {
130
131
  cds.root = path.join(root, workspace)
131
132
  cds.env = cds.env.for('cds', cds.root)
132
- paths = paths.concat(_filterModelPaths(cds.resolve(cds.env.requires.toggles ? wildcards : '*', false)).map(p => path.join(workspace, p)))
133
+ paths = paths.concat(_filterModelPaths(cds.resolve(cds.env.requires.toggles ? wildcards : '*', false), filter).map(p => path.join(workspace, p)))
133
134
  })
134
135
  } finally {
135
136
  cds.root = root
@@ -165,17 +166,6 @@ function getI18nDefaultFolder() {
165
166
  return cds.env.i18n.folders[0] ?? 'i18n'
166
167
  }
167
168
 
168
- function isClassicMtx() {
169
- function _hasMtxDependency() {
170
- try {
171
- return !!require(path.join(cds.root, 'package.json'))?.dependencies?.['@sap/cds-mtx']
172
- } catch (e) {
173
- return false
174
- }
175
- }
176
- return /true/i.test(process.env.OLD_MTX) || _hasMtxDependency()
177
- }
178
-
179
169
  async function pathExists(path) {
180
170
  return fs.promises.access(path).then(() => true).catch(() => false);
181
171
  }
@@ -264,10 +254,10 @@ function _validateWsDependencies(wsRoot, dependencies, devDependencies) {
264
254
 
265
255
  /**
266
256
  * Returns a flat array of existing files and folders, trailing '/' or '\\' characters have been removed.
267
- * @param {...any} modelPaths array of files and folders - optionally nested
257
+ * @param {Array} modelPaths array of files and folders - optionally nested
268
258
  * @returns
269
259
  */
270
- function _filterModelPaths(...modelPaths) {
260
+ function _filterModelPaths(modelPaths, filter) {
271
261
  const model = new Set()
272
262
  // may contain nested arrays
273
263
  modelPaths = flatten(modelPaths)
@@ -287,7 +277,7 @@ function _filterModelPaths(...modelPaths) {
287
277
  }
288
278
  }
289
279
  })
290
- return [...model].map(m => normalizePath(m))
280
+ return [...model].filter(m => filter ? filter(m) : true).map(m => normalizePath(m))
291
281
  }
292
282
 
293
283
  /**
@@ -378,7 +368,6 @@ module.exports = {
378
368
  getDefaultModelOptions,
379
369
  flatten,
380
370
  getI18nDefaultFolder,
381
- isClassicMtx,
382
371
  normalizePath,
383
372
  pathExists,
384
373
  getWorkspaces,
@@ -3,7 +3,8 @@ const cds = require('../../cds')
3
3
 
4
4
  module.exports = function processor(csn, options = {}) {
5
5
  const edmOptions = Object.assign({
6
- odataOpenapiHints: true, // hint to compiler
6
+ odataOpenapiHints: true, // hint to cds-compiler
7
+ edm4OpenAPI: true, // downgrades certain OData errors to warnings in cds-compiler
7
8
  to: 'openapi' // hint to cds.compile.to.edm (usually set by CLI, but also do this in programmatic usages)
8
9
  }, options)
9
10
 
package/lib/index.js CHANGED
@@ -46,9 +46,9 @@ class CdsDkAdd {
46
46
  class CdsDkBuild {
47
47
  get build() { return super.build = require('./build') }
48
48
  }
49
- if (process.argv[2] in { 'add':1, 'init':1 }) {
49
+ if (process.argv[2] in { add:1, init:1, help:1 }) {
50
50
  cds.extend (cds.constructor) .with (CdsDkAdd)
51
- } else if (process.argv[2] in { 'build':1, 'deploy':1, 'push':1 }) {
51
+ } else if (process.argv[2] in { build:1, deploy:1, push:1 }) {
52
52
  cds.extend (cds.constructor) .with (CdsDkBuild)
53
53
  }
54
54
 
@@ -4,7 +4,7 @@ module.exports = {
4
4
  COMMAND_INIT: 'init',
5
5
  COMMAND_ADD: 'add',
6
6
 
7
- MAVEN_ARCHETYPE_VERSION: '2.7.0',
7
+ MAVEN_ARCHETYPE_VERSION: '2.8.1',
8
8
 
9
9
  OPTIONS: Object.freeze({
10
10
  JAVA: 'java',
@@ -35,6 +35,7 @@ module.exports = {
35
35
  ENTERPRISE_MESSAGING: 'enterprise-messaging',
36
36
  ENTERPRISE_MESSAGING_SHARED: 'enterprise-messaging-shared',
37
37
  REDIS_MESSAGING: 'redis-messaging',
38
+ KAFKA: 'kafka',
38
39
  APPROUTER: 'approuter',
39
40
  DESTINATION: 'destination',
40
41
  DATA: 'data',
@@ -56,7 +57,7 @@ module.exports = {
56
57
  PIPELINE_HELP: 'https://sap.github.io/jenkins-library/',
57
58
  SAMPLES: 'https://github.com/SAP-samples/cloud-cap-samples',
58
59
  CAPIRE: 'https://cap.cloud.sap',
59
- MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/development/#the-maven-archetype`,
60
+ MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/developing-applications/building#the-maven-archetype`,
60
61
  MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`
61
62
  }),
62
63
 
package/lib/init/index.js CHANGED
@@ -100,11 +100,11 @@ module.exports = class CDSGenerator {
100
100
  }
101
101
 
102
102
  /**
103
- * @param {string} facets, comma separated list of templates, no blanks
103
+ * @param {string} facets, comma separated list of facets
104
104
  * @param {any} options, additional options
105
105
  */
106
- async addCmd(facets) {
107
- if (!facets) throw 'You must specify a facet to add to the project.'
106
+ async add(facets) {
107
+ if (!facets?.length) throw 'You must specify a facet to add to the project.'
108
108
 
109
109
  this._initialize(null);
110
110
  this._greetings();
@@ -113,7 +113,7 @@ module.exports = class CDSGenerator {
113
113
  throw `The current folder doesn't seem to contain a project. None of the following files found: ${PROJECT_FILES.join(', ')}.`
114
114
  }
115
115
 
116
- await this._process();
116
+ await this._process(facets);
117
117
  await this.stepEnd();
118
118
  }
119
119
 
@@ -170,11 +170,11 @@ module.exports = class CDSGenerator {
170
170
  }
171
171
  }
172
172
 
173
- async _process() {
173
+ async _process(facets) {
174
174
  DEBUG?.(`Project path: ${cds.root}`);
175
175
 
176
176
  await this._validateOptions();
177
- await this._fillTemplateList();
177
+ await this._fillTemplateList(facets);
178
178
 
179
179
  for (const template of this.templateList) {
180
180
  console.log(`Adding feature '${template.name}'...`);
@@ -216,11 +216,11 @@ module.exports = class CDSGenerator {
216
216
  }
217
217
  }
218
218
 
219
- async _fillTemplateList() {
219
+ async _fillTemplateList(facets) {
220
220
  const templates = new Map
221
- const { command, options, argv } = cds.cli
221
+ const { command, options } = cds.cli
222
222
 
223
- const plugins = command === COMMAND_INIT ? options.add : new Set(argv[0].split(','))
223
+ const plugins = command === COMMAND_INIT ? options.add : new Set(facets)
224
224
 
225
225
  for (let plugin of plugins) {
226
226
  // Compat
@@ -299,20 +299,6 @@ module.exports = class CDSGenerator {
299
299
  if (cds.cli.command === COMMAND_INIT && !options.add.has(JAVA)) {
300
300
  options.add.add(NODEJS);
301
301
  }
302
-
303
- this._checkAdditionalOptions();
304
- }
305
-
306
- _checkAdditionalOptions() {
307
- const { options } = cds.cli
308
- for (const [key] of Object.entries(options)) {
309
- const keySplit = key.split(/:/)
310
- if (keySplit.length === 2) {
311
- if (!options.add.has(keySplit[0])) {
312
- console.log(`ignoring option ${key} since ${keySplit[0]} is not part of specified feature list: ${Array.from(options.add).join(', ')}`)
313
- }
314
- }
315
- }
316
302
  }
317
303
 
318
304
  async _validateOptions() {
@@ -10,6 +10,14 @@ module.exports = class Plugin {
10
10
  this.name = this.constructor.name.replace(/template/i, '').replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
11
11
  }
12
12
 
13
+ /**
14
+ * Provides command-line options for the plugin.
15
+ * @returns {Array.<{option: string, flag: string, shortcut?: string, help: string}>}
16
+ */
17
+ options() {
18
+ return []
19
+ }
20
+
13
21
  /**
14
22
  * Gets the required templates.
15
23
  * @returns {string[]} An array of strings representing required plugins.
@@ -32,6 +32,11 @@ const enterpriseMessaging = {
32
32
  where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'enterprise-messaging' }
33
33
  }
34
34
 
35
+ const kafkaMessaging = {
36
+ in: 'resources',
37
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'kafka' }
38
+ }
39
+
35
40
  const applicationLogging = {
36
41
  in: 'resources',
37
42
  where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'application-logs' }
@@ -144,6 +149,7 @@ module.exports = {
144
149
  destination,
145
150
  connectivity,
146
151
  enterpriseMessaging,
152
+ kafkaMessaging,
147
153
  hdbDeployer,
148
154
  hdiContainer,
149
155
  notificationTypesDeployer,
@@ -0,0 +1,60 @@
1
+ // const cds = require('../../../cds')
2
+
3
+ module.exports = async (data={}, csn, { headerOnly=false, queries=false, separator=',' }={}) => {
4
+ if (!queries) { // remove all projection-like entities
5
+ Object.keys(data)
6
+ .map(name => csn.definitions[name])
7
+ .filter (entity => entity.query)
8
+ .forEach(entity => { delete data[entity.name] })
9
+ }
10
+
11
+ // We might need multiple passes if data for associations/compositions is created on the fly.
12
+ // So loop as long as there is some non-string data left.
13
+ while (Object.values(data).some(d => typeof d === 'object')) {
14
+ Object.entries(data)
15
+ .filter(([_, value]) => typeof value === 'object')
16
+ .map(([name]) => csn.definitions[name])
17
+ .forEach(entity => {
18
+ const entityData = data[entity.name]
19
+ const header = []
20
+ const records = []
21
+ for (let i = 0; i < entityData.length; i++) {
22
+ let record = []
23
+ for (const element of Object.values(entity.elements).sort(keyFirstSorter)) {
24
+ const name = element.name
25
+ let value = entityData[i][name]
26
+ if (value === undefined)
27
+ continue
28
+ if (element._type === 'cds.Composition') {
29
+ data[element.target] = value // Add object for a new entity. Next pass will process it.
30
+ }
31
+ else if (element._type === 'cds.Association') {
32
+ if (i === 0) {
33
+ header.push(...Object.keys(value).map(foreignKey => name + '_' + foreignKey))
34
+ }
35
+ record.push(...Object.values(value))
36
+ }
37
+ else {
38
+ if (i === 0) header.push(name)
39
+ if (typeof value === 'string' && (value.includes('"') || value.includes(separator)))
40
+ value = `"${value.replace(/"/g, '""')}"` // escape double quotes, then quote the value
41
+ record.push(value)
42
+ }
43
+ }
44
+ records.push(record.join(separator))
45
+ }
46
+ let res = header.join(separator)
47
+ if (!headerOnly)
48
+ res += '\n' + records.join('\n')
49
+
50
+ data[entity.name] = res
51
+ })
52
+ }
53
+ return data
54
+ }
55
+
56
+ function keyFirstSorter(e1, e2) {
57
+ if (e1.key && !e2.key) return -1
58
+ if (!e1.key && e2.key) return 1
59
+ return 0 // preserve original order otherwise
60
+ }