@sap/cds-dk 8.0.3 → 8.1.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 (43) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/bin/bind.js +3 -3
  3. package/bin/cds.js +11 -15
  4. package/bin/init.js +3 -6
  5. package/bin/version.js +7 -6
  6. package/lib/build/buildTaskFactory.js +29 -12
  7. package/lib/build/constants.js +1 -1
  8. package/lib/build/provider/hana/index.js +5 -6
  9. package/lib/build/provider/internalBuildTaskProvider.js +2 -35
  10. package/lib/build/provider/mtx/resourcesTarBuilder.js +2 -2
  11. package/lib/build/provider/mtx-sidecar/index.js +10 -3
  12. package/lib/cds.js +1 -0
  13. package/lib/compile/to-mermaid.js +27 -12
  14. package/lib/compile/to_openapi.js +2 -1
  15. package/lib/deploy/to-hana/hana.js +10 -8
  16. package/lib/deploy/to-hana/hdiDeployUtil.js +10 -1
  17. package/lib/import/common.js +1 -1
  18. package/lib/import/message.js +2 -2
  19. package/lib/import/odata/v2parser.js +2 -2
  20. package/lib/init/bas.js +73 -32
  21. package/lib/init/constants.js +5 -2
  22. package/lib/init/index.js +6 -4
  23. package/lib/init/merge.js +5 -3
  24. package/lib/init/projectReader.js +3 -4
  25. package/lib/init/template/audit-logging/files/package.sidecar.json +7 -0
  26. package/lib/init/template/audit-logging/index.js +1 -1
  27. package/lib/init/template/cf-manifest/index.js +4 -0
  28. package/lib/init/template/data/as-json.js +22 -12
  29. package/lib/init/template/data/index.js +1 -1
  30. package/lib/init/template/helm-unified-runtime/index.js +3 -1
  31. package/lib/init/template/http/index.js +37 -28
  32. package/lib/init/template/multitenancy/index.js +9 -31
  33. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +0 -1
  34. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +0 -1
  35. package/lib/init/template/sample/files/nodejs/admin-service.js +13 -8
  36. package/lib/init/template/sample/files/nodejs/cat-service.js +27 -15
  37. package/lib/init/template/sample/files/srv/cat-service.cds +5 -1
  38. package/lib/init/template/typescript/files/package.json +3 -3
  39. package/lib/init/template/workzone/index.js +0 -3
  40. package/lib/init/template/xsuaa/index.js +10 -3
  41. package/lib/mtx/push.js +3 -1
  42. package/npm-shrinkwrap.json +19 -16
  43. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,56 @@ This project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
8
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
9
9
 
10
+ ## Version 8.1.1 - 2024-08-07
11
+
12
+ ### Added
13
+
14
+ - Add more features to CAP project creation wizard in SAP Business Application Studio.
15
+ - `cds.add.merge` now you specify a `key` in its `deletions` semantics.
16
+
17
+ ### Changed
18
+
19
+ - `cds init` uses latest Maven Java archetype version 3.1.0 for creating Java projects.
20
+
21
+ ### Fixed
22
+
23
+ - `cds import` now gives proper error message for OData import in case of invalid OData version or invalid encoding.
24
+ - `cds import` now able to ignore invalid escape sequence in the description of properties for OData V2.
25
+ - `cds add mta` does not error when the `app/router` folder was deleted.
26
+ - `cds version -i` does not show a globally installed version when none is installed or linked.
27
+ - `cds compile --to openapi` no longer fails with `Error: Unknown protocol: graphql`.
28
+ - `cds add data` can now correctly handle circular dependencies in entities that point to themselves via associations and compositions.
29
+ - `cds add` returns correct suggestions for shell completion.
30
+ - `cds version` reports correct `@sap/cds-compiler` version.
31
+ - `cds deploy` falls back correctly to `@sap/hana-client` when `HDI_DEPLOY_OPTIONS='{"use_hdb":false}'` is set.
32
+
33
+ ## Version 8.1.0 - 2024-07-26
34
+
35
+ ### Added
36
+
37
+ - `cds compile --to mermaid` can be configured with a layout direction like `LR`.
38
+ - `cds deploy --to hana` now shows the `hdi-deploy` version when running.
39
+
40
+ ### Changed
41
+
42
+ - Better generation of http files with `cds add http`, especially for draft requests.
43
+
44
+ ### Fixed
45
+
46
+ - `cds add` recognizes `hasCfManifest` as inactive unless a `manifest.yml` file exists.
47
+ - `cds add` help and suggestions are only shown when the `help` method is implemented.
48
+ - `cds add sample` does not include the deprecated `synchronizationMode` property in `manifest.json` files any more.
49
+ - `cds compile --to mermaid` no longer produces empty `namespace` blocks that would lead to rendering errors.
50
+ - `cds compile --to mermaid` no longer fails for complex queries like joins.
51
+ - `cds build --for mtx-sidecar` now correctly supports different `srv` folder name.
52
+ - `cds add audit-log` in combination with `cds add multitenancy` correctly adds the audit-log dependency to the MTX sidecar.
53
+ - `cds build` adds the correct hdbtabledata file paths to the hana result set.
54
+ - `cds add` plugins can now correctly interpret `cds.cli` properties.
55
+ - `cds deploy --to hana --dry` now shows correct files with their content.
56
+ - `cds version` now reports the correct version of `@sap/cds` if this one is installed locally along with a local `@sap/cds-dk` installation.
57
+ - Shell completion now works correctly for `cds init --add`.
58
+ - `cds build --for mtx-sidecar` no longer fails if the severity of a compilation error has been downgraded from `Error` to `Warning`.
59
+
10
60
  ## Version 8.0.3 - 2024-07-20
