@sap/cds-dk 8.3.0 → 8.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -13
- package/bin/build.js +1 -1
- package/bin/cds.js +12 -0
- package/bin/compile/index.js +12 -2
- package/bin/deploy/index.js +6 -1
- package/bin/env.js +3 -2
- package/bin/init.js +8 -4
- package/bin/repl.js +3 -3
- package/lib/build/util.js +32 -20
- package/lib/compile/to-xsuaa.js +39 -26
- package/lib/deploy/to-hana/hana.js +62 -74
- package/lib/env/schemas/cds-rc.js +174 -2
- package/lib/import/asyncapi/parserContext.js +4 -30
- package/lib/import/odata/v2parser.js +3 -2
- package/lib/import/openapi/importOpenAPI.js +616 -569
- package/lib/import/openapi/utilities.js +7 -2
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +4 -0
- package/lib/init/projectReader.js +1 -2
- package/lib/init/registries/mta.js +8 -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 +2 -2
- package/lib/init/template/cf-manifest/files/manifest.yml.hbs +3 -1
- package/lib/init/template/data/as-csv.js +1 -1
- package/lib/init/template/data/as-json.js +46 -42
- package/lib/init/template/enterprise-messaging-shared/index.js +1 -1
- package/lib/init/template/file-based-messaging/index.js +1 -1
- package/lib/init/template/hana/files/db/package.json +1 -1
- package/lib/init/template/hana/files/package.json.hbs +2 -0
- package/lib/init/template/hana/index.js +1 -1
- package/lib/init/template/handler/index.js +1 -1
- package/lib/init/template/html5-repo/files/app-package.json.hbs +2 -2
- package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +2 -2
- package/lib/init/template/http/index.js +6 -7
- package/lib/init/template/local-messaging/index.js +1 -1
- package/lib/init/template/nodejs/files/.vscode/extensions.json +0 -5
- package/lib/init/template/nodejs/files/.vscode/settings.json +1 -1
- package/lib/init/template/nodejs/files/.vscode/tasks.json +0 -2
- package/lib/init/template/nodejs/files/package.json.hbs +1 -1
- package/lib/init/template/postgres/index.js +1 -1
- package/lib/init/template/redis-messaging/index.js +1 -1
- package/lib/init/template/side-by-side-extensibility/files/mta.yaml.hbs +40 -0
- package/lib/init/template/side-by-side-extensibility/files/package.json.hbs +17 -0
- package/lib/init/template/side-by-side-extensibility/index.js +34 -0
- package/lib/init/template/typer/index.js +0 -1
- package/lib/init/template/typescript/files/package.json +2 -2
- package/lib/init/template/xsuaa/index.js +1 -1
- package/lib/mtx/push.js +0 -1
- package/lib/util/fs.js +1 -1
- package/lib/util/jsonc.js +1 -1
- package/lib/util/jsonc.pegjs +6 -2
- package/npm-shrinkwrap.json +74 -151
- package/package.json +1 -1
|
@@ -2,8 +2,13 @@ module.exports = { cdsName, nameFromPath, pathAndMethod, serviceName };
|
|
|
2
2
|
|
|
3
3
|
function cdsName(oasName) {
|
|
4
4
|
if (oasName === "") return "_";
|
|
5
|
+
if (typeof oasName === "number") return `_${JSON.stringify(oasName)}`;
|
|
5
6
|
let name = oasName;
|
|
6
|
-
if (
|
|
7
|
+
if (
|
|
8
|
+
!name.startsWith("_") &&
|
|
9
|
+
name.match(/^(\p{Nd}|\p{Mn}|\p{Mc}|\p{Pc}|\p{Cf})/u)
|
|
10
|
+
)
|
|
11
|
+
name = "_" + name;
|
|
7
12
|
return name.replace(/[^_\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_");
|
|
8
13
|
//TODO: shorten if longer than 128 characters
|
|
9
14
|
}
|
|
@@ -15,7 +20,7 @@ function nameFromPath(path, method) {
|
|
|
15
20
|
.replace(/{[^}]+}/g, ".")
|
|
16
21
|
.replace(/\/\./g, "_")
|
|
17
22
|
.replace(/[^_\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_");
|
|
18
|
-
if (cleanPath === "") cleanPath = "
|
|
23
|
+
if (cleanPath === "") cleanPath = "_root";
|
|
19
24
|
//TODO: shorten if longer than 128 characters
|
|
20
25
|
return method === "get" ? cleanPath : `${cleanPath}_${method}`;
|
|
21
26
|
}
|
package/lib/init/constants.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = {
|
|
|
5
5
|
COMMAND_ADD: 'add',
|
|
6
6
|
|
|
7
7
|
JAVA_LTS_VERSIONS: [17, 21],
|
|
8
|
-
MAVEN_ARCHETYPE_VERSION: '3.
|
|
8
|
+
MAVEN_ARCHETYPE_VERSION: '3.4.0',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -24,6 +24,7 @@ module.exports = {
|
|
|
24
24
|
MULTITENANCY: 'multitenancy',
|
|
25
25
|
TOGGLES: 'toggles',
|
|
26
26
|
EXTENSIBILITY: 'extensibility',
|
|
27
|
+
SIDE_BY_SIDE_EXTENSIBILITY: 'side-by-side-extensibility',
|
|
27
28
|
MTX: 'mtx',
|
|
28
29
|
XSUAA: 'xsuaa',
|
|
29
30
|
HANA: 'hana',
|
package/lib/init/index.js
CHANGED
|
@@ -265,6 +265,10 @@ module.exports = class CDSGenerator {
|
|
|
265
265
|
// set is ordered ... always
|
|
266
266
|
options.add = new Set(trimmedTokens);
|
|
267
267
|
|
|
268
|
+
if (options?.java) { // --java -> --add java
|
|
269
|
+
options.add.add('java')
|
|
270
|
+
}
|
|
271
|
+
|
|
268
272
|
if (cds.cli.command === COMMAND_INIT && !options.add.has(JAVA)) {
|
|
269
273
|
options.add.add(NODEJS);
|
|
270
274
|
}
|
|
@@ -89,7 +89,7 @@ module.exports = new class ProjectReader {
|
|
|
89
89
|
* @property {boolean} hasApprouter Indicates if the project uses the SAP Application Router.
|
|
90
90
|
* @property {boolean} hasHtml5Repo Indicates if the project uses the SAP BTP HTML5 Application Repository.
|
|
91
91
|
* @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal service.
|
|
92
|
-
* @property {boolean}
|
|
92
|
+
* @property {boolean} hasWorkzoneStandard Indicates if the project uses the SAP Build Work Zone, Standard Edition service.
|
|
93
93
|
* @property {boolean} hasDestination Indicates if the project uses the SAP BTP Destination service.
|
|
94
94
|
* @property {boolean} hasMTXRoute Indicates if the approuter should have a multitenant route.
|
|
95
95
|
* @property {boolean} hasMTXRouteJava Indicates if the approuter should have a multitenant subscription route (java).
|
|
@@ -118,7 +118,6 @@ module.exports = new class ProjectReader {
|
|
|
118
118
|
const isApp = name => name !== 'appconfig' && !name.startsWith('_') && name !== 'router' && name !== 'portal'
|
|
119
119
|
const reserved = {
|
|
120
120
|
has: x => _inProd(x),
|
|
121
|
-
shortcut: () => true,
|
|
122
121
|
profile: () => cds.cli.options?.for,
|
|
123
122
|
language: () => exists('pom.xml') ? 'java' : 'nodejs',
|
|
124
123
|
isJava: () => _isJava(),
|
|
@@ -5,7 +5,8 @@ const srvNode4 = path => ({ in: 'modules', where: { type: 'nodejs', path } })
|
|
|
5
5
|
const srvJava4 = path => ({ in: 'modules', where: { type: 'java', path } })
|
|
6
6
|
|
|
7
7
|
// REVISIT: Clean up
|
|
8
|
-
const isJava = process.argv[process.argv.indexOf('--add') + 1].split(
|
|
8
|
+
const isJava = process.argv[process.argv.indexOf('--add') + 1].split(/[,\s+]/).includes('java') || cds.env['project-nature'] === 'java' || cds.cli.options?.add?.has('java') || process.argv.includes('--java')
|
|
9
|
+
|
|
9
10
|
const srv4 = isJava ? srvJava4 : srvNode4
|
|
10
11
|
|
|
11
12
|
const approuter = { in: 'modules', where: { type: 'approuter.nodejs' } }
|
|
@@ -25,6 +26,11 @@ const destinations = {
|
|
|
25
26
|
where: { type: 'com.sap.application.content', 'build-parameters.no-source': true }
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
const destinationSideBySideExtensibility = {
|
|
30
|
+
in: [destinations, 'parameters.content.subaccount.destinations' ],
|
|
31
|
+
where: { URL: '~{srv-api/srv-url}', Authentication: 'OAuth2ClientCredentials' }
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
const connectivity = {
|
|
29
35
|
in: 'resources',
|
|
30
36
|
where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'connectivity' }
|
|
@@ -186,6 +192,7 @@ module.exports = {
|
|
|
186
192
|
xsuaa,
|
|
187
193
|
destination,
|
|
188
194
|
destinations,
|
|
195
|
+
destinationSideBySideExtensibility,
|
|
189
196
|
connectivity,
|
|
190
197
|
enterpriseMessaging,
|
|
191
198
|
kafkaMessaging,
|
|
@@ -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() {
|
|
@@ -20,7 +20,9 @@ applications:
|
|
|
20
20
|
disk_quota: 512M
|
|
21
21
|
env:
|
|
22
22
|
SPRING_PROFILES_ACTIVE: cloud
|
|
23
|
-
|
|
23
|
+
JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jdk.SAPMachineJDK']"
|
|
24
|
+
JBP_CONFIG_SAP_MACHINE_JDK: '{ version: 21.+ }'
|
|
25
|
+
buildpack: sap_java_buildpack_jakarta
|
|
24
26
|
{{/isJava}}
|
|
25
27
|
services:
|
|
26
28
|
{{#hasHana}}
|
|
@@ -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
|
}
|
|
@@ -237,15 +239,15 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
237
239
|
}
|
|
238
240
|
case 'cds.Composition': {
|
|
239
241
|
const target = csn.definitions[element.target]
|
|
240
|
-
if (!target ||
|
|
241
|
-
|
|
242
|
+
if (!target || artifact.name === target.name || depth > MAX_ASSOC_DEPTH) return
|
|
243
|
+
|
|
242
244
|
let targetRecords = [] // in findKeysInOnCondition target records are linked to the entity, we don't want to overwrite any existing data
|
|
243
245
|
if (data[target.name]) data[target.name].deleteReference = true // for json composites references are nested and will be deleted when writing to file
|
|
244
|
-
|
|
246
|
+
|
|
245
247
|
const targetRecordCount = element.is2many ? 2 : 1 // TODO: make this configurable
|
|
246
248
|
const targetRecordID = randomNumber()
|
|
247
249
|
for (let i = 0; i < targetRecordCount; i++) {
|
|
248
|
-
const newRecord = fillRecord({}, targetRecordID+i, target, csn, data,
|
|
250
|
+
const newRecord = fillRecord({}, targetRecordID+i, target, csn, data, artifact, depth+1, referenceData)
|
|
249
251
|
if (newRecord) targetRecords.push(newRecord)
|
|
250
252
|
}
|
|
251
253
|
|
|
@@ -259,8 +261,8 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
259
261
|
}
|
|
260
262
|
uniqRecords(targetRecords, target)
|
|
261
263
|
|
|
262
|
-
//
|
|
263
|
-
return element.is2many ? targetRecords : targetRecords[0]
|
|
264
|
+
// consistent return data type: array
|
|
265
|
+
return element.is2many ? targetRecords : [targetRecords[0]]
|
|
264
266
|
}
|
|
265
267
|
case 'cds.Association': {
|
|
266
268
|
if (element.is2many)
|
|
@@ -280,16 +282,16 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
280
282
|
targetRecords = data[target.name] = []
|
|
281
283
|
else if (!targetRecords) // target entity is not in the list of entities to be created
|
|
282
284
|
return
|
|
283
|
-
|
|
285
|
+
|
|
284
286
|
if (targetRecords.length > 0) { // use record generated by following asociation
|
|
285
287
|
targetRecord = targetRecords.at(-1) // make sure the localized record references the newly generated texts
|
|
286
288
|
} else {
|
|
287
|
-
targetRecord = fillRecord({}, randomNumber(), target, csn, data,
|
|
289
|
+
targetRecord = fillRecord({}, randomNumber(), target, csn, data, artifact, depth+1, referenceData)
|
|
288
290
|
if (targetRecord) {
|
|
289
291
|
targetRecords.push(targetRecord)
|
|
290
292
|
uniqRecords(targetRecords, target)
|
|
291
293
|
}
|
|
292
|
-
if (
|
|
294
|
+
if (parent && (parent.name != artifact.name)) data[target.name].keepReferece = true
|
|
293
295
|
}
|
|
294
296
|
}
|
|
295
297
|
|
|
@@ -307,8 +309,10 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
307
309
|
return
|
|
308
310
|
case 'cds.Vector': // TODO add Vector
|
|
309
311
|
return
|
|
312
|
+
case 'cds.Map':
|
|
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
|
}
|
|
@@ -34,7 +34,7 @@ module.exports = class EnterpriseMessagingSharedTemplate extends require('../../
|
|
|
34
34
|
const project = readProject()
|
|
35
35
|
const { configFile, isNodejs } = project
|
|
36
36
|
if (isNodejs) {
|
|
37
|
-
project.shortcut =
|
|
37
|
+
project.shortcut = typeof (await read(configFile)).cds?.requires?.messaging !== 'object'
|
|
38
38
|
project.cloudevents = cds.cli.options.cloudevents
|
|
39
39
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
40
40
|
}
|
|
@@ -21,7 +21,7 @@ module.exports = class FileBasedMessagingTemplate extends require('../../plugin'
|
|
|
21
21
|
async run() {
|
|
22
22
|
const project = readProject()
|
|
23
23
|
const { configFile } = project
|
|
24
|
-
project.shortcut =
|
|
24
|
+
project.shortcut = typeof (await read(configFile)).cds?.requires?.messaging !== 'object'
|
|
25
25
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -20,7 +20,7 @@ module.exports = class HanaTemplate extends require('../../plugin') {
|
|
|
20
20
|
async run() {
|
|
21
21
|
const project = readProject()
|
|
22
22
|
const { isJava, configFile, db } = project
|
|
23
|
-
project.shortcut =
|
|
23
|
+
project.shortcut = typeof (await read(configFile)).cds?.requires?.db !== 'object'
|
|
24
24
|
|
|
25
25
|
// REVISIT: Let cds build generate those by default
|
|
26
26
|
await merge(__dirname, 'files/src/.hdiconfig').into(join(db.folder, 'src/.hdiconfig'))
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"start": "ui5 serve"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
{{#
|
|
11
|
+
{{#hasWorkzoneStandard}}"@sap/ui5-builder-webide-extension": "^1.1.9",{{/hasWorkzoneStandard}}
|
|
12
12
|
"@ui5/cli": "^4",
|
|
13
13
|
"ui5-task-zipper": "^3"
|
|
14
14
|
},
|
|
15
15
|
"ui5": {
|
|
16
16
|
"dependencies": [
|
|
17
|
-
{{#
|
|
17
|
+
{{#hasWorkzoneStandard}}"@sap/ui5-builder-webide-extension",{{/hasWorkzoneStandard}}
|
|
18
18
|
"ui5-task-zipper"
|
|
19
19
|
]
|
|
20
20
|
}
|
|
@@ -13,13 +13,13 @@ builder:
|
|
|
13
13
|
- "/test/**"
|
|
14
14
|
- "/localService/**"
|
|
15
15
|
customTasks:
|
|
16
|
-
{{#
|
|
16
|
+
{{#hasWorkzoneStandard}}
|
|
17
17
|
- name: webide-extension-task-updateManifestJson
|
|
18
18
|
afterTask: replaceVersion
|
|
19
19
|
configuration:
|
|
20
20
|
appFolder: webapp
|
|
21
21
|
destDir: dist
|
|
22
|
-
{{/
|
|
22
|
+
{{/hasWorkzoneStandard}}
|
|
23
23
|
- name: ui5-task-zipper
|
|
24
24
|
afterTask: generateVersionInfo
|
|
25
25
|
configuration:
|
|
@@ -130,9 +130,12 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
130
130
|
for (const e of service.entities) {
|
|
131
131
|
payload[e.name] = []
|
|
132
132
|
}
|
|
133
|
+
for (const a of service.actions) {
|
|
134
|
+
payload[a.name] = []
|
|
135
|
+
}
|
|
133
136
|
}
|
|
134
137
|
// csn.all('entity').reduce((all, e) => { all[e.name] = []; return all }, {})
|
|
135
|
-
await asJson(payload, csn, 1)
|
|
138
|
+
await asJson(payload, csn, 1, {referenceData: []})
|
|
136
139
|
for (const p in payload) {
|
|
137
140
|
delete payload[p][0].texts
|
|
138
141
|
delete payload[p][0].localized
|
|
@@ -195,12 +198,8 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
195
198
|
|
|
196
199
|
let actionPayload = {}
|
|
197
200
|
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
|
|
201
|
+
for (const pName of Object.keys(a.params)) {
|
|
202
|
+
actionPayload[pName] = payload[a.name]?.[0]?.[pName] ?? null
|
|
204
203
|
}
|
|
205
204
|
}
|
|
206
205
|
requests.push(`\n### ${simpleNameAction}\n# @name ${simpleNameAction}_POST\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
|
|
@@ -22,7 +22,7 @@ module.exports = class LocalMessagingTemplate extends require('../../plugin') {
|
|
|
22
22
|
async run() {
|
|
23
23
|
const project = readProject()
|
|
24
24
|
const { configFile } = project
|
|
25
|
-
project.shortcut =
|
|
25
|
+
project.shortcut = typeof (await read(configFile)).cds?.requires?.messaging !== 'object'
|
|
26
26
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
|
3
|
-
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
|
4
|
-
|
|
5
|
-
// List of extensions which should be recommended for users of this workspace.
|
|
6
2
|
"recommendations": [
|
|
7
3
|
"SAPSE.vscode-cds",
|
|
8
4
|
"dbaeumer.vscode-eslint",
|
|
@@ -11,7 +7,6 @@
|
|
|
11
7
|
"qwtel.sqlite-viewer",
|
|
12
8
|
"humao.rest-client"
|
|
13
9
|
],
|
|
14
|
-
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
|
15
10
|
"unwantedRecommendations": [
|
|
16
11
|
|
|
17
12
|
]
|
|
@@ -24,7 +24,7 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
|
|
|
24
24
|
async run() {
|
|
25
25
|
const project = readProject()
|
|
26
26
|
const { configFile, isJava } = project
|
|
27
|
-
project.shortcut =
|
|
27
|
+
project.shortcut = typeof (await read(configFile)).cds?.requires?.db !== 'object'
|
|
28
28
|
await merge(__dirname, 'files/package.json.hbs').into('package.json', { project })
|
|
29
29
|
if (isJava) await mvn.add('postgresql')
|
|
30
30
|
}
|
|
@@ -28,7 +28,7 @@ module.exports = class RedisMessagingTemplate extends require('../../plugin') {
|
|
|
28
28
|
async run() {
|
|
29
29
|
const project = readProject()
|
|
30
30
|
const { configFile } = project
|
|
31
|
-
project.shortcut =
|
|
31
|
+
project.shortcut = typeof (await read(configFile)).cds?.requires?.messaging !== 'object'
|
|
32
32
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { project })
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
modules:
|
|
3
|
+
- name: {{appName}}-srv
|
|
4
|
+
type: {{language}}
|
|
5
|
+
path: {{& srvPath}}
|
|
6
|
+
provides:
|
|
7
|
+
- name: srv-api # required by consumers of CAP services (e.g. approuter)
|
|
8
|
+
properties:
|
|
9
|
+
srv-url: ${default-url}
|
|
10
|
+
|
|
11
|
+
- name: {{appName}}-destinations
|
|
12
|
+
type: com.sap.application.content
|
|
13
|
+
requires:
|
|
14
|
+
- name: {{appName}}-auth
|
|
15
|
+
parameters:
|
|
16
|
+
service-key:
|
|
17
|
+
name: {{appName}}-auth-key
|
|
18
|
+
- name: srv-api
|
|
19
|
+
- name: {{appName}}-destination
|
|
20
|
+
parameters:
|
|
21
|
+
content-target: true
|
|
22
|
+
build-parameters:
|
|
23
|
+
no-source: true
|
|
24
|
+
parameters:
|
|
25
|
+
content:
|
|
26
|
+
subaccount:
|
|
27
|
+
existing_destinations_policy: update
|
|
28
|
+
destinations:
|
|
29
|
+
- Name: {{appName}}-side-by-side-extensibility
|
|
30
|
+
URL: ~{srv-api/srv-url}
|
|
31
|
+
Authentication: OAuth2ClientCredentials
|
|
32
|
+
TokenServiceInstanceName: {{appName}}-auth
|
|
33
|
+
TokenServiceKeyName: {{appName}}-auth-key
|
|
34
|
+
|
|
35
|
+
resources:
|
|
36
|
+
- name: {{appName}}-destination
|
|
37
|
+
type: org.cloudfoundry.managed-service
|
|
38
|
+
parameters:
|
|
39
|
+
service: destination
|
|
40
|
+
service-plan: lite
|