@sap/cds-dk 8.4.0 → 8.4.2
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 +34 -0
- package/bin/add.js +2 -12
- package/bin/cds.js +5 -0
- package/bin/compile/index.js +6 -1
- package/bin/deploy/index.js +6 -1
- package/lib/bind/index.js +1 -1
- package/lib/build/provider/hana/index.js +1 -1
- package/lib/compile/to-xsuaa.js +39 -26
- package/lib/deploy/to-hana/hana.js +60 -53
- package/lib/index.js +14 -14
- package/lib/init/constants.js +1 -1
- package/lib/init/template/application-logging/files/package.json.hbs +6 -2
- package/lib/init/template/application-logging/index.js +4 -4
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/data/as-csv.js +2 -2
- package/lib/init/template/data/as-json.js +48 -44
- package/lib/init/template/hana/files/db/package.json +1 -1
- package/lib/init/template/hana/files/package.json.hbs +18 -4
- package/lib/init/template/http/index.js +12 -11
- package/lib/init/template/side-by-side-extensibility/index.js +1 -0
- package/lib/init/template/typer/index.js +0 -1
- package/lib/mtx/push.js +0 -1
- package/npm-shrinkwrap.json +36 -25
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,44 @@ 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.4.2 - 2024-11-19
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `cds init` uses latest Maven Java archetype version 3.4.1 for creating Java projects.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `cds add -p/--package` correctly parse plugin-contributed options.
|
|
19
|
+
- `cds add side-by-side-extensibility` does not throw an error.
|
|
20
|
+
- fix method name in `cds bind` credential handling.
|
|
21
|
+
- `cds add --help` no longer has missing line breaks in help text.
|
|
22
|
+
- `cds` commands no longer fail with an error `cds.extend is not a function` with very old versions of `@sap/cds`.
|
|
23
|
+
- `cds add hana` adds the `requires.db` entry to `.cdsrc.json` for Java projects.
|
|
24
|
+
- `cds add data` creates decimal values with correct scale `0` if only precision is given, like in `Decimal(15)`.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Version 8.4.1 - 2024-11-08
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- `cds deploy --no-build` lets you skip the implicit `cds build`.
|
|
32
|
+
- `cds add data/http` supports new type `cds.Map`.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- `cds compile --to <unknown>` shows a cleaner error message.
|
|
37
|
+
- `cds add data` handles composition of one correctly.
|
|
38
|
+
- `cds add http` creates payloads for unbound actions in services.
|
|
39
|
+
- `cds compile --to xsuaa` can now handle annotation expressions in `where` clauses.
|
|
40
|
+
- `cds add http` produces requests for all expected data and no longer relies on existing data on the side.
|
|
41
|
+
- `cds add typer` creates `tsconfig.json` that works with `cds-tsx`.
|
|
42
|
+
|
|
10
43
|
## Version 8.4.0 - 2024-10-29
|
|
11
44
|
|
|
12
45
|
### Added
|
|
13
46
|
|
|
47
|
+
- `cds add application-logging` for Java also executes the `cds:add` goal with feature `APPLICATION_LOGGING`.
|
|
14
48
|
- Added support for `import` and `export` for auto-completion in `package.json` and `.cdsrc.json`.
|
|
15
49
|
- `cds add side-by-side-extensibility` adds configuration for logic extensibility via extension points (Java).
|
|
16
50
|
- `cds init --java` can be used as an alias to `cds init --add java`.
|
package/bin/add.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const cds = require('../lib/cds'), { fs } = cds.utils
|
|
2
2
|
const DEBUG = cds.debug('add')
|
|
3
3
|
const { join } = require('path')
|
|
4
|
-
const cmd = require('../lib/util/command')
|
|
5
4
|
|
|
6
5
|
const fromDk = fs.readdirSync(join(__dirname, '../lib/init/template'))
|
|
7
6
|
const fromPlugins = require('../lib/init/add').registered
|
|
@@ -60,7 +59,7 @@ ${Object.entries(conf).sort(([a], [b]) => a.localeCompare(b)).flatMap(([pluginNa
|
|
|
60
59
|
const text4 = arr => arr.map(({ key, help, short }) => help && ` ${key}${short ? ' | '+short : ''}\n\n ${help}`).join('\n\n')
|
|
61
60
|
const optionsText = text4(options), flagsText = text4(flags)
|
|
62
61
|
if (!optionsText && !flagsText) return []
|
|
63
|
-
return ` *cds add ${pluginName}
|
|
62
|
+
return ` *cds add ${pluginName}*${optionsText?`\n\n${optionsText}`:''}${flagsText?`\n\n${flagsText}`:''}`
|
|
64
63
|
}).join('\n\n\n')}
|
|
65
64
|
|
|
66
65
|
|
|
@@ -100,17 +99,8 @@ async function handleCompletion(currentWord, previousWord, argv, util) {
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
async function add(args) {
|
|
103
|
-
// await cds.plugins
|
|
104
102
|
const CDSGenerator = require('../lib/init')
|
|
105
103
|
const generator = new CDSGenerator()
|
|
106
104
|
const features = args.map(f => f.split(/[,\s]/)).flat()
|
|
107
|
-
|
|
108
|
-
console.log(`running with --package, adding dependency ${cds.cli.options.package}`)
|
|
109
|
-
await cmd.spawnCommand(`npm`, ['add', cds.cli.options.package], { cwd: cds.root })
|
|
110
|
-
await cmd.spawnCommand(`cds`, ['add', ...features], { cwd: cds.root }) // plugins need to reload
|
|
111
|
-
// await cds.plugins
|
|
112
|
-
// await generator.add(features)
|
|
113
|
-
} else {
|
|
114
|
-
await generator.add(features)
|
|
115
|
-
}
|
|
105
|
+
await generator.add(features)
|
|
116
106
|
}
|
package/bin/cds.js
CHANGED
|
@@ -62,6 +62,11 @@ const cli = {
|
|
|
62
62
|
// REVISIT: Have to load plugins to allow for plugin-contributed flags!
|
|
63
63
|
if (cmd === 'add') {
|
|
64
64
|
cds.cli = { command: 'add' }
|
|
65
|
+
if (argv.includes('-p') || argv.includes('--package')) {
|
|
66
|
+
const p = argv[argv.indexOf('-p') + 1] || argv[argv.indexOf('--package') + 1]
|
|
67
|
+
console.log(`running with --package, adding dependency ${p}`)
|
|
68
|
+
await require('../lib/util/command').spawnCommand(`npm`, ['add', p], { cwd: cds.root })
|
|
69
|
+
}
|
|
65
70
|
await cds.plugins
|
|
66
71
|
for (const [, Plugin] of cds.add.registered) {
|
|
67
72
|
const all = Object.entries((new Plugin).options())
|
package/bin/compile/index.js
CHANGED
|
@@ -238,6 +238,12 @@ async function compile (models, options={}) {
|
|
|
238
238
|
if (/mta.*\.ya?ml/.test(models[0])) {
|
|
239
239
|
model = Promise.resolve(models[0])
|
|
240
240
|
} else {
|
|
241
|
+
for (const kind of ['to', 'for']) {
|
|
242
|
+
const tail = options[kind]
|
|
243
|
+
if (typeof tail === 'string' && !cds.compile[kind][tail]) {
|
|
244
|
+
throw `Unknown model processor: cds.compile.${kind}.${tail}`
|
|
245
|
+
}
|
|
246
|
+
}
|
|
241
247
|
model = cds.load (models, options)
|
|
242
248
|
src = models[0] .replace (/\.[^.]+$/,'') //> excluding source file extension, e.g. .cds
|
|
243
249
|
}
|
|
@@ -305,7 +311,6 @@ async function compile (models, options={}) {
|
|
|
305
311
|
let p = cds.compile[kind]
|
|
306
312
|
for (let each of tail.split('.')) {
|
|
307
313
|
p = p[each]
|
|
308
|
-
if (!p) throw `Unknown model processor: cds.compile.${kind}.${tail}`
|
|
309
314
|
}
|
|
310
315
|
return p
|
|
311
316
|
}
|
package/bin/deploy/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = Object.assign(deploy, {
|
|
2
2
|
options: ['--to', '--for', '--out', '--tunnel-address', '--vcap-file', '--delta-from' ],
|
|
3
|
-
flags: ['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--model-only', '--script', '--with-auto-schema-evolution' ],
|
|
3
|
+
flags: ['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--no-build', '--model-only', '--script', '--with-auto-schema-evolution' ],
|
|
4
4
|
shortcuts: ['-2', '-4', '-o'],
|
|
5
5
|
help: `
|
|
6
6
|
# SYNOPSIS
|
|
@@ -37,6 +37,10 @@ module.exports = Object.assign(deploy, {
|
|
|
37
37
|
|
|
38
38
|
Just print the SQL statements without executing them.
|
|
39
39
|
|
|
40
|
+
*--no-build* (only in combination with *--to hana*)
|
|
41
|
+
|
|
42
|
+
Skip running *cds build*.
|
|
43
|
+
|
|
40
44
|
*--out* | *-o* <file>
|
|
41
45
|
|
|
42
46
|
Write the DDL statements to the given file.
|
|
@@ -97,6 +101,7 @@ module.exports = Object.assign(deploy, {
|
|
|
97
101
|
cds deploy --to sqlite:db
|
|
98
102
|
cds deploy --to hana
|
|
99
103
|
cds deploy --to hana:myService --auto-undeploy
|
|
104
|
+
cds deploy --to hana --no-build
|
|
100
105
|
|
|
101
106
|
`})
|
|
102
107
|
|
package/lib/bind/index.js
CHANGED
|
@@ -118,7 +118,7 @@ function mergeCredentials(credentials, customCredentials) {
|
|
|
118
118
|
if (!credentials[key]) {
|
|
119
119
|
credentials[key] = {};
|
|
120
120
|
}
|
|
121
|
-
credentials[key] = this.
|
|
121
|
+
credentials[key] = this.mergeCredentials(credentials[key], customCredentials[key]);
|
|
122
122
|
} else {
|
|
123
123
|
credentials[key] = customCredentials[key];
|
|
124
124
|
}
|
|
@@ -56,7 +56,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
56
56
|
}
|
|
57
57
|
// copy native hana artifacts from workspaces
|
|
58
58
|
if (workspaceFiles) {
|
|
59
|
-
const dest = path.join(this.task.dest, 'src/gen')
|
|
59
|
+
const dest = path.join(this.task.dest, this.isStagingBuild() ? 'src' : 'src/gen')
|
|
60
60
|
await Promise.all(
|
|
61
61
|
workspaceFiles.map((src) => {
|
|
62
62
|
const { dir, filePath } = src
|
package/lib/compile/to-xsuaa.js
CHANGED
|
@@ -7,44 +7,57 @@ const hardcoded = {
|
|
|
7
7
|
Attributes: ['id', 'tenant', 'ext_attr']
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
module.exports = function compile_to_xsuaa (model, options) {
|
|
12
11
|
|
|
13
12
|
const base = getBaseModel(options)
|
|
14
13
|
const scopes = {}
|
|
15
14
|
const attributes = {}
|
|
16
15
|
|
|
16
|
+
const findUserAttrInExpression = (expr, attributes = []) => {
|
|
17
|
+
// find `$user` ref, but not e.g. `$self.$user`
|
|
18
|
+
if (typeof expr === 'object' && expr.ref?.length && expr.ref[0] === '$user') {
|
|
19
|
+
if (expr.ref.length === 2) {
|
|
20
|
+
const attr = expr.ref[1]
|
|
21
|
+
if (!/^[a-zA-Z0-9_]*$/.test(attr)) {
|
|
22
|
+
// see https://help.sap.com/docs/CP_AUTHORIZ_TRUST_MNG/ae8e8427ecdf407790d96dad93b5f723/517895a9612241259d6941dbf9ad81cb.html#attributes
|
|
23
|
+
throw new Error(`Illegal attribute name '${attr}' in '${expr}'. Only characters a-z, A-Z, 0-9, _ are allowed.`)
|
|
24
|
+
}
|
|
25
|
+
if (!hardcoded.Attributes.includes(attr)) attributes.push(attr)
|
|
26
|
+
}
|
|
27
|
+
// Reject expressions like `$user.foo.bar`, as xsuaa attributes are primitive, not structured
|
|
28
|
+
else if (expr.ref.length > 2) {
|
|
29
|
+
throw new Error (`Structured attributes like '${expr.ref.join('.')}' are not supported`)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (typeof expr === 'object') { // descend sub expressions etc.
|
|
33
|
+
Object.values(expr).forEach(e => findUserAttrInExpression(e, attributes))
|
|
34
|
+
}
|
|
35
|
+
else if (Array.isArray(expr)) { // descend arrays
|
|
36
|
+
expr.forEach(e => findUserAttrInExpression(e, attributes))
|
|
37
|
+
}
|
|
38
|
+
return attributes
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string|object} condition - the condition object or string
|
|
43
|
+
* @returns string[] - the attributes found in the condition
|
|
44
|
+
*/
|
|
17
45
|
const parseAttributes = (condition) => { // e.g. 'foo = $user.bar or baz = $user.boo'
|
|
18
46
|
if (!condition) return []
|
|
19
47
|
|
|
20
|
-
// first validate basic syntax of expression by parsing it
|
|
21
48
|
let expr
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const userIdx = expr.ref?.indexOf('$user')
|
|
31
|
-
if (userIdx >= 0 && expr.ref.length > userIdx+2) {
|
|
32
|
-
throw new Error (`Structured attributes like '${expr.ref.join('.')}' are not supported`)
|
|
49
|
+
if (typeof condition === 'object') {
|
|
50
|
+
if (!condition.xpr && !condition['=']) throw new Error(`Invalid expression: ${JSON.stringify(condition)}`)
|
|
51
|
+
expr = condition.xpr
|
|
52
|
+
} else {
|
|
53
|
+
try {
|
|
54
|
+
expr = cds.parse.expr(condition).xpr
|
|
55
|
+
} catch (err) {
|
|
56
|
+
throw new Error(`Invalid expression': ${err.message}`, { cause: err })
|
|
33
57
|
}
|
|
34
|
-
}
|
|
58
|
+
}
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
// Impl. note: use the expression string rather than the parsed expression as the above example would be
|
|
38
|
-
// `[ { ref: [ '$user', 'foo' ] }, '-', { ref: [ 'bar' ] } ]`
|
|
39
|
-
// making it ambiguous to tell if it was `$user.foo-bar` or `$user.foo - bar`
|
|
40
|
-
return Array.from(condition.matchAll(/\$user\s*\.\s*(\S*)/g))
|
|
41
|
-
.map(m => m[1])
|
|
42
|
-
.filter(m => !hardcoded.Attributes.includes(m))
|
|
43
|
-
.map(m => {
|
|
44
|
-
// see https://help.sap.com/docs/CP_AUTHORIZ_TRUST_MNG/ae8e8427ecdf407790d96dad93b5f723/517895a9612241259d6941dbf9ad81cb.html#attributes
|
|
45
|
-
if (!/^[a-zA-Z0-9_]*$/.test(m)) throw new Error(`Illegal attribute name '${m}' in '${condition}'. Only characters a-z, A-Z, 0-9, _ are allowed.`)
|
|
46
|
-
return m
|
|
47
|
-
})
|
|
60
|
+
return findUserAttrInExpression(expr)
|
|
48
61
|
}
|
|
49
62
|
|
|
50
63
|
cds.reflect(model).foreach(def => {
|
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const fs = require('fs').promises;
|
|
4
4
|
|
|
5
5
|
const cds = require('../../../lib/cds');
|
|
6
|
-
const { exists, read } = cds.utils;
|
|
6
|
+
const { exists, read, write, mkdirp } = cds.utils;
|
|
7
7
|
|
|
8
8
|
const { build } = require('../../build');
|
|
9
9
|
|
|
@@ -67,68 +67,73 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
let buildResult
|
|
71
|
+
if (cds.cli.options?.['no-build']) {
|
|
72
|
+
console.log(`Provided flag '--no-build', skipping cds build`)
|
|
73
|
+
} else {
|
|
74
|
+
buildResult = await this._build();
|
|
75
|
+
}
|
|
71
76
|
|
|
72
77
|
if (process.env.TARGET_CONTAINER) {
|
|
73
78
|
vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
if (dry) {
|
|
82
|
+
const sortedResult = Array.from(buildResult.hana).sort();
|
|
83
|
+
for (const file of sortedResult) {
|
|
84
|
+
const srcPath = path.join(buildResult.dest, file);
|
|
85
|
+
console.log(`-- ${path.relative(cds.root, srcPath)}`);
|
|
86
|
+
console.log(await read(srcPath));
|
|
87
|
+
console.log();
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
// the primary database is always built and deployed
|
|
91
|
+
const dest = path.join(cds.env.build.target, cds.env.folders.db);
|
|
92
|
+
|
|
93
|
+
let serviceName = pServiceName;
|
|
94
|
+
let serviceKeyName;
|
|
95
|
+
const { instance, key } = cds.requires.db?.binding ?? {};
|
|
96
|
+
if (!serviceName && instance) {
|
|
97
|
+
serviceName = instance;
|
|
98
|
+
serviceKeyName = key;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (undeployWhitelist) {
|
|
102
|
+
console.log('Writing undeploy.json');
|
|
103
|
+
await write(undeployWhitelist).to(dest, 'undeploy.json')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
|
|
107
|
+
if (!serviceName && hasVCAPEnv) {
|
|
108
|
+
await mkdirp(dest);
|
|
85
109
|
} else {
|
|
86
|
-
const
|
|
110
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
111
|
+
await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
|
|
87
112
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!
|
|
91
|
-
|
|
92
|
-
serviceKeyName = cds.env.requires.db.binding.key;
|
|
113
|
+
serviceName ??= cfServiceInstanceName;
|
|
114
|
+
serviceKeyName ??= cfServiceInstanceKeyName
|
|
115
|
+
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
116
|
+
vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
93
117
|
}
|
|
94
118
|
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
await fs.write(path.join(dest, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
|
|
119
|
+
if (storeCredentials) {
|
|
120
|
+
await this._addInstanceToDefaultEnvJson([dest, cds.root], cfServiceInstanceName, serviceKey);
|
|
98
121
|
}
|
|
122
|
+
}
|
|
99
123
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
} else {
|
|
104
|
-
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
105
|
-
await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
|
|
106
|
-
|
|
107
|
-
serviceName ??= cfServiceInstanceName;
|
|
108
|
-
serviceKeyName ??= cfServiceInstanceKeyName
|
|
109
|
-
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
110
|
-
vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (storeCredentials) {
|
|
114
|
-
await this._addInstanceToDefaultEnvJson([dest, cds.root], cfServiceInstanceName, serviceKey);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
124
|
+
console.log(`${bold('Starting deployment to SAP HANA ...')}`);
|
|
125
|
+
DEBUG?.({ dest, vcapEnv, hdiOptions })
|
|
126
|
+
await hdi.deploy(dest, vcapEnv, hdiOptions);
|
|
117
127
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
...options,
|
|
125
|
-
to: `${serviceName}:${serviceKeyName}`,
|
|
126
|
-
for: options.for,
|
|
127
|
-
kind: task.for
|
|
128
|
-
}
|
|
129
|
-
const services = await bind(bindOptions);
|
|
130
|
-
await storeBindings(services, bindOptions);
|
|
128
|
+
if (!hasVCAPEnv) {
|
|
129
|
+
const bindOptions = {
|
|
130
|
+
...options,
|
|
131
|
+
to: `${serviceName}:${serviceKeyName}`,
|
|
132
|
+
for: options.for,
|
|
133
|
+
kind: BUILD_TASK_HANA
|
|
131
134
|
}
|
|
135
|
+
const services = await bind(bindOptions);
|
|
136
|
+
await storeBindings(services, bindOptions);
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
if (storeCredentials) {
|
|
@@ -145,7 +150,7 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
145
150
|
console.warn(warn(warnings.join('\n')));
|
|
146
151
|
}
|
|
147
152
|
|
|
148
|
-
return
|
|
153
|
+
return buildResult;
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
|
|
@@ -206,9 +211,11 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
206
211
|
src: cds.env.folders.db,
|
|
207
212
|
[OUTPUT_MODE]: OUTPUT_MODE_FILESYSTEM | OUTPUT_MODE_RESULT
|
|
208
213
|
}
|
|
209
|
-
|
|
210
214
|
console.log(`Running build`);
|
|
211
|
-
|
|
215
|
+
|
|
216
|
+
// cds build throws an error if the src folder does not exist
|
|
217
|
+
const buildResult = await build(buildTaskOptions);
|
|
218
|
+
return buildResult[0].result;
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
async _addInstanceToDefaultEnvJson(currentFolders, serviceInstanceName, serviceKey) {
|
package/lib/index.js
CHANGED
|
@@ -35,6 +35,20 @@ class CdsDk {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
const REQUIRED_CDS_MAJOR = 7, REQUIRED_CDS_MINOR = 2
|
|
39
|
+
const [major, minor] = cds.version.split('.').map(Number)
|
|
40
|
+
if (major < REQUIRED_CDS_MAJOR || major === REQUIRED_CDS_MAJOR && minor < REQUIRED_CDS_MINOR) {
|
|
41
|
+
const [dkVersion] = require('../package.json').version.split('.')
|
|
42
|
+
|
|
43
|
+
console.error(`
|
|
44
|
+
This application uses '@sap/cds' version ${major}.${minor}, which is not compatible with the installed '@sap/cds-dk' version ${dkVersion}.
|
|
45
|
+
You can either:
|
|
46
|
+
- update '@sap/cds' to the latest version (recommended)
|
|
47
|
+
- downgrade '@sap/cds-dk' to version ${REQUIRED_CDS_MAJOR}
|
|
48
|
+
`)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
cds.extend (cds.constructor) .with (CdsDk)
|
|
39
53
|
|
|
40
54
|
class CdsDkAdd {
|
|
@@ -49,20 +63,6 @@ if (process.argv[2] in { add:1, init:1, help:1 }) {
|
|
|
49
63
|
cds.extend (cds.constructor) .with (CdsDkBuild)
|
|
50
64
|
}
|
|
51
65
|
|
|
52
|
-
const REQUIRED_CDS_MAJOR = 7, REQUIRED_CDS_MINOR = 2
|
|
53
|
-
const [major, minor] = cds.version.split('.').map(Number)
|
|
54
|
-
if (major < REQUIRED_CDS_MAJOR || major === REQUIRED_CDS_MAJOR && minor < REQUIRED_CDS_MINOR) {
|
|
55
|
-
const [dkVersion] = require('../package.json').version.split('.')
|
|
56
|
-
|
|
57
|
-
console.error(`
|
|
58
|
-
This application uses '@sap/cds' version ${major}.${minor}, which is not compatible with the installed '@sap/cds-dk' version ${dkVersion}.
|
|
59
|
-
You can either:
|
|
60
|
-
- update '@sap/cds' to the latest version (recommended)
|
|
61
|
-
- downgrade '@sap/cds-dk' to version ${REQUIRED_CDS_MAJOR}
|
|
62
|
-
`)
|
|
63
|
-
process.exit(1)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
66
|
function _local (id) {
|
|
67
67
|
return require.resolve (id, {paths:[
|
|
68
68
|
process.cwd(), // project-local module is preferred
|
package/lib/init/constants.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { readProject } = require('../../projectReader')
|
|
2
2
|
const { merge } = require('../../merge')
|
|
3
3
|
const { srv4, mtxSidecar4, applicationLogging, approuter } = require('../../registries/mta')
|
|
4
|
+
const mvn = require('../../mvn')
|
|
4
5
|
|
|
5
6
|
module.exports = class ApplicationLoggingTemplate extends require('../../plugin') {
|
|
6
7
|
|
|
@@ -20,10 +21,9 @@ module.exports = class ApplicationLoggingTemplate extends require('../../plugin'
|
|
|
20
21
|
|
|
21
22
|
async run() {
|
|
22
23
|
const project = readProject()
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
24
|
+
const { isJava, configFile } = project
|
|
25
|
+
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
26
|
+
if (isJava) await mvn.add('APPLICATION_LOGGING')
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async combine() {
|
|
@@ -39,7 +39,7 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
|
|
|
39
39
|
}
|
|
40
40
|
record.push(...Object.values(value))
|
|
41
41
|
}
|
|
42
|
-
else if (element.is_struct) {
|
|
42
|
+
else if (element.is_struct && element.type !== 'cds.Map') {
|
|
43
43
|
if (i === 0) {
|
|
44
44
|
header.push(...Object.keys(value).map(structField => name + '_' + structField))
|
|
45
45
|
}
|
|
@@ -47,7 +47,7 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
|
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
49
|
if (i === 0) header.push(name)
|
|
50
|
-
if (Array.isArray(value)) { // `array of`
|
|
50
|
+
if (Array.isArray(value) || typeof value === 'object') { // `array of`/`many` and `cds.Map`
|
|
51
51
|
value = JSON.stringify(value)
|
|
52
52
|
}
|
|
53
53
|
value = escape(value)
|
|
@@ -10,35 +10,37 @@ const MAX_ASSOC_DEPTH = 5
|
|
|
10
10
|
*
|
|
11
11
|
* @param {Record<string, object[]>|undefined} data the data with entities and their records to be created
|
|
12
12
|
* @param {object} csn the model
|
|
13
|
-
* @param {Number} numRecords the number of records to be created for each
|
|
13
|
+
* @param {Number} numRecords the number of records to be created for each artifact
|
|
14
14
|
* @param {{referenceData? : Record<string, string}} options misc. options
|
|
15
15
|
* @returns {Record<string, object[]>} the filled data
|
|
16
16
|
*/
|
|
17
17
|
const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
|
|
18
|
-
let { referenceData, contentType } = options
|
|
18
|
+
let { referenceData, contentType, artifact='entity' } = options
|
|
19
19
|
|
|
20
|
-
// if no
|
|
21
|
-
if (!data)
|
|
20
|
+
// if no data is given, use all data
|
|
21
|
+
if (!data) {
|
|
22
|
+
data = csn.all(artifact).reduce((all, e) => { all[e.name] = []; return all }, {})
|
|
23
|
+
}
|
|
22
24
|
|
|
23
25
|
// if no reference data is given, scan for it using cds.deploy.resources
|
|
24
26
|
if (!referenceData) referenceData = await cds.deploy.resources(csn)
|
|
25
27
|
|
|
26
28
|
// sort all entities first that are referenced by others, so that dependant entities can be created after them
|
|
27
29
|
const { definitions: defs } = csn
|
|
28
|
-
const
|
|
29
|
-
const n1HasComp = Object.values(defs[n1].elements).some(el => el.type === 'cds.Composition')
|
|
30
|
-
const n2HasComp = Object.values(defs[n2].elements).some(el => el.type === 'cds.Composition')
|
|
30
|
+
const names = Object.keys(data).sort((n1, n2) => {
|
|
31
|
+
const n1HasComp = Object.values(defs[n1].elements??[]).some(el => el.type === 'cds.Composition')
|
|
32
|
+
const n2HasComp = Object.values(defs[n2].elements??[]).some(el => el.type === 'cds.Composition')
|
|
31
33
|
if (n1HasComp && !n2HasComp) return -1
|
|
32
34
|
if (!n1HasComp && n2HasComp) return 1
|
|
33
35
|
return 0
|
|
34
36
|
})
|
|
35
37
|
|
|
36
38
|
// requested entities shall always be created, so remove it from reference data
|
|
37
|
-
Object.entries(referenceData).filter(([, name]) =>
|
|
39
|
+
Object.entries(referenceData).filter(([, name]) => names.includes(name)).forEach(([file]) => delete referenceData[file])
|
|
38
40
|
|
|
39
41
|
addSemantics(csn)
|
|
40
42
|
|
|
41
|
-
for (const name of
|
|
43
|
+
for (const name of names) {
|
|
42
44
|
if (!data[name] // removed on purpose because records are created inline through compositions
|
|
43
45
|
|| data[name].length >= numRecords) { // numRecords already created by following associations
|
|
44
46
|
continue
|
|
@@ -47,26 +49,26 @@ const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
|
|
|
47
49
|
let numRecordsToGenerate // numRecords might be reduced if some records were created by following associations
|
|
48
50
|
let records = []
|
|
49
51
|
if (data[name].length > 0) {
|
|
50
|
-
numRecordsToGenerate = numRecords - data[name].length
|
|
52
|
+
numRecordsToGenerate = numRecords - data[name].length
|
|
51
53
|
records = Array(numRecordsToGenerate).fill({}).map(() => ({}))
|
|
52
54
|
} else {
|
|
53
55
|
numRecordsToGenerate = numRecords
|
|
54
56
|
records = data[name] = Array(numRecordsToGenerate).fill({}).map(() => ({}))
|
|
55
57
|
}
|
|
56
|
-
const
|
|
58
|
+
const artifact = defs[name]
|
|
57
59
|
|
|
58
60
|
const idNum = randomNumber()
|
|
59
61
|
for (let i = 0; i < numRecordsToGenerate; i++) {
|
|
60
|
-
fillRecord(records[i], idNum+i,
|
|
62
|
+
fillRecord(records[i], idNum+i, artifact, csn, data, null, 0, referenceData)
|
|
61
63
|
}
|
|
62
|
-
data[name] = data[name].concat(records) // append the new records to the possibly
|
|
64
|
+
data[name] = data[name].concat(records) // append the new records to the possibly existing and referenced records
|
|
63
65
|
// remove records w/ duplicate keys. This can happen if the key is an enum (and we have less enum values than requested records).
|
|
64
|
-
data[name] = uniqRecords(data[name],
|
|
66
|
+
data[name] = uniqRecords(data[name], artifact)
|
|
65
67
|
}
|
|
66
68
|
for (let [key, d] of Object.entries(data)) {
|
|
67
69
|
if (contentType === 'json' && d.deleteReference && !d.keepReferece) {
|
|
68
70
|
delete data[key]
|
|
69
|
-
}
|
|
71
|
+
}
|
|
70
72
|
delete d.keepReferece
|
|
71
73
|
delete d.deleteReference
|
|
72
74
|
uniqRecords(d, csn.definitions[key])
|
|
@@ -74,13 +76,13 @@ const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
|
|
|
74
76
|
return data
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
function fillRecord(record, recordID,
|
|
79
|
+
function fillRecord(record, recordID, artifact, csn, data, parent, depth, referenceData) {
|
|
78
80
|
Object.defineProperty(record, '$id', {value: recordID, enumerable: false})
|
|
79
|
-
if (data[
|
|
81
|
+
if (data[artifact.name]) data[artifact.name].push(record) // add id to data, to be referenced by associations to prevent circular dependencies
|
|
80
82
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
record[el.name] = dataForField(el,
|
|
83
|
+
const elems = Object.values(artifact.elements ?? artifact.params).sort(asJson.elementsSorter(csn))
|
|
84
|
+
elems.forEach(el => {
|
|
85
|
+
record[el.name] = dataForField(el, artifact, csn, record, data, parent, depth, referenceData)
|
|
84
86
|
})
|
|
85
87
|
return record
|
|
86
88
|
}
|
|
@@ -110,7 +112,7 @@ asJson.elementsSorter = function (csn) {
|
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
|
|
113
|
-
function dataForField(element,
|
|
115
|
+
function dataForField(element, artifact, csn, record, data, parent, depth, referenceData) {
|
|
114
116
|
if (element.virtual || (element['@Core.Computed'] && element.value))
|
|
115
117
|
return
|
|
116
118
|
if (record[element.name] !== undefined)
|
|
@@ -119,12 +121,12 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
119
121
|
if (element.items?.elements) // `array of` / `many` with complex type
|
|
120
122
|
return [ fillRecord({}, record['$id'], element.items, csn, data, null, depth+1, referenceData) ]
|
|
121
123
|
else if (element.items?.type) // `array of` / `many` with simple type
|
|
122
|
-
return [ dataForField(element.items,
|
|
124
|
+
return [ dataForField(element.items, artifact, csn, record, data, null, depth+1, referenceData) ]
|
|
123
125
|
|
|
124
126
|
if (element.is_struct) {
|
|
125
127
|
let struct = {}
|
|
126
128
|
for (const el of element.elements) {
|
|
127
|
-
struct[el.name] = dataForField(el,
|
|
129
|
+
struct[el.name] = dataForField(el, artifact, csn, record, data, null, depth+1, referenceData)
|
|
128
130
|
}
|
|
129
131
|
return struct
|
|
130
132
|
}
|
|
@@ -166,11 +168,11 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
166
168
|
const enumName = randomFromList(Object.keys(element.enum))
|
|
167
169
|
const value = element.enum[enumName].val ?? element.enum[enumName].value ?? enumName
|
|
168
170
|
// key of common.CodeList, also fill name and descr with enum name
|
|
169
|
-
if (element.key &&
|
|
171
|
+
if (element.key && artifact.elements.name && artifact.elements.descr)
|
|
170
172
|
record.name = record.descr = enumName
|
|
171
173
|
return value
|
|
172
174
|
}
|
|
173
|
-
if (
|
|
175
|
+
if (artifact['@Communication.Contact.email']?.some(mail => mail.address?.['='] === element.name)
|
|
174
176
|
|| element['@Communication.IsEmailAddress']
|
|
175
177
|
|| element['@cds.on.insert']?.['='] === '$user'
|
|
176
178
|
|| element['@cds.on.update']?.['='] === '$user') {
|
|
@@ -180,9 +182,9 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
180
182
|
let str = `${element.name}-${record['$id']}`
|
|
181
183
|
|
|
182
184
|
if (element.key) {
|
|
183
|
-
str = `${
|
|
184
|
-
if (element.name === 'locale' &&
|
|
185
|
-
const record = randomFromReferenceData(
|
|
185
|
+
str = `${artifact.name.split('.').pop()}-${record['$id']}`
|
|
186
|
+
if (element.name === 'locale' && artifact.name.endsWith('texts')) {
|
|
187
|
+
const record = randomFromReferenceData(artifact, csn, referenceData)
|
|
186
188
|
if (record) return record.locale
|
|
187
189
|
return randomLanguage()
|
|
188
190
|
}
|
|
@@ -227,9 +229,11 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
227
229
|
}
|
|
228
230
|
case 'cds.hana.REAL':
|
|
229
231
|
case 'cds.Decimal': {
|
|
230
|
-
const
|
|
232
|
+
const prec = element.precision ? Math.min(Math.pow(10, element.precision), Number.MAX_SAFE_INTEGER) : 100
|
|
233
|
+
const range = { from: 0, to: prec }
|
|
234
|
+
const scale = element.scale ?? (element.precision ? 0 : 2) // scale is 0 if only precision is given
|
|
231
235
|
annotatedIntRange(element, range)
|
|
232
|
-
return +(randomDecimalInRange(range).toFixed(
|
|
236
|
+
return +(randomDecimalInRange(range).toFixed(scale))
|
|
233
237
|
}
|
|
234
238
|
case 'cds.Double': {
|
|
235
239
|
const range = {from: 0, to: 100}
|
|
@@ -237,15 +241,15 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
237
241
|
}
|
|
238
242
|
case 'cds.Composition': {
|
|
239
243
|
const target = csn.definitions[element.target]
|
|
240
|
-
if (!target ||
|
|
241
|
-
|
|
244
|
+
if (!target || artifact.name === target.name || depth > MAX_ASSOC_DEPTH) return
|
|
245
|
+
|
|
242
246
|
let targetRecords = [] // in findKeysInOnCondition target records are linked to the entity, we don't want to overwrite any existing data
|
|
243
247
|
if (data[target.name]) data[target.name].deleteReference = true // for json composites references are nested and will be deleted when writing to file
|
|
244
|
-
|
|
248
|
+
|
|
245
249
|
const targetRecordCount = element.is2many ? 2 : 1 // TODO: make this configurable
|
|
246
250
|
const targetRecordID = randomNumber()
|
|
247
251
|
for (let i = 0; i < targetRecordCount; i++) {
|
|
248
|
-
const newRecord = fillRecord({}, targetRecordID+i, target, csn, data,
|
|
252
|
+
const newRecord = fillRecord({}, targetRecordID+i, target, csn, data, artifact, depth+1, referenceData)
|
|
249
253
|
if (newRecord) targetRecords.push(newRecord)
|
|
250
254
|
}
|
|
251
255
|
|
|
@@ -259,8 +263,8 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
259
263
|
}
|
|
260
264
|
uniqRecords(targetRecords, target)
|
|
261
265
|
|
|
262
|
-
//
|
|
263
|
-
return element.is2many ? targetRecords : targetRecords[0]
|
|
266
|
+
// consistent return data type: array
|
|
267
|
+
return element.is2many ? targetRecords : [targetRecords[0]]
|
|
264
268
|
}
|
|
265
269
|
case 'cds.Association': {
|
|
266
270
|
if (element.is2many)
|
|
@@ -280,16 +284,16 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
280
284
|
targetRecords = data[target.name] = []
|
|
281
285
|
else if (!targetRecords) // target entity is not in the list of entities to be created
|
|
282
286
|
return
|
|
283
|
-
|
|
287
|
+
|
|
284
288
|
if (targetRecords.length > 0) { // use record generated by following asociation
|
|
285
289
|
targetRecord = targetRecords.at(-1) // make sure the localized record references the newly generated texts
|
|
286
290
|
} else {
|
|
287
|
-
targetRecord = fillRecord({}, randomNumber(), target, csn, data,
|
|
291
|
+
targetRecord = fillRecord({}, randomNumber(), target, csn, data, artifact, depth+1, referenceData)
|
|
288
292
|
if (targetRecord) {
|
|
289
293
|
targetRecords.push(targetRecord)
|
|
290
294
|
uniqRecords(targetRecords, target)
|
|
291
295
|
}
|
|
292
|
-
if (
|
|
296
|
+
if (parent && (parent.name != artifact.name)) data[target.name].keepReferece = true
|
|
293
297
|
}
|
|
294
298
|
}
|
|
295
299
|
|
|
@@ -308,7 +312,7 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
308
312
|
case 'cds.Vector': // TODO add Vector
|
|
309
313
|
return
|
|
310
314
|
default: {
|
|
311
|
-
console.error(`Unknown type ${element._type} (${element.type}) in '${
|
|
315
|
+
console.error(`Unknown type ${element._type} (${element.type}) in '${artifact.name}:${element.name}'`)
|
|
312
316
|
return
|
|
313
317
|
}
|
|
314
318
|
}
|
|
@@ -393,14 +397,14 @@ function findKeysInOnCondition(assocElement, target) {
|
|
|
393
397
|
return [ownKey, ownKeyStructured, targetKey]
|
|
394
398
|
}
|
|
395
399
|
|
|
396
|
-
function uniqRecords(records,
|
|
397
|
-
const keys = Object.keys(
|
|
398
|
-
if (keys.length === 0)
|
|
400
|
+
function uniqRecords(records, artifact) {
|
|
401
|
+
const keys = Object.keys(artifact.keys||{})
|
|
402
|
+
if (keys.length === 0) keys.push('$id') // no keys (e.g. in actions) -> use record's $id
|
|
399
403
|
const keySet = new Set()
|
|
400
404
|
for (let i = 0; i < records.length; i++) {
|
|
401
405
|
const key = keys.map(k => records[i][k]).join('|')
|
|
402
406
|
if (keySet.has(key)) {
|
|
403
|
-
// console.debug('Removing', keys, key, 'from',
|
|
407
|
+
// console.debug('Removing', keys, key, 'from', artifact.name)
|
|
404
408
|
records.splice(i, 1)
|
|
405
409
|
i--
|
|
406
410
|
}
|
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@cap-js/hana": "^1"
|
|
5
5
|
},
|
|
6
|
-
{{/isNodejs}}
|
|
7
|
-
{{#isNodejs}}
|
|
8
6
|
"cds": {
|
|
9
|
-
{{/isNodejs}}
|
|
10
7
|
"sql": {
|
|
11
8
|
"native_hana_associations": false
|
|
12
9
|
}{{#profile}},
|
|
@@ -22,7 +19,24 @@
|
|
|
22
19
|
{{/shortcut}}
|
|
23
20
|
}
|
|
24
21
|
}{{/profile}}
|
|
25
|
-
{{#isNodejs}}
|
|
26
22
|
}
|
|
27
23
|
{{/isNodejs}}
|
|
24
|
+
{{#isJava}}
|
|
25
|
+
"requires": {
|
|
26
|
+
{{#profile}}
|
|
27
|
+
"[{{profile}}]": {
|
|
28
|
+
{{/profile}}
|
|
29
|
+
{{#shortcut}}
|
|
30
|
+
"db": "hana"
|
|
31
|
+
{{/shortcut}}
|
|
32
|
+
{{^shortcut}}
|
|
33
|
+
"db": {
|
|
34
|
+
"kind": "hana"
|
|
35
|
+
}
|
|
36
|
+
{{/shortcut}}
|
|
37
|
+
{{#profile}}
|
|
38
|
+
}
|
|
39
|
+
{{/profile}}
|
|
40
|
+
}
|
|
41
|
+
{{/isJava}}
|
|
28
42
|
}
|
|
@@ -29,12 +29,14 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
29
29
|
'for-app': {
|
|
30
30
|
type: 'string',
|
|
31
31
|
short: 'a',
|
|
32
|
-
help:
|
|
32
|
+
help: `Specify the name of the app to generate requests for.
|
|
33
|
+
If not specified, localhost and default auth will be used.`
|
|
33
34
|
},
|
|
34
35
|
'out': {
|
|
35
36
|
type: 'string',
|
|
36
37
|
short: 'o',
|
|
37
|
-
help:
|
|
38
|
+
help: `The output directory.
|
|
39
|
+
By default, an \`http\` dir is created in either \`test/\`, \`tests/\`, \`__tests__/\`, or at the root level.`
|
|
38
40
|
},
|
|
39
41
|
'dry': {
|
|
40
42
|
type: 'boolean',
|
|
@@ -130,9 +132,12 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
130
132
|
for (const e of service.entities) {
|
|
131
133
|
payload[e.name] = []
|
|
132
134
|
}
|
|
135
|
+
for (const a of service.actions) {
|
|
136
|
+
payload[a.name] = []
|
|
137
|
+
}
|
|
133
138
|
}
|
|
134
139
|
// csn.all('entity').reduce((all, e) => { all[e.name] = []; return all }, {})
|
|
135
|
-
await asJson(payload, csn, 1)
|
|
140
|
+
await asJson(payload, csn, 1, {referenceData: []})
|
|
136
141
|
for (const p in payload) {
|
|
137
142
|
delete payload[p][0].texts
|
|
138
143
|
delete payload[p][0].localized
|
|
@@ -176,11 +181,11 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
176
181
|
else {
|
|
177
182
|
const postBody = JSON.stringify(entityData, null, 2)
|
|
178
183
|
let id
|
|
179
|
-
const compositeKey = Object.keys(e.keys).length > 1
|
|
184
|
+
const compositeKey = Object.keys(e.keys||{}).length > 1
|
|
180
185
|
if (compositeKey) {
|
|
181
186
|
id = [...e.keys].map(k => k.type === 'cds.String' ? `${k.name}='${entityData[k.name]}'` : `${k.name}=${entityData[k.name]}`).join(',')
|
|
182
187
|
} else {
|
|
183
|
-
id = entityData[Object.keys(e.keys)[0]]
|
|
188
|
+
id = entityData[Object.keys(e.keys||{})[0]]
|
|
184
189
|
}
|
|
185
190
|
compositeKey ? id = `(${id})` : id = `/${id}`
|
|
186
191
|
requests.push(`${requestTitle}\n# @name ${nameSlug}_POST\nPOST ${url}\n${headers}\n\n${postBody}\n`)
|
|
@@ -195,12 +200,8 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
195
200
|
|
|
196
201
|
let actionPayload = {}
|
|
197
202
|
if (a.params) {
|
|
198
|
-
for (const
|
|
199
|
-
actionPayload[
|
|
200
|
-
: value.type === 'string' ? 'sample string'
|
|
201
|
-
// TODO reference an object that has been generated
|
|
202
|
-
: typeof value.type === 'object' && value.type.ref ? payload[value.type.ref[0]][0][value.type.ref[1]]
|
|
203
|
-
: null
|
|
203
|
+
for (const pName of Object.keys(a.params)) {
|
|
204
|
+
actionPayload[pName] = payload[a.name]?.[0]?.[pName] ?? null
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
requests.push(`\n### ${simpleNameAction}\n# @name ${simpleNameAction}_POST\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
|
|
@@ -24,7 +24,6 @@ module.exports = class TyperTemplate extends require('../../plugin') {
|
|
|
24
24
|
let target = 'jsconfig.json'
|
|
25
25
|
|
|
26
26
|
if (exists('tsconfig.json')) {
|
|
27
|
-
source.compilerOptions.paths["#cds-models/*"][0] += '/index.ts'
|
|
28
27
|
target = 'tsconfig.json'
|
|
29
28
|
}
|
|
30
29
|
await merge(template('package.json')).into('package.json')
|
package/lib/mtx/push.js
CHANGED
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-dk",
|
|
3
|
-
"version": "8.4.
|
|
3
|
+
"version": "8.4.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sap/cds-dk",
|
|
9
|
-
"version": "8.4.
|
|
9
|
+
"version": "8.4.2",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@cap-js/asyncapi": "^1.0.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"node_modules/@cap-js/db-service": {
|
|
46
|
-
"version": "1.
|
|
46
|
+
"version": "1.15.0",
|
|
47
47
|
"license": "SEE LICENSE",
|
|
48
48
|
"optional": true,
|
|
49
49
|
"dependencies": {
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"node_modules/@cap-js/sqlite": {
|
|
67
|
-
"version": "1.7.
|
|
67
|
+
"version": "1.7.7",
|
|
68
68
|
"license": "SEE LICENSE",
|
|
69
69
|
"optional": true,
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@cap-js/db-service": "^1.
|
|
71
|
+
"@cap-js/db-service": "^1.14.1",
|
|
72
72
|
"better-sqlite3": "^11.0.0"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
}
|
|
150
150
|
},
|
|
151
151
|
"node_modules/@eslint/js": {
|
|
152
|
-
"version": "9.
|
|
152
|
+
"version": "9.14.0",
|
|
153
153
|
"license": "MIT",
|
|
154
154
|
"engines": {
|
|
155
155
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
@@ -190,6 +190,17 @@
|
|
|
190
190
|
"node": ">=18.18.0"
|
|
191
191
|
}
|
|
192
192
|
},
|
|
193
|
+
"node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
|
|
194
|
+
"version": "0.3.1",
|
|
195
|
+
"license": "Apache-2.0",
|
|
196
|
+
"engines": {
|
|
197
|
+
"node": ">=18.18"
|
|
198
|
+
},
|
|
199
|
+
"funding": {
|
|
200
|
+
"type": "github",
|
|
201
|
+
"url": "https://github.com/sponsors/nzakas"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
193
204
|
"node_modules/@humanwhocodes/module-importer": {
|
|
194
205
|
"version": "1.0.1",
|
|
195
206
|
"license": "Apache-2.0",
|
|
@@ -202,7 +213,7 @@
|
|
|
202
213
|
}
|
|
203
214
|
},
|
|
204
215
|
"node_modules/@humanwhocodes/retry": {
|
|
205
|
-
"version": "0.
|
|
216
|
+
"version": "0.4.1",
|
|
206
217
|
"license": "Apache-2.0",
|
|
207
218
|
"engines": {
|
|
208
219
|
"node": ">=18.18"
|
|
@@ -213,7 +224,7 @@
|
|
|
213
224
|
}
|
|
214
225
|
},
|
|
215
226
|
"node_modules/@sap/cds": {
|
|
216
|
-
"version": "8.4.
|
|
227
|
+
"version": "8.4.2",
|
|
217
228
|
"license": "SEE LICENSE IN LICENSE",
|
|
218
229
|
"dependencies": {
|
|
219
230
|
"@sap/cds-compiler": ">=5.1",
|
|
@@ -239,7 +250,7 @@
|
|
|
239
250
|
}
|
|
240
251
|
},
|
|
241
252
|
"node_modules/@sap/cds-compiler": {
|
|
242
|
-
"version": "5.4.
|
|
253
|
+
"version": "5.4.4",
|
|
243
254
|
"license": "SEE LICENSE IN LICENSE",
|
|
244
255
|
"dependencies": {
|
|
245
256
|
"antlr4": "4.9.3"
|
|
@@ -287,7 +298,7 @@
|
|
|
287
298
|
}
|
|
288
299
|
},
|
|
289
300
|
"node_modules/@sap/eslint-plugin-cds": {
|
|
290
|
-
"version": "3.1.
|
|
301
|
+
"version": "3.1.2",
|
|
291
302
|
"license": "See LICENSE file",
|
|
292
303
|
"dependencies": {
|
|
293
304
|
"@sap/cds": ">=7",
|
|
@@ -717,7 +728,7 @@
|
|
|
717
728
|
"license": "MIT"
|
|
718
729
|
},
|
|
719
730
|
"node_modules/cross-spawn": {
|
|
720
|
-
"version": "7.0.
|
|
731
|
+
"version": "7.0.5",
|
|
721
732
|
"license": "MIT",
|
|
722
733
|
"dependencies": {
|
|
723
734
|
"path-key": "^3.1.0",
|
|
@@ -875,19 +886,19 @@
|
|
|
875
886
|
}
|
|
876
887
|
},
|
|
877
888
|
"node_modules/eslint": {
|
|
878
|
-
"version": "9.
|
|
889
|
+
"version": "9.14.0",
|
|
879
890
|
"license": "MIT",
|
|
880
891
|
"dependencies": {
|
|
881
892
|
"@eslint-community/eslint-utils": "^4.2.0",
|
|
882
|
-
"@eslint-community/regexpp": "^4.
|
|
893
|
+
"@eslint-community/regexpp": "^4.12.1",
|
|
883
894
|
"@eslint/config-array": "^0.18.0",
|
|
884
895
|
"@eslint/core": "^0.7.0",
|
|
885
896
|
"@eslint/eslintrc": "^3.1.0",
|
|
886
|
-
"@eslint/js": "9.
|
|
897
|
+
"@eslint/js": "9.14.0",
|
|
887
898
|
"@eslint/plugin-kit": "^0.2.0",
|
|
888
|
-
"@humanfs/node": "^0.16.
|
|
899
|
+
"@humanfs/node": "^0.16.6",
|
|
889
900
|
"@humanwhocodes/module-importer": "^1.0.1",
|
|
890
|
-
"@humanwhocodes/retry": "^0.
|
|
901
|
+
"@humanwhocodes/retry": "^0.4.0",
|
|
891
902
|
"@types/estree": "^1.0.6",
|
|
892
903
|
"@types/json-schema": "^7.0.15",
|
|
893
904
|
"ajv": "^6.12.4",
|
|
@@ -895,9 +906,9 @@
|
|
|
895
906
|
"cross-spawn": "^7.0.2",
|
|
896
907
|
"debug": "^4.3.2",
|
|
897
908
|
"escape-string-regexp": "^4.0.0",
|
|
898
|
-
"eslint-scope": "^8.
|
|
899
|
-
"eslint-visitor-keys": "^4.
|
|
900
|
-
"espree": "^10.
|
|
909
|
+
"eslint-scope": "^8.2.0",
|
|
910
|
+
"eslint-visitor-keys": "^4.2.0",
|
|
911
|
+
"espree": "^10.3.0",
|
|
901
912
|
"esquery": "^1.5.0",
|
|
902
913
|
"esutils": "^2.0.2",
|
|
903
914
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -933,7 +944,7 @@
|
|
|
933
944
|
}
|
|
934
945
|
},
|
|
935
946
|
"node_modules/eslint-scope": {
|
|
936
|
-
"version": "8.
|
|
947
|
+
"version": "8.2.0",
|
|
937
948
|
"license": "BSD-2-Clause",
|
|
938
949
|
"dependencies": {
|
|
939
950
|
"esrecurse": "^4.3.0",
|
|
@@ -947,7 +958,7 @@
|
|
|
947
958
|
}
|
|
948
959
|
},
|
|
949
960
|
"node_modules/eslint-visitor-keys": {
|
|
950
|
-
"version": "4.
|
|
961
|
+
"version": "4.2.0",
|
|
951
962
|
"license": "Apache-2.0",
|
|
952
963
|
"engines": {
|
|
953
964
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
@@ -957,12 +968,12 @@
|
|
|
957
968
|
}
|
|
958
969
|
},
|
|
959
970
|
"node_modules/espree": {
|
|
960
|
-
"version": "10.
|
|
971
|
+
"version": "10.3.0",
|
|
961
972
|
"license": "BSD-2-Clause",
|
|
962
973
|
"dependencies": {
|
|
963
|
-
"acorn": "^8.
|
|
974
|
+
"acorn": "^8.14.0",
|
|
964
975
|
"acorn-jsx": "^5.3.2",
|
|
965
|
-
"eslint-visitor-keys": "^4.
|
|
976
|
+
"eslint-visitor-keys": "^4.2.0"
|
|
966
977
|
},
|
|
967
978
|
"engines": {
|
|
968
979
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
@@ -1690,7 +1701,7 @@
|
|
|
1690
1701
|
}
|
|
1691
1702
|
},
|
|
1692
1703
|
"node_modules/object-inspect": {
|
|
1693
|
-
"version": "1.13.
|
|
1704
|
+
"version": "1.13.3",
|
|
1694
1705
|
"license": "MIT",
|
|
1695
1706
|
"engines": {
|
|
1696
1707
|
"node": ">= 0.4"
|