11
61
 
12
62
  ### Fixed
@@ -36,6 +86,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
36
86
  - `cds add helm` removed `saasRegistryParameters` key and moved parameters to `saas-registry`.
37
87
  - `cds init` no longer creates a `.cdsrc.json` for Node.js projects.
38
88
  - `cds build` configuration cleanup for `data.model` and `service.model` to only support configuration of a single model folder.
89
+ - `cds build` now fully supports default model definitions for custom build plugins.
39
90
 
40
91
  ## Version 8.0.1 - 2024-07-09
41
92
 
@@ -142,7 +193,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
142
193
 
143
194
  - `cds add notebook` is removed. CAP Jupyter Notebooks have been replaced by custom CAP Notebooks for VS Code which are now part of the CDS Editor. See https://cap.cloud.sap/docs/tools/#add-cds-editor for more.
144
195
 
145
-
146
196
  ## Version 7.9.6 - 2024-07-19
147
197
 
148
198
  ### Fixed
package/bin/bind.js CHANGED
@@ -46,7 +46,7 @@ module.exports = Object.assign(cds_bind, {
46
46
 
47
47
  *-a | --to-app-services* <app>
48
48
 
49
- Bind to a given application (Cloud Foundry only).
49
+ Bind services of a given application (Cloud Foundry only).
50
50
 
51
51
  *--no-create-service-key*
52
52
 
@@ -164,11 +164,11 @@ async function cds_bind(args, options) {
164
164
  options.targets = options.to.split(/,/g);
165
165
 
166
166
  if (options.targets.length >= 2 && options.serviceArg) {
167
- throw `Service argument cannot be specified together with multiple targets (${term.bold('--to')}) services. Use one service per call or omit the service argument.`;
167
+ throw `Service argument cannot be specified together with multiple target (${term.bold('--to')}) services. Use one service per call or omit the service argument.`;
168
168
  }
169
169
 
170
170
  if (options.targets.length >= 2 && options.kind) {
171
- throw `The option '--kind' cannot be specified together with multiple targets (${term.bold('--to')}) services. Use one service per call or omit the ${term.bold('--kind')} option.`;
171
+ throw `The option '--kind' cannot be specified together with multiple target (${term.bold('--to')}) services. Use one service per call or omit the ${term.bold('--kind')} option.`;
172
172
  }
173
173
 
174
174
  const services = await bind({ ...options, out });
package/bin/cds.js CHANGED
@@ -55,8 +55,15 @@ const cli = {
55
55
  argv = argv.slice(0, endOfOptions)
56
56
  }
57
57
 
58
- const unknown = new Set
59
- const args = this.args(task, argv, unknown)
58
+ // REVISIT: Have to load plugins to allow for plugin-contributed flags!
59
+ if (cmd === 'add') {
60
+ cds.cli = { command: 'add' }
61
+ await cds.plugins
62
+ for (const [, Plugin] of cds.add.registered) {
63
+ task.options.push(...Object.keys((new Plugin).options()).map(key => '--'+key))
64
+ }
65
+ }
66
+ const args = this.args(task, argv)
60
67
  args[0].push(...appendArgs)
61
68
  cds.cli = {
62
69
  command: cmd,
@@ -64,17 +71,6 @@ const cli = {
64
71
  options: args[1],
65
72
  }
66
73
 
67
- if (cmd === 'add') { // cds add plugins allow additional custom options
68
- await cds.plugins
69
- for (const [, Plugin] of cds.add.registered) {
70
- const options = Object.keys((new Plugin).options()).map(key => '--'+key)
71
- task.options.push(options)
72
- options.forEach(o => unknown.delete(o))
73
- }
74
- }
75
- if (unknown.size === 1) throw 'Invalid option: ' + Array.from(unknown)[0]
76
- if (unknown.size > 1) throw 'Invalid options: ' + Array.from(unknown).join(', ')
77
-
78
74
  if (args[1]?.['resolve-bindings']) await _resolveBindings({ silent: cmd === 'env' })
79
75
 
80
76
  // cds.root may for some subcommands not point to the correct directory (e.g. cds w where users can pass a project directory)
@@ -106,7 +102,7 @@ const cli = {
106
102
  },
107
103
 
108
104
  // TODO replace w/ common arg parser from node
109
- args (task, argv, unknown) {
105
+ args (task, argv) {
110
106
 
111
107
  const { options:o=[], flags:f=[], shortcuts:s=[] } = task
112
108
  const _global = /^--(profile|production|sql|odata|add-.*|build-.*|cdsc-.*|odata-.*|folders-.*)$/
@@ -120,7 +116,7 @@ const cli = {
120
116
  else if ((k = o.indexOf(a)) >= 0) add(o[k],argv[++i])
121
117
  else if ((k = f.indexOf(a)) >= 0) add(f[k])
122
118
  else if (_global.test(a)) add_global(a, _flags[a] || argv[++i])
123
- else unknown.add(a)
119
+ else throw 'Invalid option: '+ a
124
120
  }
125
121
  // consistent production setting for NODE_ENV and CDS_ENV
126
122
  // if (process.env.NODE_ENV !== 'production') process.env.NODE_ENV = process.env.CDS_ENV?.split(',').find(p => p === 'production') || process.env.NODE_ENV
package/bin/init.js CHANGED
@@ -61,18 +61,15 @@ async function handleCompletion(currentWord, previousWord, argv, util) {
61
61
  return allOptionsFlags;
62
62
  }
63
63
 
64
- if (previousWord === '--add') {
65
- return util.completionFs.readTemplates(argv);
66
- }
67
-
68
64
  if (previousWord === '--java:package') {
69
65
  const excludes = ['_merging'];
70
66
  return (await util.completionFs.readdir(currentWord, { files: false }))
71
67
  .filter(e => !excludes.includes(e));
72
68
  }
73
69
 
74
- if (!currentWord && init.options?.includes(previousWord)) {
75
- return [];
70
+ const templates = await util.completionFs.readTemplates(argv);
71
+ if (previousWord === '--add' || argv.includes('--add') && previousWord.endsWith(',')) {
72
+ return templates;
76
73
  }
77
74
 
78
75
  return allOptionsFlags;
package/bin/version.js CHANGED
@@ -61,7 +61,8 @@ function list_versions(args, options) {
61
61
  }
62
62
 
63
63
  function info({ root:cwd=process.cwd() }) {
64
- const env = require('../lib').env.for('cds', cwd)
64
+ const cds = require('../lib')
65
+ const env = cds.env.for('cds', cwd)
65
66
  env.cli ??= {}; env.cli.version ??= {}
66
67
  const javaInfo = !env.cli.version.skipjava && env['project-nature'] === 'java'
67
68
  if (javaInfo && process.stderr.isTTY) {
@@ -70,14 +71,14 @@ function info({ root:cwd=process.cwd() }) {
70
71
 
71
72
  const npmRoot = execSync('npm root -g').toString().trim()
72
73
  let versions = {
73
- ..._versions_from(_pkg_path('@sap/cds-dk', __dirname), '@sap/cds-dk (global)'), // ... linked in cap/dev setup
74
- ..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // ... or installed globally
74
+ ..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // linked in cap/dev or installed globally
75
75
  ..._versions_from(_pkg_path('@sap/cds-dk', cwd)), // local dk, important for Java projects
76
76
  ..._versions_from(_pkg_path('@sap/cds', __dirname)), // @sap/cds from here
77
- ..._versions_from(cwd), // ... or from local app dependencies
78
- ..._versions_from(_pkg_path('@sap/cds-compiler', cwd, __dirname)), // compiler is no direct dependency, so list it explicitly
77
+ ..._versions_from(_pkg_path('@sap/cds', cds.home)), // ... or local @sap/cds
78
+ ..._versions_from(cwd), // ... more from local app dependencies
79
+ '@sap/cds-compiler': cds.compiler.version(),
79
80
  'Node.js': process.version,
80
- 'home': require('../lib/cds').home, // effective (local) cds
81
+ 'home': cds.home, // effective cds home
81
82
  }
82
83
  // sort Node entries
83
84
  versions = Object.keys(versions).sort().reduce((all, key) => { all[key] = versions[key]; return all }, {})
@@ -3,7 +3,7 @@ const path = require('path')
3
3
  const cds = require('../cds')
4
4
  const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
5
5
  const { hasJavaNature, getProperty, getDefaultModelOptions, hasOptionValue, BuildError } = require('./util')
6
- const { FILE_EXT_CDS, OPTION_WS } = require("./constants")
6
+ const { FILE_EXT_CDS, OPTION_WS, IGNORE_DEFAULT_MODELS, NODEJS_MODEL_EXCLUDE_LIST, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF } = require("./constants")
7
7
  const term = require('../util/term')
8
8
  const DEBUG = cds.debug('cli|build')
9
9
 
@@ -95,11 +95,7 @@ class BuildTaskFactory {
95
95
  task.options.model = [task.options.model]
96
96
  }
97
97
  })
98
- // there must be at least one task without model options
99
- if (!tasks.some(task => !task.options?.model?.length > 0)) {
100
- return
101
- }
102
- const modelPaths = await getDefaultModelOptions()
98
+ const modelPaths = await getDefaultModelOptions(false)
103
99
  let wsModelPaths
104
100
  // calculate only once
105
101
  if (cds.cli.options?.[OPTION_WS] || tasks.some(task => hasOptionValue(task.options?.[OPTION_WS], true))) {
@@ -107,10 +103,12 @@ class BuildTaskFactory {
107
103
  }
108
104
  // set default model options
109
105
  tasks.forEach(task => {
110
- this._setTaskModelOptions(task, cds.cli.options?.[OPTION_WS] || hasOptionValue(task.options?.[OPTION_WS], true) ? wsModelPaths : modelPaths)
111
106
  if (!task.src) {
112
107
  throw new BuildError(`Mandatory property 'src' not defined for build task '${task.for}'.`)
113
108
  }
109
+ const optionWs = cds.cli.options?.[OPTION_WS] || hasOptionValue(task.options?.[OPTION_WS], true)
110
+ // shallow copy model paths
111
+ this._setTaskModelOptions(task, optionWs ? [...wsModelPaths] : [...modelPaths])
114
112
  })
115
113
  }
116
114
 
@@ -180,14 +178,33 @@ class BuildTaskFactory {
180
178
  }
181
179
 
182
180
  static _setTaskModelOptions(task, defaultModelPaths) {
181
+ // bootstrap service model is only required for Nodejs build task - issues/12770#issuecomment-1805719
182
+ if (task.for !== BUILD_TASK_NODEJS && task.for !== BUILD_TASK_NODE_CF) {
183
+ defaultModelPaths = defaultModelPaths.filter(p => !NODEJS_MODEL_EXCLUDE_LIST.includes(p))
184
+ }
185
+
186
+ let taskModelPaths = task.options?.model
187
+ if (taskModelPaths && !Array.isArray(taskModelPaths)) {
188
+ taskModelPaths = [taskModelPaths]
189
+ }
190
+ const allowList = new RegExp(`^@sap/cds${cds.env.requires.toggles ? '|^' + cds.env.features.folders.replace('*', '') : ''}`)
183
191
  task.options = task.options || {}
184
- if (!task.options.model?.length > 0) {
185
- defaultModelPaths = new Set(defaultModelPaths)
186
- if (task.src) {
187
- defaultModelPaths.add(task.src)
192
+ let modelPaths = []
193
+ if (taskModelPaths?.length) {
194
+ if (!hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
195
+ modelPaths = defaultModelPaths.filter(p => p.match(allowList))
196
+ }
197
+ } else {
198
+ modelPaths = [...defaultModelPaths]
199
+ if (!modelPaths.includes(task.src)) {
200
+ modelPaths.push(task.src)
201
+ }
202
+ if (hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
203
+ // all default models except the built-in models
204
+ modelPaths = modelPaths.filter(p => !p.match(allowList))
188
205
  }
189
- task.options.model = [...defaultModelPaths]
190
206
  }
207
+ task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(modelPaths) : modelPaths)]
191
208
  }
192
209
 
193
210
  static _stringifyTask(task) {
@@ -48,7 +48,7 @@ exports.FILE_EXT_CDS = ".cds"
48
48
  exports.MTX_SIDECAR_FOLDER = "mtx/sidecar" // default name of the mtx sidecar folder
49
49
  exports.DEFAULT_CSN_FILE_NAME = "csn.json"
50
50
  // REVISIT: the models are not required if a custom server.js file is used for MTX bootstrap
51
- exports.NODEJS_MODEL_EXCLUDE_LIST = ['@sap/cds/srv/mtx', '@sap/cds-mtxs/srv/bootstrap']
51
+ exports.NODEJS_MODEL_EXCLUDE_LIST = ['@sap/cds-mtxs/srv/bootstrap']
52
52
 
53
53
  exports.CDS_CONFIG_PATH_SEP = "/"
54
54
  exports.SEVERITY_ERROR = "Error"
@@ -26,7 +26,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
26
26
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
27
27
  this._result = {
28
28
  dest: this.task.dest,
29
- hana: []
29
+ hana: new Set()
30
30
  }
31
31
  }
32
32
  this.task.options.compileDest = path.resolve(this.task.dest, this.task.options.compileDest || DEFAULT_COMPILE_DEST_FOLDER)
@@ -228,7 +228,6 @@ class HanaBuildPlugin extends InternalBuildPlugin {
228
228
  }
229
229
 
230
230
  const promises = []
231
- const relDest = path.relative(this.task.dest, this.task.options.compileDest)
232
231
  const options = { ...this.options(), messages: this.messages, dirs: csvDirs, baseDir: this.task.options.compileDest }
233
232
 
234
233
  const toHdbtabledata = cds.compile.to.hdbtabledata ?? require(path.join(cds.home, 'bin/build/provider/hana/2tabledata')) // cds@6 compatibility
@@ -240,7 +239,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
240
239
  let tableDataPath = csvFolder.startsWith(this.task.options.compileDest) ? csvFolder : this.task.options.compileDest
241
240
  tableDataPath = path.join(tableDataPath, file)
242
241
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
243
- this._result.hana.push(path.join(relDest, file))
242
+ this._result.hana.add(path.relative(this.task.dest, tableDataPath))
244
243
  }
245
244
  promises.push(this.write(tableData).to(tableDataPath))
246
245
  }
@@ -296,7 +295,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
296
295
 
297
296
  hdiPlugins.add(suffix)
298
297
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
299
- this._result.hana.push(path.join(relDest, file))
298
+ this._result.hana.add(path.join(relDest, file))
300
299
  }
301
300
  promises.push(this.write(content).to(path.join(this.task.options.compileDest, file)))
302
301
  if (suffix === FILE_EXT_HDBTABLE) {
@@ -345,7 +344,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
345
344
  migrationTableFiles.push(path.join(relDbSrcDir, fileName))
346
345
  if (changed) {
347
346
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
348
- this._result.hana.push(path.join("src", fileName))
347
+ this._result.hana.add(path.join("src", fileName))
349
348
  }
350
349
  promises.push(this.write(content).to(path.join(dbSrcDir, fileName)))
351
350
  } else {
@@ -354,7 +353,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
354
353
  }
355
354
  } else {
356
355
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
357
- this._result.hana.push(path.join(relDestDir, fileName))
356
+ this._result.hana.add(path.join(relDestDir, fileName))
358
357
  }
359
358
  promises.push(this.write(content).to(path.join(this.task.options.compileDest, fileName)))
360
359
  if (suffix === FILE_EXT_HDBTABLE) {
@@ -1,11 +1,10 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const cds = require('../../cds')
4
- const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue, normalizePath } = require('../util')
4
+ const { hasJavaNature, BuildError, normalizePath } = require('../util')
5
5
  const BuildTaskProvider = require('../buildTaskProvider')
6
6
  const { FILE_EXT_CDS, BUILD_TASK_HANA, BUILD_TASK_FIORI, BUILD_TASK_JAVA, BUILD_TASK_JAVA_CF, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_MTX,
7
- BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION, NODEJS_MODEL_EXCLUDE_LIST,
8
- IGNORE_DEFAULT_MODELS, OPTION_WS } = require("../constants")
7
+ BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION } = require("../constants")
9
8
  const ResourcesTarBuilder = require('./mtx/resourcesTarBuilder')
10
9
  const DEBUG = cds.debug('cli|build')
11
10
 
@@ -19,10 +18,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
19
18
  }
20
19
 
21
20
  async applyTaskDefaults(task) {
22
- // src folder needs to be initialized first
23
21
  InternalBuildTaskProvider._setDefaultSrcFolder(task)
24
-
25
- await InternalBuildTaskProvider._setDefaultModel(task)
26
22
  }
27
23
 
28
24
  getPlugin(task) {
@@ -284,35 +280,6 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
284
280
  }
285
281
  }
286
282
 
287
- static async _setDefaultModel(task) {
288
- const ws = hasOptionValue(task.options?.[OPTION_WS], true) || cds.cli.options?.[OPTION_WS]
289
- // REVISIT: filter nodejs bootstrap service models for Java - issues/12770#issuecomment-1805719
290
- const filter = hasJavaNature() ? (m) => !NODEJS_MODEL_EXCLUDE_LIST.includes(m) : null
291
- let taskModelPaths = task.options?.model
292
- if (taskModelPaths && !Array.isArray(taskModelPaths)) {
293
- taskModelPaths = [taskModelPaths]
294
- }
295
- const allowList = new RegExp(`^@sap/cds${cds.env.requires.toggles ? '|^' + cds.env.features.folders.replace('*', '') : ''}`)
296
- task.options = task.options || {}
297
- let defaultModelPaths = []
298
- if (taskModelPaths?.length) {
299
- if (!hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
300
- defaultModelPaths = await getDefaultModelOptions(ws, filter)
301
- defaultModelPaths = defaultModelPaths.filter(p => p.match(allowList))
302
- }
303
- } else {
304
- defaultModelPaths = await getDefaultModelOptions(ws, filter)
305
- defaultModelPaths.push(task.src)
306
- if (hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
307
- // all default models except the built-in models
308
- defaultModelPaths = defaultModelPaths.filter(p => !p.match(allowList))
309
- }
310
- }
311
- if (defaultModelPaths.length) {
312
- task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(defaultModelPaths) : defaultModelPaths)]
313
- }
314
- }
315
-
316
283
  /**
317
284
  * Determines the module folder from the given list that may represent files or folders w or w/o .cds file extension.
318
285
  * @param {Array} filesOrFolders
@@ -92,10 +92,10 @@ class ResourcesTarBuilder {
92
92
 
93
93
  return hanaTasks.find(hanaTask => {
94
94
  // if called from internalBuildTaskProvider, the src folder for the tenant db task might still not set
95
- // while an existing second shared db always requires src to be configured using a relative path
95
+ // while an existing second shared db always requires src to be set
96
96
  if (!hanaTask.src) return true
97
97
  const srcFolder = path.isAbsolute(hanaTask.src) ? path.relative(cds.root, hanaTask.src) : hanaTask.src
98
- return mtxTask.options?.model?.includes(srcFolder)
98
+ return mtxTask.options?.model?.includes(srcFolder) || srcFolder === cds.env.folders.db.replace('/', '')
99
99
  })
100
100
  }
101
101
  }
@@ -45,7 +45,7 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
45
45
  */
