@sap/cds-dk 7.7.1 → 7.8.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 (57) hide show
  1. package/bin/add.js +42 -17
  2. package/bin/bind.js +21 -12
  3. package/bin/build.js +14 -2
  4. package/bin/deploy/to-sqlite.js +1 -1
  5. package/bin/help.js +4 -1
  6. package/bin/import.js +5 -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 +19 -5
  12. package/lib/build/provider/java/index.js +4 -4
  13. package/lib/build/provider/mtx/resourcesTarBuilder.js +16 -4
  14. package/lib/build/provider/mtx-extension/extensionCompilation.js +13 -2
  15. package/lib/build/provider/mtx-extension/index.js +6 -2
  16. package/lib/build/provider/nodejs/index.js +4 -4
  17. package/lib/build/util.js +1 -12
  18. package/lib/compile/openapi/index.js +2 -1
  19. package/lib/deploy/to-hana/hana.js +19 -17
  20. package/lib/import/common.js +1 -1
  21. package/lib/import/importapi.js +1 -1
  22. package/lib/import/odata/v4parser.js +18 -11
  23. package/lib/index.js +2 -2
  24. package/lib/init/constants.js +2 -1
  25. package/lib/init/index.js +9 -23
  26. package/lib/init/mvn.js +1 -5
  27. package/lib/init/plugin.js +8 -0
  28. package/lib/init/registries/mta.js +6 -0
  29. package/lib/init/template/data/as-csv.js +60 -0
  30. package/lib/init/template/data/as-json.js +441 -0
  31. package/lib/init/template/data/index.js +115 -86
  32. package/lib/init/template/helm/files/content-deployment/Chart.yaml +1 -1
  33. package/lib/init/template/helm/files/content-deployment/templates/job.yaml +7 -0
  34. package/lib/init/template/helm/files/content-deployment/values.schema.json +17 -0
  35. package/lib/init/template/helm/files/content-deployment/values.yaml +6 -1
  36. package/lib/init/template/helm/files/service-instance/Chart.yaml +1 -1
  37. package/lib/init/template/helm/files/service-instance/templates/serviceinstance.yaml +4 -0
  38. package/lib/init/template/helm/files/service-instance/values.schema.json +17 -1
  39. package/lib/init/template/helm/files/service-instance/values.yaml +4 -1
  40. package/lib/init/template/helm/files/values.yaml.hbs +2 -2
  41. package/lib/init/template/helm/files/web-application/Chart.yaml +1 -1
  42. package/lib/init/template/helm/files/web-application/templates/api-rule.yaml +4 -0
  43. package/lib/init/template/helm/files/web-application/templates/network-policy.yaml +15 -0
  44. package/lib/init/template/helm/files/web-application/values.schema.json +41 -0
  45. package/lib/init/template/helm/files/web-application/values.yaml +19 -1
  46. package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +2 -2
  47. package/lib/init/template/helm-unified-runtime/index.js +9 -6
  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/index.js +40 -0
  52. package/lib/init/template/lint/index.js +4 -4
  53. package/lib/init/template/mta/files/mta.yaml.hbs +4 -0
  54. package/lib/init/template/multitenancy/files/values.yaml.hbs +2 -2
  55. package/lib/util/cf.js +6 -2
  56. package/npm-shrinkwrap.json +300 -276
  57. package/package.json +1 -1
package/bin/add.js CHANGED
@@ -1,10 +1,31 @@
1
- const cds = require('../lib/cds')
2
- const term = require('../lib/util/term')
1
+ const cds = require('../lib/cds'), { fs } = cds.utils
2
+ const DEBUG = cds.debug('add')
3
+ const { join } = require('path')
4
+
5
+ const fromDk = fs.readdirSync(join(__dirname, '../lib/init/template'))
6
+ const fromPlugins = require('../lib/init/add').registered
7
+ const conf = {}
8
+ for (const plugin of [...fromDk, ...Object.keys(Object.fromEntries(fromPlugins))]) {
9
+ const Plugin = fromPlugins.get(plugin) ?? require(join(__dirname, '../lib/init/template', plugin))
10
+ for (const [key, { type, help, short }] of Object.entries(new Plugin().options())) {
11
+ conf[plugin] ??= { options: [], flags: [], help: [] }
12
+ if (type === 'boolean') conf[plugin].flags.push({ key: '--'+key, short: short ? '-'+short : undefined, help })
13
+ else if (type === 'number') conf[plugin].options.push({ key: '--'+key, short: short ? '-'+short : undefined, help })
14
+ else if (type === 'string') conf[plugin].options.push({ key: '--'+key, short: short ? '-'+short : undefined, help })
15
+ }
16
+ }
17
+
18
+ const byShortcut = (a, b) => (b.short ? 1 : 0) - (a.short ? 1 : 0)
19
+
20
+ const options = ['--for', ...Object.values(conf).flatMap(p => p.options).sort(byShortcut).map(o => o.key)]
21
+ const shortcuts = ['-4', ...Object.values(conf).flatMap(p => p.options).sort(byShortcut).map(o => o.short)]
22
+ const flags = ['--force', ...Object.values(conf).flatMap(p => p.flags).map(o => o.key)]
23
+ DEBUG?.('cli config ', { options, shortcuts, flags })
3
24
 
