@sap/cds-dk 8.0.2 → 8.1.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.
- package/CHANGELOG.md +49 -3
- package/bin/bind.js +3 -3
- package/bin/cds.js +8 -7
- package/bin/init.js +3 -6
- package/bin/version.js +5 -3
- package/bin/watch.js +12 -4
- package/lib/build/constants.js +1 -0
- package/lib/build/provider/hana/index.js +54 -37
- package/lib/build/provider/internalBuildTaskProvider.js +2 -2
- package/lib/build/provider/mtx-sidecar/index.js +10 -3
- package/lib/build/provider/nodejs/index.js +4 -3
- package/lib/compile/to-mermaid.js +27 -12
- package/lib/deploy/to-hana/hana.js +10 -8
- package/lib/deploy/to-hana/hdiDeployUtil.js +9 -1
- package/lib/init/constants.js +13 -3
- package/lib/init/index.js +6 -4
- package/lib/init/projectReader.js +3 -4
- package/lib/init/template/audit-logging/files/package.sidecar.json +7 -0
- package/lib/init/template/audit-logging/index.js +1 -1
- package/lib/init/template/cf-manifest/index.js +4 -0
- package/lib/init/template/cov2ap/files/dependencies.package.json +5 -0
- package/lib/init/template/cov2ap/index.js +31 -0
- package/lib/init/template/data/as-json.js +25 -11
- package/lib/init/template/destination/files/mta.yaml.hbs +6 -0
- package/lib/init/template/destination/files/package.sidecar.json +7 -0
- package/lib/init/template/destination/index.js +12 -2
- package/lib/init/template/event-queue/files/dependencies.package.json +5 -0
- package/lib/init/template/event-queue/index.js +35 -0
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +6 -58
- package/lib/init/template/html5-repo/files/package.sidecar.json +7 -0
- package/lib/init/template/html5-repo/files/xs-app.json.hbs +1 -15
- package/lib/init/template/html5-repo/index.js +13 -10
- package/lib/init/template/http/index.js +37 -28
- package/lib/init/template/malware-scanner/index.js +1 -1
- package/lib/init/template/mta/files/package.json +1 -1
- package/lib/init/template/redis/files/mta.yaml.hbs +25 -0
- package/lib/init/template/redis/files/package.json.hbs +5 -0
- package/lib/init/template/redis/index.js +44 -0
- package/lib/init/template/redis-messaging/files/package.json.hbs +0 -3
- package/lib/init/template/redis-messaging/index.js +4 -18
- package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +0 -1
- package/lib/init/template/sample/files/app/browse/webapp/manifest.json +0 -1
- package/lib/init/template/sample/files/nodejs/admin-service.js +13 -8
- package/lib/init/template/sample/files/nodejs/cat-service.js +27 -15
- package/lib/init/template/typescript/files/package.json +3 -3
- package/lib/init/template/websocket/files/dependencies.package.json +5 -0
- package/lib/init/template/websocket/index.js +35 -0
- package/lib/init/template/workzone/index.js +1 -0
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +59 -0
- package/lib/init/template/workzone-standard/index.js +24 -8
- package/npm-shrinkwrap.json +22 -19
- package/package.json +1 -1
- package/lib/init/template/redis-messaging/files/mta.yaml.hbs +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,50 @@ 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.0 - 2024-07-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `cds compile --to mermaid` can be configured with a layout direction like `LR`.
|
|
15
|
+
- `cds deploy --to hana` now shows the `hdi-deploy` version when running.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Better generation of http files with `cds add http`, especially for draft requests.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- `cds add` recognizes `hasCfManifest` as inactive unless a `manifest.yml` file exists.
|
|
24
|
+
- `cds add` help and suggestions are only shown when the `help` method is implemented.
|
|
25
|
+
- `cds add sample` does not include the deprecated `synchronizationMode` property in `manifest.json` files any more.
|
|
26
|
+
- `cds compile --to mermaid` no longer produces empty `namespace` blocks that would lead to rendering errors.
|
|
27
|
+
- `cds compile --to mermaid` no longer fails for complex queries like joins.
|
|
28
|
+
- `cds build --for mtx-sidecar` now correctly supports different `srv` folder name.
|
|
29
|
+
- `cds add audit-log` in combination with `cds add multitenancy` correctly adds the audit-log dependency to the MTX sidecar.
|
|
30
|
+
- `cds build` adds the correct hdbtabledata file paths to the hana result set.
|
|
31
|
+
- `cds add` plugins can now correctly interpret `cds.cli` properties.
|
|
32
|
+
- `cds deploy --to hana --dry` now shows correct files with their content.
|
|
33
|
+
- `cds version` now reports the correct version of `@sap/cds` if this one is installed locally along with a local `@sap/cds-dk` installation.
|
|
34
|
+
- Shell completion now works correctly for `cds init --add`.
|
|
35
|
+
- `cds build --for mtx-sidecar` no longer fails if the severity of a compilation error has been downgraded from `Error` to `Warning`.
|
|
36
|
+
|
|
37
|
+
## Version 8.0.3 - 2024-07-20
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- `cds build --ws` no longer creates migration tables in shared db if already existing in a workspace.
|
|
42
|
+
- `cds add destination` adds the destination service as an MTX SaaS dependency.
|
|
43
|
+
- `cds add html5-repo` adds the HTML5 repo runtime as an MTX SaaS dependency.
|
|
44
|
+
- `cds add html5-repo` does not add a destination service any more if not required by other services.
|
|
45
|
+
- `cds add workzone` runs `cds add destination` as a dependent facet.
|
|
46
|
+
- `cds add` plugins don't fail if custom options are provided via the `options` API.
|
|
47
|
+
- `cds add data` can now correctly handle circular associations. The maximum depth of associations is increased to 5.
|
|
48
|
+
- `cds add redis` is separated from `cds add redis-messaging` to allow for separate consumption.
|
|
49
|
+
- `cds add redis` uses the `standard` service plan by default.
|
|
50
|
+
- `cds watch` no longer leads to a warning `'@sap/cds' was loaded from different installations` when local project dependencies are installed.
|
|
51
|
+
- `cds init` uses latest Maven Java archetype version 3.0.0 for creating Java projects.
|
|
52
|
+
|
|
53
|
+
|
|
10
54
|
## Version 8.0.2 - 2024-07-12
|
|
11
55
|
|
|
12
56
|
### Added
|
|
@@ -20,9 +64,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
20
64
|
- `cds init` no longer creates a `.cdsrc.json` for Node.js projects.
|
|
21
65
|
- `cds build` configuration cleanup for `data.model` and `service.model` to only support configuration of a single model folder.
|
|
22
66
|
|
|
23
|
-
### Fixed
|
|
24
|
-
|
|
25
|
-
|
|
26
67
|
## Version 8.0.1 - 2024-07-09
|
|
27
68
|
|
|
28
69
|
### Added
|
|
@@ -128,6 +169,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
128
169
|
|
|
129
170
|
- `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.
|
|
130
171
|
|
|
172
|
+
## Version 7.9.6 - 2024-07-19
|
|
173
|
+
|
|
174
|
+
### Fixed
|
|
175
|
+
|
|
176
|
+
- `cds add data` can now correctly handle circular associations. The maximum depth of associations is increased to 5.
|
|
131
177
|
|
|
132
178
|
## Version 7.9.5 - 2024-07-12
|
|
133
179
|
|
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
|
|
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
|
|
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
|
|
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,6 +55,14 @@ const cli = {
|
|
|
55
55
|
argv = argv.slice(0, endOfOptions)
|
|
56
56
|
}
|
|
57
57
|
|
|
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
|
+
}
|
|
58
66
|
const args = this.args(task, argv)
|
|
59
67
|
args[0].push(...appendArgs)
|
|
60
68
|
cds.cli = {
|
|
@@ -63,13 +71,6 @@ const cli = {
|
|
|
63
71
|
options: args[1],
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
// REVISIT: Have to do it here to allow custom flags for plugins!
|
|
67
|
-
if (cmd === 'add') {
|
|
68
|
-
await cds.plugins
|
|
69
|
-
for (const [, Plugin] of cds.add.registered) {
|
|
70
|
-
task.options.push(...Object.keys((new Plugin).options()).map(key => '--'+key))
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
74
|
if (args[1]?.['resolve-bindings']) await _resolveBindings({ silent: cmd === 'env' })
|
|
74
75
|
|
|
75
76
|
// cds.root may for some subcommands not point to the correct directory (e.g. cds w where users can pass a project directory)
|
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
|
-
|
|
75
|
-
|
|
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
|
|
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) {
|
|
@@ -74,10 +75,11 @@ function info({ root:cwd=process.cwd() }) {
|
|
|
74
75
|
..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // ... or installed globally
|
|
75
76
|
..._versions_from(_pkg_path('@sap/cds-dk', cwd)), // local dk, important for Java projects
|
|
76
77
|
..._versions_from(_pkg_path('@sap/cds', __dirname)), // @sap/cds from here
|
|
77
|
-
..._versions_from(
|
|
78
|
+
..._versions_from(_pkg_path('@sap/cds', cds.home)), // ... or local @sap/cds
|
|
79
|
+
..._versions_from(cwd), // ... more from local app dependencies
|
|
78
80
|
..._versions_from(_pkg_path('@sap/cds-compiler', cwd, __dirname)), // compiler is no direct dependency, so list it explicitly
|
|
79
81
|
'Node.js': process.version,
|
|
80
|
-
'home':
|
|
82
|
+
'home': cds.home, // effective cds home
|
|
81
83
|
}
|
|
82
84
|
// sort Node entries
|
|
83
85
|
versions = Object.keys(versions).sort().reduce((all, key) => { all[key] = versions[key]; return all }, {})
|
package/bin/watch.js
CHANGED
|
@@ -76,6 +76,7 @@ async function watch ([cwd], {
|
|
|
76
76
|
} catch (err) {
|
|
77
77
|
throw `Error: No such folder or package: '${cwd}'`
|
|
78
78
|
}
|
|
79
|
+
const _cwd = cwd || process.cwd()
|
|
79
80
|
|
|
80
81
|
if (!Object.keys(options).includes('with-mocks')) args.push ('--with-mocks')
|
|
81
82
|
if (!Object.keys(options).find(a => /^in-memory\??$/.test(a))) args.push ('--in-memory?')
|
|
@@ -91,18 +92,20 @@ async function watch ([cwd], {
|
|
|
91
92
|
})
|
|
92
93
|
if (process.env.CDS_TYPESCRIPT) {
|
|
93
94
|
// Needed by ts-node/tsc to find the tsconfig.json. Same as `--project`.
|
|
94
|
-
env.TS_NODE_PROJECT = env.TS_NODE_PROJECT ??
|
|
95
|
+
env.TS_NODE_PROJECT = env.TS_NODE_PROJECT ?? _cwd
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
//
|
|
98
|
-
|
|
98
|
+
// if sap/cds is not installed locally, run with cds-dk's deps. (sap/cds, sqlite etc)
|
|
99
|
+
if (!installed('@sap/cds', _cwd)) {
|
|
100
|
+
env.NODE_PATH = [...paths(_cwd), ...paths(__dirname)].join(delimiter)
|
|
101
|
+
}
|
|
99
102
|
|
|
100
103
|
if (typeof ext !== 'string') ext = extDefaults
|
|
101
104
|
const delay = parseInt(process.env.CDS_WATCH_DELAY) || 200
|
|
102
105
|
|
|
103
106
|
let liveReload //= ...
|
|
104
107
|
let firstTime = true
|
|
105
|
-
if (!/false|no/.test(options['livereload']) && cds.env.for('cds',
|
|
108
|
+
if (!/false|no/.test(options['livereload']) && cds.env.for('cds', _cwd).livereload !== false) {
|
|
106
109
|
liveReload = new (require ('../lib/watch/livereload-server'))
|
|
107
110
|
env.CDS_LIVERELOAD_PATH = require.resolve ('../lib/watch/livereload-connect')
|
|
108
111
|
env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'], cwd, delay)
|
|
@@ -199,3 +202,8 @@ function paths (dir) {
|
|
|
199
202
|
const parts = resolve(dir).split(sep), n = parts.length, nm = sep+'node_modules'
|
|
200
203
|
return parts.map ((_,i,a)=> a.slice(0,n-i).join(sep)+nm).filter(p => fs.existsSync(p))
|
|
201
204
|
}
|
|
205
|
+
|
|
206
|
+
function installed(id, ...paths) {
|
|
207
|
+
try { return require.resolve (id, { paths }) }
|
|
208
|
+
catch(e) { if (e.code !== 'MODULE_NOT_FOUND') throw e }
|
|
209
|
+
}
|
package/lib/build/constants.js
CHANGED
|
@@ -35,6 +35,7 @@ exports.CONTENT_ENV = "contentEnv" // copy .env fil
|
|
|
35
35
|
exports.CONTENT_DEFAULT_ENV_JSON = "contentDefaultEnvJson" // copy default-env.json file into deployment folder - DISABLED by default
|
|
36
36
|
|
|
37
37
|
exports.OPTION_WS = "ws" // run a workspace build, e.g. including model folders of configured npm workspaces
|
|
38
|
+
exports.OPTION_WS_PACK = "ws-pack" // enable tarball based packaging of workspace dependencies
|
|
38
39
|
exports.CSV_FILE_TARGET = "csvFileTarget" // target folder when copying CSV files to the deployment target folder ./db/src/gen/*, default is 'data'
|
|
39
40
|
exports.CSV_FILE_DETECTION = "csvFileDetection" // detect CSV files based on CDS model location - ENABLED by default
|
|
40
41
|
exports.EXTENSION_POINT_VALIDATION = "extensionPointValidation" // validate extension point restrictions defined by the extended SaaS app - ENABLED by default
|
|
@@ -6,7 +6,7 @@ const InternalBuildPlugin = require('../internalBuildPlugin')
|
|
|
6
6
|
const { BuildError, relativePaths, BuildMessage, getWorkspaces, hasOptionValue, getFiles } = require('../../util')
|
|
7
7
|
const { OUTPUT_MODE_NONE, OUTPUT_MODE, CONTENT_PACKAGE_JSON, CONTENT_HDBTABLEDATA, CSV_FILE_DETECTION,
|
|
8
8
|
CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, CONTENT_NODE_MODULES, OUTPUT_MODE_RESULT, CONTINUE_UNRESOLVED_SCHEMA_CHANGES,
|
|
9
|
-
CSV_FILE_TARGET, OPTION_WS} = require('../../constants')
|
|
9
|
+
CSV_FILE_TARGET, OPTION_WS } = require('../../constants')
|
|
10
10
|
const { WARNING } = InternalBuildPlugin
|
|
11
11
|
const DEFAULT_COMPILE_DEST_FOLDER = path.normalize("src/gen")
|
|
12
12
|
const FILE_EXT_CSV = ".csv"
|
|
@@ -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)
|
|
@@ -38,9 +38,14 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
38
38
|
if (!model) {
|
|
39
39
|
return
|
|
40
40
|
}
|
|
41
|
+
let workspaceFiles
|
|
42
|
+
if (cds.cli.options?.[OPTION_WS] || hasOptionValue(this.task.options[OPTION_WS], true)) {
|
|
43
|
+
workspaceFiles = await this._getWorkspaceFiles()
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
// the order of 1 and 2 is important
|
|
42
47
|
// 1. compile
|
|
43
|
-
const hdiPlugins = await this._compileToHana(model)
|
|
48
|
+
const hdiPlugins = await this._compileToHana(model, workspaceFiles)
|
|
44
49
|
|
|
45
50
|
if (!this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_NONE)) {
|
|
46
51
|
// 2. copy resources
|
|
@@ -49,9 +54,15 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
49
54
|
} else {
|
|
50
55
|
await this._copyResourcesExt(src, dest, model)
|
|
51
56
|
}
|
|
52
|
-
// copy native hana artifacts from
|
|
53
|
-
if (
|
|
54
|
-
|
|
57
|
+
// copy native hana artifacts from workspaces
|
|
58
|
+
if (workspaceFiles) {
|
|
59
|
+
const dest = path.join(this.task.dest, 'src/gen')
|
|
60
|
+
await Promise.all(
|
|
61
|
+
workspaceFiles.map((src) => {
|
|
62
|
+
const { dir, filePath } = src
|
|
63
|
+
return this.copy(path.join(dir, filePath)).to(path.join(dest, filePath))
|
|
64
|
+
})
|
|
65
|
+
)
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
// create additional stuff in dest
|
|
@@ -217,7 +228,6 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
217
228
|
}
|
|
218
229
|
|
|
219
230
|
const promises = []
|
|
220
|
-
const relDest = path.relative(this.task.dest, this.task.options.compileDest)
|
|
221
231
|
const options = { ...this.options(), messages: this.messages, dirs: csvDirs, baseDir: this.task.options.compileDest }
|
|
222
232
|
|
|
223
233
|
const toHdbtabledata = cds.compile.to.hdbtabledata ?? require(path.join(cds.home, 'bin/build/provider/hana/2tabledata')) // cds@6 compatibility
|
|
@@ -229,7 +239,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
229
239
|
let tableDataPath = csvFolder.startsWith(this.task.options.compileDest) ? csvFolder : this.task.options.compileDest
|
|
230
240
|
tableDataPath = path.join(tableDataPath, file)
|
|
231
241
|
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
|
|
232
|
-
this._result.hana.
|
|
242
|
+
this._result.hana.add(path.relative(this.task.dest, tableDataPath))
|
|
233
243
|
}
|
|
234
244
|
promises.push(this.write(tableData).to(tableDataPath))
|
|
235
245
|
}
|
|
@@ -254,7 +264,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
254
264
|
*
|
|
255
265
|
* @param {object} model The compiled csn model
|
|
256
266
|
*/
|
|
257
|
-
async _compileToHana(model) {
|
|
267
|
+
async _compileToHana(model, workspaceFiles) {
|
|
258
268
|
// compile to old format (.hdbcds) or new format (.hdbtable / .hdbview)
|
|
259
269
|
const format = this.getBuildOption(DEPLOY_FORMAT) || cds.env.requires.db?.[DEPLOY_FORMAT] || cds.env.hana?.[DEPLOY_FORMAT]
|
|
260
270
|
if (!cds.compile.to[format]) {
|
|
@@ -267,7 +277,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
267
277
|
// compatibility with existing cds.compile.to.hdbtable plugins cds < 8.0.0
|
|
268
278
|
return await this._compileToHdb(model, format)
|
|
269
279
|
} else {
|
|
270
|
-
return await this._compileToHdbmigration(model, format)
|
|
280
|
+
return await this._compileToHdbmigration(model, format, workspaceFiles)
|
|
271
281
|
}
|
|
272
282
|
}
|
|
273
283
|
|
|
@@ -285,7 +295,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
285
295
|
|
|
286
296
|
hdiPlugins.add(suffix)
|
|
287
297
|
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
|
|
288
|
-
this._result.hana.
|
|
298
|
+
this._result.hana.add(path.join(relDest, file))
|
|
289
299
|
}
|
|
290
300
|
promises.push(this.write(content).to(path.join(this.task.options.compileDest, file)))
|
|
291
301
|
if (suffix === FILE_EXT_HDBTABLE) {
|
|
@@ -303,7 +313,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
303
313
|
return hdiPlugins
|
|
304
314
|
}
|
|
305
315
|
|
|
306
|
-
async _compileToHdbmigration(model, format) {
|
|
316
|
+
async _compileToHdbmigration(model, format, workspaceFiles) {
|
|
307
317
|
const hdiPlugins = new Set()
|
|
308
318
|
const relDestDir = path.relative(this.task.dest, this.task.options.compileDest)
|
|
309
319
|
const relDbDestDir = path.relative(cds.root, this.task.options.compileDest)
|
|
@@ -326,33 +336,37 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
326
336
|
|
|
327
337
|
for (const { name, suffix, content, changed } of definitions) {
|
|
328
338
|
hdiPlugins.add(suffix)
|
|
329
|
-
const
|
|
339
|
+
const fileName = name + suffix
|
|
330
340
|
if (suffix === FILE_EXT_HDBMIGRATIONTABLE) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
341
|
+
// Skip the .hdbmigrationtable file if its origin is a different workspace.
|
|
342
|
+
// The workspace build copies artifacts located in 'db/src/**' workspace folders.
|
|
343
|
+
if (!workspaceFiles?.find(wsFile => wsFile.fileName === fileName)) {
|
|
344
|
+
migrationTableFiles.push(path.join(relDbSrcDir, fileName))
|
|
345
|
+
if (changed) {
|
|
346
|
+
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
|
|
347
|
+
this._result.hana.add(path.join("src", fileName))
|
|
348
|
+
}
|
|
349
|
+
promises.push(this.write(content).to(path.join(dbSrcDir, fileName)))
|
|
350
|
+
} else {
|
|
351
|
+
DEBUG?.(`no change, keep existing ${fileName}`)
|
|
335
352
|
}
|
|
336
|
-
promises.push(this.write(content).to(path.join(dbSrcDir, file)))
|
|
337
|
-
} else {
|
|
338
|
-
DEBUG?.(`no change, keep existing ${file}`)
|
|
339
353
|
}
|
|
340
354
|
} else {
|
|
341
355
|
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
|
|
342
|
-
this._result.hana.
|
|
356
|
+
this._result.hana.add(path.join(relDestDir, fileName))
|
|
343
357
|
}
|
|
344
|
-
promises.push(this.write(content).to(path.join(this.task.options.compileDest,
|
|
358
|
+
promises.push(this.write(content).to(path.join(this.task.options.compileDest, fileName)))
|
|
345
359
|
if (suffix === FILE_EXT_HDBTABLE) {
|
|
346
360
|
// issue an error in case a .hdbmigrationtable file already exists
|
|
347
361
|
if (fs.existsSync(path.join(dbSrcDir, name + FILE_EXT_HDBMIGRATIONTABLE))) {
|
|
348
|
-
validationError = new BuildError(`Multiple files exist defining the same HANA artifact - [${path.join(relDbSrcDir, name + FILE_EXT_HDBMIGRATIONTABLE)}, ${path.join(relDbDestDir,
|
|
362
|
+
validationError = new BuildError(`Multiple files exist defining the same HANA artifact - [${path.join(relDbSrcDir, name + FILE_EXT_HDBMIGRATIONTABLE)}, ${path.join(relDbDestDir, fileName)}].\nEither annotate the model entity using @cds.persistence.journal or undeploy the file [${path.join('src', name + FILE_EXT_HDBMIGRATIONTABLE)}] using an undeploy.json file.`)
|
|
349
363
|
}
|
|
350
364
|
}
|
|
351
365
|
}
|
|
352
366
|
}
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
// cds build command is again executed
|
|
367
|
+
// The last-dev CSN shall only be updated, if all .hdbmigrationtable files could be successfully updated.
|
|
368
|
+
// Of course, .hdbmigrationtable files already written would need to be manually reverted before the
|
|
369
|
+
// cds build command is again executed.
|
|
356
370
|
await Promise.all(promises)
|
|
357
371
|
|
|
358
372
|
try {
|
|
@@ -531,35 +545,38 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
531
545
|
}
|
|
532
546
|
}
|
|
533
547
|
|
|
534
|
-
|
|
535
548
|
/**
|
|
536
|
-
*
|
|
549
|
+
* Returns all hana native artifacts located in db/src/** workspace folders.
|
|
550
|
+
* @returns {Array} Array of workspace files, each entry contains the db src directory,
|
|
551
|
+
* the relative file path and the file name.
|
|
537
552
|
*/
|
|
538
|
-
async
|
|
539
|
-
const
|
|
553
|
+
async _getWorkspaceFiles() {
|
|
554
|
+
const dbPaths = new Set()
|
|
540
555
|
const workspaces = await getWorkspaces()
|
|
541
556
|
|
|
542
557
|
workspaces.forEach(workspace => {
|
|
543
558
|
const dbSrc = path.join(cds.root, workspace, 'db/src')
|
|
544
559
|
const dbCfg = path.join(cds.root, workspace, 'db/cfg')
|
|
545
560
|
if (fs.existsSync(dbSrc)) {
|
|
546
|
-
|
|
561
|
+
dbPaths.add(dbSrc)
|
|
547
562
|
}
|
|
548
563
|
if (fs.existsSync(dbCfg)) {
|
|
549
|
-
|
|
564
|
+
dbPaths.add(dbCfg)
|
|
550
565
|
}
|
|
551
566
|
})
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
for (const src of srcPaths) {
|
|
555
|
-
await this.copyNativeContent(src, dest, (entry) => {
|
|
567
|
+
const workspaceFiles = [...dbPaths].reduce((acc, src) => {
|
|
568
|
+
const files = getFiles(src, (entry) => {
|
|
556
569
|
if (entry.match(/(\/|\\)gen(\/|\\)?$/)) {
|
|
557
570
|
// skip gen folder content
|
|
558
571
|
return false
|
|
559
572
|
}
|
|
560
573
|
return true
|
|
561
574
|
})
|
|
562
|
-
|
|
575
|
+
files.forEach(file => acc.add({ dir: src, filePath: path.relative(src, file), fileName: path.basename(file) }))
|
|
576
|
+
return acc
|
|
577
|
+
}, new Set())
|
|
578
|
+
|
|
579
|
+
return [...workspaceFiles]
|
|
563
580
|
}
|
|
564
581
|
|
|
565
582
|
static async _readTemplateAsJson(template) {
|
|
@@ -5,7 +5,7 @@ const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue, norma
|
|
|
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_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION, NODEJS_MODEL_EXCLUDE_LIST,
|
|
8
|
-
IGNORE_DEFAULT_MODELS,
|
|
8
|
+
IGNORE_DEFAULT_MODELS, OPTION_WS } = require("../constants")
|
|
9
9
|
const ResourcesTarBuilder = require('./mtx/resourcesTarBuilder')
|
|
10
10
|
const DEBUG = cds.debug('cli|build')
|
|
11
11
|
|
|
@@ -285,7 +285,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
static async _setDefaultModel(task) {
|
|
288
|
-
const ws = hasOptionValue(task.options?.[
|
|
288
|
+
const ws = hasOptionValue(task.options?.[OPTION_WS], true) || cds.cli.options?.[OPTION_WS]
|
|
289
289
|
// REVISIT: filter nodejs bootstrap service models for Java - issues/12770#issuecomment-1805719
|
|
290
290
|
const filter = hasJavaNature() ? (m) => !NODEJS_MODEL_EXCLUDE_LIST.includes(m) : null
|
|
291
291
|
let taskModelPaths = task.options?.model
|
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
@@ -5,7 +5,8 @@ const cmd = require('../../../util/command');
|
|
|
5
5
|
const EdmxBuildPlugin = require('../edmxBuildPlugin')
|
|
6
6
|
const { BuildError, getWorkspaces } = require('../../util')
|
|
7
7
|
const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
|
|
8
|
-
CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX
|
|
8
|
+
CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX,
|
|
9
|
+
OPTION_WS_PACK} = require('../../constants')
|
|
9
10
|
const { WARNING } = EdmxBuildPlugin
|
|
10
11
|
const DEBUG = cds.debug('cli|build')
|
|
11
12
|
|
|
@@ -36,7 +37,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
36
37
|
}
|
|
37
38
|
// by default model contains all features
|
|
38
39
|
const model = await this.model()
|
|
39
|
-
if (!model && !this.context.options[
|
|
40
|
+
if (!model && !this.context.options[OPTION_WS_PACK]) {
|
|
40
41
|
return
|
|
41
42
|
}
|
|
42
43
|
if (model) {
|
|
@@ -55,7 +56,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
55
56
|
if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
|
|
56
57
|
const srcSrv = this.task.src === cds.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
|
|
57
58
|
await this._copyNativeContent(cds.root, srcSrv, destRoot, destSrv)
|
|
58
|
-
if (this.context.options[
|
|
59
|
+
if (this.context.options[OPTION_WS_PACK]) {
|
|
59
60
|
await this._packWorkspaceDependencies(destRoot)
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -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,
|
|
6
|
-
elements : false,
|
|
7
|
-
min : false,
|
|
8
|
-
namespaces: true,
|
|
9
|
-
queries : false,
|
|
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)
|
|
60
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -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
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
console.log(`-- ${
|
|
82
|
-
console.log(
|
|
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:
|
|
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`);
|