46
46
  async _buildNodeApp(sidecarEnv) {
47
47
  const destSidecar = this.task.dest
48
- const destSidecarSrc = path.join(destSidecar, cds.env.folders.srv)
48
+ const destSidecarSrc = path.join(destSidecar, sidecarEnv.folders.srv)
49
49
  const model = this._compileSidecarSync(sidecarEnv)
50
50
  await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
51
51
  await this.collectLanguageBundles(model, path.join(destSidecarSrc, getI18nDefaultFolder()))
@@ -129,8 +129,15 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
129
129
  this.pushMessage(`CDS models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure up-to-date versions of the missing npm modules are installed.`, ERROR)
130
130
  }
131
131
 
132
- // synchronous compilation
133
- return cds.load(modelFilePaths, { sync: true, ...this.options() })
132
+ // using synchronous compilation as cds.root and cds.env have been switched to mtx/sidecar
133
+ // using different messages array ensures that cds compilation doesn't fail because of
134
+ // existing compilation error messages that have been downgraded to warnings
135
+ // see cap/issues/issues/16401#issuecomment-7205397
136
+ const messages = []
137
+ const model = cds.load(modelFilePaths, { sync: true, messages })
138
+ messages.forEach(msg => this.messages.push(msg))
139
+
140
+ return model
134
141
  } finally {
135
142
  // restore project scope
136
143
  cds.root = root
package/lib/cds.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /** @type { import('@sap/cds/lib') } */
2
2
  const cds = _local ('@sap/cds')
3
3
 
4
+ /** @type { import('@sap/cds/lib') & {_local: (id: string) => any} } */
4
5
  module.exports = Object.defineProperties (cds, {
5
6
  // for internal use in cds-dk only
6
7
  _log: { value: _local ('../bin/util/log'), configurable: true },
@@ -2,12 +2,13 @@ const cds = require('../cds')
2
2
 
3
3
  module.exports = function to_mermaid (model, options={}) {
4
4
  options = Object.assign({
5
- assocnames: false, // include association names
6
- elements : false, // include all elements { falsy | 'all'|true | 'keys' }
7
- min : false, // minify the model, i.e. remove unused definitions, available in CLI
8
- namespaces: true, // group entities by namespace/service
9
- queries : false, // include relations for queries
10
- service : undefined // filter service by name, available in CLI
5
+ assocnames: false, // include association names
6
+ elements : false, // include all elements { falsy | 'all'|true | 'keys' }
7
+ min : false, // minify the model, i.e. remove unused definitions, available in CLI
8
+ namespaces: true, // group entities by namespace/service
9
+ queries : false, // include relations for queries
10
+ service : undefined, // filter service by name, available in CLI
11
+ direction : undefined // direction of the graph
11
12
  }, cds.env.mermaid ?? {}, options)
12
13
 
13
14
  const elemOpts = options.elements === 'none' ? false : options.elements
@@ -27,6 +28,7 @@ module.exports = function to_mermaid (model, options={}) {
27
28
  const entityFilter = e => !e.name.startsWith('cds.') && !e.name.endsWith('.texts')
28
29
 
29
30
  let diag = ''
31
+ const direction = options.direction ? ` direction ${options.direction}\n` : ''
30
32
 
31
33
  // if requested, filter by one service
32
34
  model.all('service')
@@ -56,8 +58,10 @@ module.exports = function to_mermaid (model, options={}) {
56
58
  })
57
59
 
58
60
  // create namespace groups and classes
59
- Object.keys(namespaces).forEach(ns => {
60
- if (!diag.length) diag = `classDiagram\n`
61
+ Object.keys(namespaces)
62
+ .filter (ns => namespaces[ns].length) // empty namespaces can cause rendering errors, so skip them
63
+ .forEach(ns => {
64
+ if (!diag.length) diag = `classDiagram\n${direction}`
61
65
  diag += ` namespace ${ns.replaceAll('.','_')} {\n`
62
66
  namespaces[ns].forEach (e => {
63
67
  const simpleName = e.name.slice(e.name.lastIndexOf('.')+1)
@@ -65,7 +69,7 @@ module.exports = function to_mermaid (model, options={}) {
65
69
  diag += `["${simpleName}"]` // simple name as label
66
70
  if (elemOpts) {
67
71
  diag += ` {\n`
68
- e.elements.forEach (el => {
72
+ e.elements?.forEach (el => {
69
73
  if ((elemOpts === 'all' || elemOpts === true) || (el.key && elemOpts == 'keys')) {
70
74
  diag += ` +${el._type?.replace('cds.','')} ${el.name}`
71
75
  diag += `\n`
@@ -85,8 +89,8 @@ module.exports = function to_mermaid (model, options={}) {
85
89
  ? Array.from(seenEntities).map(name => model.definitions[name])
86
90
  : model.all('entity').filter (entityFilter) // if no grouping and no service filter is on
87
91
  entities.forEach(e => {
88
- if (!diag.length) diag = `classDiagram\n`
89
- e.elements.forEach (el => {
92
+ if (!diag.length) diag = `classDiagram\n${direction}`
93
+ e.elements?.forEach (el => {
90
94
  if (el instanceof cds.Association) {
91
95
  if (el.target.endsWith('.texts')) return
92
96
  const type = el.type === 'cds.Composition' ? '*-->' : '-->'
@@ -100,9 +104,20 @@ module.exports = function to_mermaid (model, options={}) {
100
104
  })
101
105
  // add relations for queries
102
106
  if (e.query && options.queries) {
103
- diag += ` \`${e.name}\` ..> \`${e.query.target.name}\`\n`
107
+ resolveTargets(e.query).forEach(t => {
108
+ diag += ` \`${e.name}\` ..> \`${t}\`\n`
109
+ })
104
110
  }
105
111
  })
106
112
 
107
113
  return diag.trim()
108
114
  }
115
+
116
+ function resolveTargets (query) {
117
+ if (query.target) return [ query.target.name ] // simple query, like projection on
118
+
119
+ // SELECT: { from: { args: [{ ref: ['t1'] }, { ref: ['t2'] }], join: ..., on: ... } }
120
+ const targets = query.SELECT?.from?.args?.filter(arg => arg.ref?.length).map (arg => arg.ref[0])
121
+
122
+ return targets
123
+ }
@@ -1,4 +1,5 @@
1
- const { compile } = require('@cap-js/openapi')
1
+ const cds = require('../cds')
2
+ const { compile } = cds._local('@cap-js/openapi') // load a local cap-js/openapi (fallback to our own)
2
3
 
3
4
  function cds_compile_to_openapi (csn, options) {
4
5
  return compile(csn, options)
@@ -6,7 +6,6 @@ const cds = require('../../../lib/cds');
6
6
  const { exists, read } = cds.utils;
7
7
 
8
8
  const { build } = require('../../build');
9
- const buildConstants = require('../../build/constants');
10
9
 
11
10
  const hdi = require('./hdiDeployUtil');
12
11
  const cfUtil = require('../../util/cf');
@@ -16,6 +15,7 @@ const mtaUtil = require('./mtaUtil');
16
15
  const { bold, info, warn } = require('../../util/term');
17
16
 
18
17
  const { bind, storeBindings } = require('../../bind');
18
+ const { BUILD_TASK_HANA, OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, OUTPUT_MODE_RESULT } = require('../../build/constants');
19
19
 
20
20
  const DEBUG = cds.debug('cli');
21
21
 
@@ -73,13 +73,13 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
73
73
  vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
74
74
  }
75
75
 
76
- for (const { task } of buildResults) {
76
+ for (const { task, result } of buildResults) {
77
77
  if (dry) {
78
- const model = await cds.load(task.src);
79
- const data = cds.compile.to.hdbtable(model);
80
- for (const [src, { file }] of data) {
81
- console.log(`-- ${file}`);
82
- console.log(src);
78
+ const sortedResult = Array.from(result.hana).sort();
79
+ for (const file of sortedResult) {
80
+ const srcPath = path.join(result.dest, file);
81
+ console.log(`-- ${path.relative(cds.root, srcPath)}`);
82
+ console.log(await read(srcPath));
83
83
  console.log();
84
84
  }
85
85
  } else {
@@ -221,7 +221,9 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
221
221
 
222
222
  async _build() {
223
223
  const buildTaskOptions = {
224
- for: buildConstants.BUILD_TASK_HANA
224
+ for: BUILD_TASK_HANA,
225
+ src: cds.env.folders.db,
226
+ [OUTPUT_MODE]: OUTPUT_MODE_FILESYSTEM | OUTPUT_MODE_RESULT
225
227
  }
226
228
 
227
229
  console.log(`Running build`);
@@ -37,6 +37,7 @@ module.exports = new class HdiDeployUtil {
37
37
  else throw e
38
38
  }
39
39
  if (options.use_hdb !== false) options.use_hdb = true
40
+ if (options.use_hdb === false) options.use_hdb = undefined
40
41
  deployerEnv.HDI_DEPLOY_OPTIONS = JSON.stringify(options)
41
42
 
42
43
  const asyncDeploy = util.promisify(deploy);
@@ -59,7 +60,15 @@ module.exports = new class HdiDeployUtil {
59
60
  Add it either to ${bold('devDependencies')} using 'npm install -D ${HDI_DEPLOY_LIB}' or install it globally using 'npm install -g ${HDI_DEPLOY_LIB}'.`);
60
61
  }
61
62
 
62
- console.log(`Using HDI deployer from ${libPath}`)
63
+ console.log(`HDI deployer path: ${libPath}`)
64
+
65
+ const libRoot = path.dirname(libPath);
66
+ try {
67
+ const libPackageJson = require(path.join(libRoot, 'package.json'));
68
+ console.log(`HDI deployer version: ${libPackageJson.version}`);
69
+ } catch {
70
+ // ignore
71
+ }
63
72
 
64
73
  // let any error go through and abort deploy
65
74
  return require(libPath);
@@ -67,7 +67,7 @@ async function getVersion(fileInputStream){
67
67
  oDataVersion = 'V4';
68
68
  }
69
69
  else {
70
- throw new Error(messages.INVALID_EDMX_METADATA_VERSION);
70
+ throw new Error(messages.INVALID_ODATA_VERSION_OR_UNSUPPORTED_ENCODING);
71
71
  }
72
72
 
73
73
  return oDataVersion;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const messages = {
4
4
  INVALID_EDMX_METADATA: 'The OData model is not valid. Specify the correct OData model.',
5
- INVALID_EDMX_METADATA_VERSION: 'The OData version is not valid. Specify the correct OData version.',
5
+ INVALID_ODATA_VERSION_OR_UNSUPPORTED_ENCODING: 'Either the OData version is not valid, or the encoding of the file is not utf-8. Please specify the supported OData version and file encoding. OData version is expected one of [1.0, 2.0, 4.0, 4.01] and the input file encoding is expected UTF-8.',
6
6
  INVALID_OUTPUT_FILE: 'Please provide an output file with one of these [cds, csn, json] file extension.',
7
7
  SPECIFY_INPUT_FILE: 'Specify an input file that exists.',
8
8
  SPECIFY_OUTPUT_FILE: 'Specify an output file or correct the output file path provided.',
@@ -45,4 +45,4 @@ function getMessages() {
45
45
 
46
46
  module.exports = {
47
47
  getMessages
48
- };
48
+ };