4
25
  module.exports = Object.assign(add, {
5
- options: ['--for', '--out', '--data:for'],
6
- shortcuts: ['-4', '-o'],
7
- flags: ['--force', '--verbose', '--internal-unified-runtime-charts'],
26
+ options,
27
+ shortcuts,
28
+ flags,
8
29
  help: `
9
30
  # SYNOPSIS
10
31
 
@@ -12,7 +33,7 @@ module.exports = Object.assign(add, {
12
33
 
13
34
  Add one or more features to an existing project - grow as you go.
14
35
 
15
- The following features can be added:
36
+ Pick any of these:
16
37
 
17
38
  *hana* - add support for SAP HANA
18
39
 
@@ -65,23 +86,24 @@ module.exports = Object.assign(add, {
65
86
 
66
87
  # OPTIONS
67
88
 
68
- *--for* <profile>
89
+ *--for | -4* <profile>
69
90
 
70
91
  Write configuration data for the given profile.
71
92
 
72
- *--data:for* <entity name or prefix>
93
+ *--force*
73
94
 
74
- Add data content for entities matching the given pattern. If it contains
75
- meta characters like '^' or '*', it is treated like a regular expression,
76
- otherwise as a prefix.
95
+ Overwrite all files in case the target files already exist.
77
96
 
78
- *-o* | *--out* <target folder>
79
97
 
80
- Specify output target folder (applicable to ${term.bold('data')} feature only).
98
+ # FEATURE OPTIONS
81
99
 
82
- *--force*
100
+ ${Object.entries(conf).sort(([a], [b]) => a.localeCompare(b)).flatMap(([pluginName, { options, flags }]) => {
101
+ const text4 = arr => arr.map(({ key, help, short }) => help && ` ${key}${short ? ' | '+short : ''}\n\n ${help}`).join('\n\n')
102
+ const optionsText = text4(options), flagsText = text4(flags)
103
+ if (!optionsText && !flagsText) return []
104
+ return ` *cds add ${pluginName}*\n\n${optionsText}${flagsText}`
105
+ }).join('\n\n\n')}
83
106
 
84
- Overwrite all files in case the target files already exist.
85
107
 
86
108
  # EXAMPLES
87
109
 
@@ -98,9 +120,12 @@ module.exports = Object.assign(add, {
98
120
 
99
121
  `})
100
122
 
101
- async function add(args) {
123
+ async function add(features) {
102
124
  await cds.plugins
103
125
  const CDSGenerator = require('../lib/init')
104
126
  const generator = new CDSGenerator()
105
- await generator.addCmd(args[0])
127
+
128
+ const args = features.map(f => f.split(/[,\s+]/)).flat()
129
+
130
+ await generator.add(args)
106
131
  }
package/bin/bind.js CHANGED
@@ -1,5 +1,7 @@
1
+ const { warn } = require('../lib/util/term')
2
+
1
3
  module.exports = Object.assign(cds_bind, {
2
- options: ['--to', '--for', '--on', '--kind', '--output-file', '--to-app-services', '--no-create-service-key'],
4
+ options: ['--to', '--for', '--on', '--kind', '--out', '--to-app-services', '--no-create-service-key', '--output-file'],
3
5
  flags: ['--exec'],
4
6
  shortcuts: ['-2', '-4', '-n', '-k', '-o', '-a'],
5
7
  help: `
@@ -12,7 +14,7 @@ module.exports = Object.assign(cds_bind, {
12
14
  information in *.cdsrc-private.json*
13
15
  in your project directory. Credentials are not stored in the file but rather
14
16
  retrieved dynamically during *cds watch*.
15
- Use option *--output-file* to specify a different path or file.
17
+ Use option *--out* to specify a different path or file.
16
18
 
17
19
  With *--exec* you can execute arbitrary commands with your service bindings.
18
20
  The service bindings are provided
@@ -36,11 +38,11 @@ module.exports = Object.assign(cds_bind, {
36
38
 
37
39
  Kind of the service.
38
40
 
39
- *-o | --output-file* <path>
41
+ *-o | --out* <path>
40
42
 
41
43
  Output file for added binding information. Use this option if binding configuration should
42
- be added to *package.json* or *.cdsrc.json*. Defaults to *.cdsrc-private.json*.
43
- If *path* is a directory, the *.cdsrc.json* file in that directory is used.
44
+ be added to *package.json* or *.cdsrc.json*. If *path* is a directory, the *.cdsrc.json*
45
+ file in that directory is used. Defaults to *.cdsrc-private.json*.
44
46
 
45
47
  *-a | --to-app-services <app>* (Beta)
46
48
 
@@ -56,8 +58,7 @@ module.exports = Object.assign(cds_bind, {
56
58
  cds bind --to bookshop-db,bookshop-auth
57
59
  cds bind auth --to bookshop-auth:bookshop-auth-key --kind xsuaa --for my-profile
58
60
 
59
- cds bind --to bookshop-db --output-file .
60
- cds bind --to bookshop-db --output-file package.json
61
+ cds bind --to bookshop-db --out .cdsrc.json
61
62
 
62
63
  cds bind --to bookshop-db --on k8s
63
64
  cds bind --to bookshop-db --for my-profile
@@ -74,7 +75,9 @@ const { bind, parseProfile } = require('../lib/bind');
74
75
 
75
76
  async function cds_bind(args, options) {
76
77
 
77
- if (options.exec) {
78
+ const { on, 'to-app-services': app, to, out, 'output-file': outputFile, exec } = options
79
+
80
+ if (exec) {
78
81
  process.env.CDS_ENV = parseProfile(process.env.CDS_ENV);
79
82
  if (options.for) {
80
83
  console.warn('Parameter --for is not supported in combination with --exec. Its value will be ignored.');
@@ -87,7 +90,10 @@ async function cds_bind(args, options) {
87
90
  throw 'Option --profile is no longer supported. Use --for instead to specify the profile to use when storing connection settings.';
88
91
  }
89
92
 
90
- const { on, 'to-app-services': app, to } = options
93
+ if (outputFile) {
94
+ console.warn(warn('Option --output-file is deprecated. Please use --out instead.'))
95
+ options.out ??= outputFile
96
+ }
91
97
 
92
98
  if (on === 'k8s' && app) {
93
99
  throw 'Option --to-app-services is only supported for Cloud Foundry.';
@@ -97,12 +103,15 @@ async function cds_bind(args, options) {
97
103
  throw `Use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`;
98
104
  }
99
105
 
100
- const outputFile = options['output-file']
106
+ if (app === true) {
107
+ throw `Option '--to-app-services' requires an app name, for example 'cds bind --to-app-services bookshop-srv'.`
108
+ }
109
+
101
110
  if (app) {
102
111
  const services = await require('../lib/util/cf').getServices(app)
103
112
  // Later: optimize logging for parallelization, services.forEach(service => bind({ ...options, to: service.name, outputFile }))
104
113
  for (const service of services.filter(s => Object.keys(s.credentials).length > 0)) {
105
- await bind({ ...options, to: service.name, outputFile })
114
+ await bind({ ...options, to: service.name, out })
106
115
  }
107
116
  } else {
108
117
  if (args.length > 1) {
@@ -120,7 +129,7 @@ async function cds_bind(args, options) {
120
129
  throw `The option '--kind' cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the '--kind' option.`;
121
130
  }
122
131
 
123
- await bind({ ...options, outputFile });
132
+ await bind({ ...options, out });
124
133
  }
125
134
  }
126
135
 
package/bin/build.js CHANGED
@@ -1,7 +1,7 @@
1
1
  module.exports = Object.assign(build, {
2
2
  options: ['--project', '--src', '--dest', '--for', '--use', '--opts', '--log-level', '--options-model'],
3
3
  shortcuts: ['-in', '-s', '-o', '-4'],
4
- flags: ['--clean', '--no-clean', '--ws'], // REVISIT: --clean is deprecated, backward compatibility
4
+ flags: ['--clean', '--no-clean', '--ws', '--ws-pack'], // REVISIT: --clean is deprecated, backward compatibility
5
5
  help: `
6
6
  # SYNOPSIS
7
7
 
@@ -73,13 +73,25 @@ module.exports = Object.assign(build, {
73
73
  operation. Clients need to clean the output folder upfront to ensure
74
74
  consistent build results.
75
75
 
76
- *--ws* (beta)
76
+ *--ws-pack* (beta)
77
77
  Enable tarball based packaging of npm workspace dependencies - typically defined for
78
78
  Node.js mono or multi-repo setups.
79
79
  For each workspace dependency of your project using '*' as version identifier,
80
80
  a tarball will be created in folder 'gen/srv'. Dependencies in package.json
81
81
  will be adapted to point to the correct tarball file URL.
82
82
  Contents is packaged based on the rules of the 'npm pack' command.
83
+
84
+ *--ws* (beta)
85
+ Npm workspaces are typically used in mono or multi-repo setups whereas each workspace
86
+ folder represents a microservice CAP project with subfolders _db_, _srv_, _app_.
87
+ With the *ws* option the model scope used in executed build tasks is calculated based on
88
+ all workspaces. This also applies to the feature toggles. Feature toggles defined cross workspace
89
+ are merged. The output is created in the _mtx/sidecar_ for multitenant applications
90
+ or in the 'srv' folder for node.js apps if no sidecar exists.
91
+ HANA native content distributed across the workspaces is copied into the top-level 'db'
92
+ folder of the project. This 'db' folder represents the single database shared by all workspaces.
93
+ *Note:* Make sure to use consistent package versions in your workspace dependencies in order
94
+ to avoid compilation errors caused by duplicate model definitions.
83
95
 
84
96
  # EXAMPLES
85
97
 
@@ -4,7 +4,7 @@ exports.deploy = function deploy_to_sqlite (_model, _db, options) {
4
4
 
5
5
  const cds = require('../../lib/cds')
6
6
  const conf = cds.env.requires.db ?? {}
7
- const fts = cds.env.features.folders
7
+ const fts = cds.env.requires.toggles && cds.env.features.folders
8
8
  const model = _model || conf.model || cds.resolve(!fts ? '*' : ['*',fts], false)
9
9
  let file = 'db'
10
10
  if (options.to) {
package/bin/help.js CHANGED
@@ -1,3 +1,5 @@
1
+ const cds = require('../lib/cds')
2
+
1
3
  /* eslint-disable no-console */
2
4
  module.exports = exports = Object.assign (help, {formatHelp, help:`
3
5
  # USAGE
@@ -38,7 +40,8 @@ module.exports = exports = Object.assign (help, {formatHelp, help:`
38
40
 
39
41
  `})
40
42
 
41
- function help (topic) {
43
+ async function help (topic) {
44
+ await cds.plugins
42
45
  if (Array.isArray(topic)) topic = topic[0]
43
46
  const task = !topic ? help : this.load?.(this.Shortcuts[topic] || topic)
44
47
  if (task?.help) console.log (formatHelp (task.help))
package/bin/import.js CHANGED
@@ -1,7 +1,7 @@
1
1
  module.exports = Object.assign(_import, {
2
2
  _name: 'import',
3
3
  options: ['--out', '--as', '--include-namespaces', '--from', '--config'],
4
- flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace'],
4
+ flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace', '--beta'],
5
5
  shortcuts: ['-o', '-as', '-ns', undefined, '-c', '-', '-f'],
6
6
  help: `
7
7
  # SYNOPSIS
@@ -47,6 +47,10 @@ module.exports = Object.assign(_import, {
47
47
  *-c | --config* <JSON Object>
48
48
  Adds the provided JSON Object as custom configuration data to the package.json.
49
49
 
50
+ *--beta*
51
+ Enables beta mode in cds import.
52
+ - Populates default value for optional action and function parameters in the CSN or CDS.
53
+
50
54
  *Below options are valid for OData only*
51
55
 
52
56
  *--keep-namespace*
package/bin/version.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-console */
2
2
  const { join, resolve } = require ('path')
3
- const { execSync } = require('child_process')
3
+ const { execSync, spawnSync } = require('child_process')
4
4
  module.exports = Object.assign(list_versions, {
5
5
  flags: [ '--info', '--markdown', '--all', '--npm-list', '--npm-tree', '--no-colors'],
6
6
  shortcuts: [ '-i', '-m','-a', '-ls', '-ll' ],
@@ -35,38 +35,57 @@ module.exports = Object.assign(list_versions, {
35
35
  const MISSING = '-- missing --'
36
36
 
37
37
  function list_versions(args, options) {
38
+ const colorsOff = options['no-colors'] ?? !process.stdout.isTTY
39
+
38
40
  if (options['npm-list'] || options['npm-tree']) {
39
41
  let [pattern] = args, re = pattern ? RegExp(pattern) : /@sap\/cd[rs]|@sap\/eslint-plugin-cds/
40
42
  let cmd = 'npm ls --depth ' + (options['npm-tree'] ? 11 : 0)
41
- console.log (cmd,'| grep', pattern)
43
+ console.log (cmd,'| grep', re.source)
42
44
  return require('child_process').exec(cmd, (e,stdout)=>{
43
45
  // if (e) console.error(e)
44
- const replacement = (options['no-colors'] ? '$1 $2$3$4' : '\x1b[91m$1 \x1b[32m$2\x1b[0m\x1b[2m$3\x1b[32m$4\x1b[0m');
46
+ const replacement = (colorsOff ? '$1 $2$3$4' : '\x1b[91m$1 \x1b[32m$2\x1b[0m\x1b[2m$3\x1b[32m$4\x1b[0m');
45
47
  for (let line of stdout.split(/\n/)) if (line.match(re)) console.log(
46
48
  line.replace(/(@sap[^@]*)@([\S]+)( -> [\S]+)?(deduped)?/,replacement)
47
49
  )
48
50
  })
49
51
  }
50
- const versions = info ()
51
- if (options.markdown) return _markdown (versions)
52
- if (options.info) return _markdown (versions)
53
- const mark = options['no-colors'] ? s => s : require('../lib/util/term').info
54
- for (let each of Object.keys(versions).sort()) console.log(`${mark(each)}: ${versions[each]}`)
52
+ const versions = info (options)
53
+
54
+ process.stdout.isTTY && console.log()
55
+ if (options.markdown || options.info) {
56
+ _markdown (versions)
57
+ } else {
58
+ const mark = colorsOff ? s => s : require('../lib/util/term').info
59
+ for (let each of Object.keys(versions)) console.log(`${mark(each)}: ${versions[each]}`)
60
+ }
61
+ process.stdout.isTTY && console.log()
55
62
  }
56
63
 
57
- function info() {
58
- const cwd = process.cwd()
64
+ function info({ root:cwd=process.cwd() }) {
65
+ const env = require('../lib').env.for('cds', cwd)
66
+ const isJava = env['project-nature'] === 'java'
67
+ if (isJava && process.stderr.isTTY) {
68
+ console.error('\x1b[2m'+'This takes a while. Collecting Java versions...', '\x1b[0m')
69
+ }
70
+
59
71
  const npmRoot = execSync('npm root -g').toString().trim()
60
- return {
72
+ let versions = {
61
73
  ..._versions_from(_pkg_path('@sap/cds-dk', __dirname), '@sap/cds-dk (global)'), // ... linked in cap/dev setup
62
- ..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // ... or installed globally
63
- ..._versions_from(_pkg_path('@sap/cds-dk', cwd)), // local dk, important for Java projects
74
+ ..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // ... or installed globally
75
+ ..._versions_from(_pkg_path('@sap/cds-dk', cwd)), // local dk, important for Java projects
64
76
  ..._versions_from(_pkg_path('@sap/cds', __dirname)), // @sap/cds from here
65
- ..._versions_from(cwd), // ... or from local app dependencies
77
+ ..._versions_from(cwd), // ... or from local app dependencies
66
78
  ..._versions_from(_pkg_path('@sap/cds-compiler', cwd, __dirname)), // compiler is no direct dependency, so list it explicitly
67
79
  'Node.js': process.version,
68
- 'home': require('../lib/cds').home // effective (local) cds
80
+ 'home': require('../lib/cds').home, // effective (local) cds
69
81
  }
82
+ // sort Node entries
83
+ versions = Object.keys(versions).sort().reduce((all, key) => { all[key] = versions[key]; return all }, {})
84
+
85
+ if (isJava) {
86
+ Object.assign(versions, capJavaVersion(cwd))
87
+ }
88
+ return versions
70
89
  }
71
90
 
72
91
  function _versions_from(pkg_path, pkg_label) {
@@ -95,7 +114,6 @@ function _markdown (versions) {
95
114
  const repo = pkg.repository.url || pkg.repository || 'https://github.com/<your/repo>'
96
115
  const valLength = Math.max(repo.length, versions.home.length)
97
116
 
98
- console.log ()
99
117
  console.log ('|', $(pkg.name, repo), '|')
100
118
  console.log ('|', $('----------------------', '-'.repeat(valLength)), '|')
101
119
 
@@ -104,12 +122,6 @@ function _markdown (versions) {
104
122
  .sort()
105
123
  .forEach(id => console.log ('|', $(id, versions[id]), '|'))
106
124
 
107
- // TODO get CAP Java version from mvn
108
- // if (require('../lib').env['project-nature'] === 'java') {
109
- // console.log ('|', $('CAP Java Runtime'), '|')
110
- // }
111
- console.log()
112
-
113
125
  function $ (key, val=MISSING) {
114
126
  return ( key + ' '.repeat(22)).slice(0,22)
115
127
  +' | '+ (val + ' '.repeat(valLength)).slice(0,valLength)
@@ -124,3 +136,23 @@ function _pkg_path(id, ...paths) {
124
136
  if (e.code !== 'MODULE_NOT_FOUND') throw e
125
137
  }
126
138
  }
139
+
140
+ function capJavaVersion(cwd) {
141
+ const cmd = 'mvn com.sap.cds:cds-maven-plugin:version -B -Dcds.version.excludeCds -Dcds.version.json'
142
+
143
+ let out = ''
144
+ try {
145
+ out = spawnSync(cmd, {cwd, shell: true}).stdout.toString().trim()
146
+ } catch (e) { /* ignored */ }
147
+
148
+ const match = out.match(/===\n(.*)\n===/ms)
149
+ if (match?.[1]) {
150
+ const res = {}
151
+ Object.entries(JSON.parse(match[1])).forEach(([k,v]) => {
152
+ const entries = Object.entries(v)
153
+ res[k] = entries.map(([k,v]) => (k === 'version' || k === 'name') ? v : `${k}: ${v}`).join(', ')
154
+ })
155
+ return res
156
+ }
157
+ return { 'CAP Java SDK': MISSING }
158
+ }
@@ -31,7 +31,7 @@ class UserCdsrcJsonStorageProvider {
31
31
  }
32
32
 
33
33
  async storeServices(services, options = {}) {
34
- let jsonPath = options.outputFile || CDSRC_FILENAME;
34
+ let jsonPath = options.out || CDSRC_FILENAME;
35
35
 
36
36
  if (jsonPath === CDSRC_FILENAME) {
37
37
  await gitUtil.ensureFileIsGitignored(CDSRC_FILENAME);
@@ -106,12 +106,12 @@ class BuildTaskFactory {
106
106
  const modelPaths = await getDefaultModelOptions()
107
107
  let wsModelPaths
108
108
  // calculate only once
109
- if (tasks.some(task => hasOptionValue(task.options?.[CONTENT_WS], true))) {
109
+ if (cds.cli.options?.ws || tasks.some(task => hasOptionValue(task.options?.[CONTENT_WS], true))) {
110
110
  wsModelPaths = await getDefaultModelOptions(true)
111
111
  }
112
112
  // set default model options
113
113
  tasks.forEach(task => {
114
- this._setTaskModelOptions(task, hasOptionValue(task.options?.[CONTENT_WS], true) ? wsModelPaths : modelPaths)
114
+ this._setTaskModelOptions(task, cds.cli.options?.ws || hasOptionValue(task.options?.[CONTENT_WS], true) ? wsModelPaths : modelPaths)
115
115
  if (!task.src) {
116
116
  throw new Error(`Invalid build task definition - value of property 'src' is missing in [${task.for || task.use}].`)
117
117
  }
@@ -50,7 +50,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
50
50
  await this._copyResourcesExt(src, dest, model)
51
51
  }
52
52
  // copy native hana artifacts from workspace dependencies
53
- if (hasOptionValue(this.task.options?.[CONTENT_WS], true)) {
53
+ if (cds.cli.options?.ws || hasOptionValue(this.task.options?.[CONTENT_WS], true)) {
54
54
  await this._addWorkspaceContent()
55
55
  }
56
56
 
@@ -1,18 +1,19 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const cds = require('../../cds')
4
- const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue, isClassicMtx, normalizePath } = require('../util')
4
+ const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue, 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
7
  BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION, NODEJS_MODEL_EXCLUDE_LIST,
8
8
  IGNORE_DEFAULT_MODELS, CONTENT_WS } = require("../constants")
9
+ const ResourcesTarBuilder = require('./mtx/resourcesTarBuilder')
9
10
  const DEBUG = cds.debug('cli|build')
10
11
 
11
12
  class InternalBuildTaskProvider extends BuildTaskProvider {
12
13
  constructor() {
13
14
  super()
14
15
  // ensure that classic mtx is not used in combination with cds-dk@7
15
- if (isClassicMtx()) {
16
+ if (InternalBuildTaskProvider._isClassicMtx()) {
16
17
  throw new BuildError(`@sap/cds-mtx is deprecated and not supported by @sap/cds-dk >= 7.
17
18
  Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/docs/guides/multitenancy/old-mtx-migration or install @sap/cds-dk version 6 instead.`)
18
19
  }
@@ -78,10 +79,12 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
78
79
  }
79
80
  })
80
81
  }
81
- let dbTask = tasks.find(task => task.for === BUILD_TASK_HANA)
82
+ let dbTask = ResourcesTarBuilder.getHanaTenantDbTask(tasks, mtxTask)
82
83
  if (!dbTask) {
83
84
  // db task might be missing if mtx task is enforced by 'cds build --for mtx'
84
- dbTask = cds.env.build.tasks?.find(task => task.for === BUILD_TASK_HANA)
85
+ if (cds.env.build.tasks) {
86
+ dbTask = ResourcesTarBuilder.getHanaTenantDbTask(cds.env.build.tasks, mtxTask)
87
+ }
85
88
  if (!dbTask) {
86
89
  dbTask = this._createDbTask(db, dbOptions)
87
90
  }
@@ -251,6 +254,17 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
251
254
  return !!cds.env.extends
252
255
  }
253
256
 
257
+ static _isClassicMtx() {
258
+ function _hasMtxDependency() {
259
+ try {
260
+ return !!require(path.join(cds.root, 'package.json'))?.dependencies?.['@sap/cds-mtx']
261
+ } catch (e) {
262
+ return false
263
+ }
264
+ }
265
+ return /true/i.test(process.env.OLD_MTX) || _hasMtxDependency()
266
+ }
267
+
254
268
  /**
255
269
  * Distinguishes whether the Nodejs project is a Streamlined MTX (cds >=6) or an old MTX project.
256
270
  */
@@ -294,7 +308,7 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
294
308
  }
295
309
 
296
310
  static async _setDefaultModel(task) {
297
- const ws = hasOptionValue(task.options?.[CONTENT_WS], true)
311
+ const ws = hasOptionValue(task.options?.[CONTENT_WS], true) || cds.cli.options?.ws
298
312
  let taskModelPaths = task.options?.model
299
313
  if (taskModelPaths && !Array.isArray(taskModelPaths)) {
300
314
  taskModelPaths = [taskModelPaths]
@@ -21,16 +21,16 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
21
21
  if (!model) {
22
22
  return
23
23
  }
24
- const odata = await this._compileForOdata(model, compileDest)
24
+ await this._compileForJava(model, compileDest)
25
25
 
26
26
  if (!this.hasBuildOption(CONTENT_EDMX, false)) {
27
27
  // generate edmx files containing all features
28
28
  // REVISIT: localized edmx for backward compatibility
29
29
  if (!this.hasBuildOption(CONTENT_LOCALIZED_EDMX, false)) {
30
- await this.compileToEdmx(odata, compileDest, { [FLAVOR_LOCALIZED_EDMX]: true })
30
+ await this.compileToEdmx(model, compileDest, { [FLAVOR_LOCALIZED_EDMX]: true })
31
31
  }
32
32
  // non-localized edmx
33
- await this.compileToEdmx(odata, path.join(compileDest, 'odata', cds.env.odata.version), { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) })
33
+ await this.compileToEdmx(model, path.join(compileDest, 'odata', cds.env.odata.version), { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) })
34
34
  }
35
35
 
36
36
  if (!this.hasBuildOption(CONTENT_LANGUAGE_BUNDLES, false)) {
@@ -65,7 +65,7 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
65
65
  })
66
66
  }
67
67
 
68
- async _compileForOdata(model, csnDest, compileOptions = {}) {
68
+ async _compileForJava(model, csnDest, compileOptions = {}) {
69
69
  // csn for service providers
70
70
  const m = cds.compile.for.java(model, {
71
71
  ...this._options4odata(),
@@ -41,7 +41,7 @@ class ResourcesTarBuilder {
41
41
 
42
42
  async _getResources(model) {
43
43
  // distinguish HANA and SQLITE deployment
44
- let root = await this._getHanaTenantDbDest()
44
+ let root = this._getHanaTenantDbPath()
45
45
  let resources
46
46
  if (root) {
47
47
  resources = this._getHanaResources(root)
@@ -82,9 +82,21 @@ class ResourcesTarBuilder {
82
82
  return Object.keys(await resources(model))
83
83
  }
84
84
 
85
- async _getHanaTenantDbDest() {
86
- const hanaTask = this.plugin.context.tasks.find(task => task.for === BUILD_TASK_HANA)
87
- return hanaTask?.dest
85
+ _getHanaTenantDbPath() {
86
+ return ResourcesTarBuilder.getHanaTenantDbTask(this.plugin.context.tasks, this.plugin.task)?.dest
87
+ }
88
+
89
+ static getHanaTenantDbTask(tasks, mtxTask) {
90
+ const hanaTasks = tasks.filter(task => task.for === BUILD_TASK_HANA)
91
+ if (hanaTasks.length === 1) return hanaTasks[0]
92
+
93
+ return hanaTasks.find(hanaTask => {
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
96
+ if (!hanaTask.src) return true
97
+ const srcFolder = path.isAbsolute(hanaTask.src) ? path.relative(cds.root, hanaTask.src) : hanaTask.src
98
+ return mtxTask.options?.model?.includes(srcFolder)
99
+ })
88
100
  }
89
101
  }
90
102
  module.exports = ResourcesTarBuilder
@@ -1,6 +1,6 @@
1
1
  const path = require('path')
2
2
  const cds = require('../../../cds')
3
- const { WARNING } = require('../internalBuildPlugin')
3
+ const { SEVERITY_WARNING, SEVERITY_INFO } = require('../../constants')
4
4
 
5
5
  class ExtensionCompilation {
6
6
  constructor(plugin) {
@@ -38,7 +38,7 @@ class ExtensionCompilation {
38
38
  const pkgNames = [...[...duplicates].reduce((acc, dir) => acc.add(getPkgName(dir)), new Set())]
39
39
  const modelPaths = this.plugin.task.options.model.filter(m => !pkgNames.some(pkg => m === pkg || m.startsWith(pkg + '/')))
40
40
  this.plugin.task.options.model = modelPaths
41
- this.plugin.pushMessage(`Duplicate CDS model definitions found. Using CDS artifacts from base model instead and recompiling with model options ${JSON.stringify(modelPaths)}.\nMake sure to use compatible versions for the dependencies '${pkgNames.join(', ')}'.`, WARNING)
41
+ this.plugin.pushMessage(`Duplicate CDS model definitions found. Using CDS artifacts from base model instead and recompiling with model options ${JSON.stringify(modelPaths)}.\nMake sure to use compatible versions for the dependencies '${pkgNames.join(', ')}'.`, SEVERITY_WARNING)
42
42
  // recompile without duplicates
43
43
  return this.plugin.model()
44
44
  }
@@ -46,5 +46,16 @@ class ExtensionCompilation {
46
46
  resolveExtensionFiles(model, saasPkgFolder) {
47
47
  return model['$sources'].filter(file => file.startsWith(cds.root) && !file.startsWith(saasPkgFolder))
48
48
  }
49
+
50
+ reclassifyBaseModelMessages(messages, saasPkgPath) {
51
+ const severities = cds.env.cdsc?.severities ?? {}
52
+ messages.forEach(m => {
53
+ if (m.$location?.file && m.severity === SEVERITY_WARNING) {
54
+ if (path.resolve(cds.root, m.$location.file).startsWith(saasPkgPath) && !severities[m.messageId]) {
55
+ m.severity = SEVERITY_INFO
56
+ }
57
+ }
58
+ })
59
+ }
49
60
  }
50
61
  module.exports = ExtensionCompilation
@@ -28,7 +28,7 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
28
28
  const saasPkgPath = MtxExtensionBuildPlugin._getSaasPkgPath()
29
29
  const extensionCompilation = new ExtensionCompilation(this)
30
30
 
31
- // full compile of the extension using base model - ensuring consistency
31
+ // full model compilation including base model ensures consistency
32
32
  try {
33
33
  model = await this.model()
34
34
  } catch (e) {
@@ -39,8 +39,8 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
39
39
  }
40
40
  }
41
41
 
42
+ // extension model compilation with parsed format
42
43
  if (model) {
43
- // extension CSN using parsed format
44
44
  const options = { ...this.options(), flavor: 'parsed' }
45
45
  const extensionFiles = extensionCompilation.resolveExtensionFiles(model, saasPkgPath)
46
46
  if (extensionFiles.length > 0) {
@@ -50,6 +50,10 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
50
50
  }
51
51
  }
52
52
  }
53
+
54
+ // reclassify base model warnings as info messages
55
+ extensionCompilation.reclassifyBaseModelMessages(this.messages, saasPkgPath)
56
+
53
57
  if (extensionModel) {
54
58
  // IMPORTANT NOTE: perform all model operations before the linter is called as the linter
55
59
  // will modify the model causing the creation of 'unresolved' associations
@@ -36,7 +36,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
36
36
  }
37
37
  // by default model contains all features
38
38
  const model = await this.model()
39
- if (!model && !this.context.options['ws']) {
39
+ if (!model && !this.context.options['ws-pack']) {
40
40
  return
41
41
  }
42
42
  if (model) {
@@ -55,8 +55,8 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
55
55
  if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
56
56
  const srcSrv = this.task.src === cds.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
57
57
  await this._copyNativeContent(cds.root, srcSrv, destRoot, destSrv)
58
- if (this.context.options['ws']) {
59
- await this._addWorkspaceDependencies(destRoot)
58
+ if (this.context.options['ws-pack']) {
59
+ await this._packWorkspaceDependencies(destRoot)
60
60
  }
61
61
  }
62
62
  return this._result
@@ -158,7 +158,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
158
158
  })
159
159
  }
160
160
 
161
- async _addWorkspaceDependencies(dest) {
161
+ async _packWorkspaceDependencies(dest) {
162
162
  const wsRoot = NodejsBuildPlugin._findWorkspaceRoot(cds.root)
163
163
  const appPkg = require(path.join(dest, 'package.json'))
164
164
  let workspaces