@sap/cds-dk 8.0.3 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/bin/bind.js +3 -3
- package/bin/cds.js +11 -15
- package/bin/init.js +3 -6
- package/bin/version.js +7 -6
- package/lib/build/buildTaskFactory.js +29 -12
- package/lib/build/constants.js +1 -1
- package/lib/build/provider/hana/index.js +5 -6
- package/lib/build/provider/internalBuildTaskProvider.js +2 -35
- package/lib/build/provider/mtx/resourcesTarBuilder.js +2 -2
- package/lib/build/provider/mtx-sidecar/index.js +10 -3
- package/lib/cds.js +1 -0
- package/lib/compile/to-mermaid.js +27 -12
- package/lib/compile/to_openapi.js +2 -1
- package/lib/deploy/to-hana/hana.js +10 -8
- package/lib/deploy/to-hana/hdiDeployUtil.js +10 -1
- package/lib/import/common.js +1 -1
- package/lib/import/message.js +2 -2
- package/lib/import/odata/v2parser.js +2 -2
- package/lib/init/bas.js +73 -32
- package/lib/init/constants.js +5 -2
- package/lib/init/index.js +6 -4
- package/lib/init/merge.js +5 -3
- 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/data/as-json.js +22 -12
- package/lib/init/template/data/index.js +1 -1
- package/lib/init/template/helm-unified-runtime/index.js +3 -1
- package/lib/init/template/http/index.js +37 -28
- package/lib/init/template/multitenancy/index.js +9 -31
- 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/sample/files/srv/cat-service.cds +5 -1
- package/lib/init/template/typescript/files/package.json +3 -3
- package/lib/init/template/workzone/index.js +0 -3
- package/lib/init/template/xsuaa/index.js +10 -3
- package/lib/mtx/push.js +3 -1
- package/npm-shrinkwrap.json +19 -16
- package/package.json +1 -1
|
@@ -355,7 +355,7 @@ function _extractAllFunctionImports(functionImports, parserContext) {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
function _replaceSpecialCharacters(text) {
|
|
358
|
-
return text.replace(/(?:\\[rn]|[\r\n]+)+/gm, "\\n").replace(/\s+/g, ' ').replace(/"/g, """).trim();
|
|
358
|
+
return text.replace(/\\/g, "\\\\").replace(/(?:\\[rn]|[\r\n]+)+/gm, "\\n").replace(/\s+/g, ' ').replace(/"/g, """).trim();
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
// to parse the documentation tag
|
|
@@ -972,7 +972,7 @@ function _parseEntityProperty(properties, parserContext) {
|
|
|
972
972
|
// Has more than one entities
|
|
973
973
|
for (let i = 0; i < properties.length; i++) {
|
|
974
974
|
propAttributes = properties[i]._attributes;
|
|
975
|
-
// if the property contains
|
|
975
|
+
// if the property contains documentation tag
|
|
976
976
|
if (properties[i] && properties[i].Documentation)
|
|
977
977
|
propAttributes.doc = _parseDocumentationTag(properties[i].Documentation);
|
|
978
978
|
retProperties[propAttributes.Name] = _getPropertyAttributes(propAttributes, parserContext);
|
package/lib/init/bas.js
CHANGED
|
@@ -5,8 +5,10 @@ const { bold } = require('../util/term')
|
|
|
5
5
|
const { OPTIONS } = require('./constants')
|
|
6
6
|
const {
|
|
7
7
|
NODEJS, JAVA,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
XSUAA, APPROUTER, CONNECTIVITY, DESTINATION, HTML5_REPO, PORTAL, APPLICATION_LOGGING, AUDIT_LOGGING, NOTIFICATIONS,
|
|
9
|
+
ENTERPRISE_MESSAGING, KAFKA, REDIS_MESSAGING,
|
|
10
|
+
HANA, POSTGRES,
|
|
11
|
+
MTA, HELM, CONTAINERIZE, MULTITENANCY, PIPELINE,
|
|
10
12
|
TINY_SAMPLE, SAMPLE
|
|
11
13
|
} = OPTIONS
|
|
12
14
|
const cwd = process.cwd()
|
|
@@ -17,75 +19,114 @@ module.exports = [{
|
|
|
17
19
|
default: '',
|
|
18
20
|
target: 'projectName',
|
|
19
21
|
validate: async projectName => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
try {
|
|
23
|
+
cds.root = path.resolve(cwd, projectName || '.')
|
|
24
|
+
validate.projectName(projectName)
|
|
25
|
+
validate.projectFolder(path.join(cwd, projectName), cwd)
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return e // BAS Yeoman generator requires error to be returned
|
|
28
|
+
}
|
|
29
|
+
return true
|
|
28
30
|
},
|
|
29
31
|
guiOptions: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
breadcrumb: 'Project Name',
|
|
33
|
+
hint: 'This name is restricted to certain characters. A valid project name is compatible across platforms.',
|
|
34
|
+
mandatory: true
|
|
32
35
|
}
|
|
33
36
|
}, {
|
|
34
37
|
title: 'Type',
|
|
35
38
|
description: `Select your runtime.`,
|
|
36
39
|
default: NODEJS,
|
|
37
40
|
choices: [
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
{ name: 'Node.js', value: NODEJS },
|
|
42
|
+
{ name: 'Java', value: JAVA }
|
|
40
43
|
],
|
|
41
44
|
multiple: false,
|
|
42
45
|
target: 'add',
|
|
43
46
|
guiOptions: {
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
breadcrumb: 'Runtime',
|
|
48
|
+
hint: `The preferred runtime can't be changed afterwards. To use a different runtime, create a new project.`,
|
|
49
|
+
mandatory: true
|
|
46
50
|
}
|
|
47
51
|
}, {
|
|
48
|
-
title: '
|
|
49
|
-
description: 'Choose
|
|
52
|
+
title: 'Databases',
|
|
53
|
+
description: 'Choose a database for your application.',
|
|
50
54
|
default: '',
|
|
51
55
|
choices: [
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{ name: 'User Authentication via XSUAA', value: XSUAA },
|
|
55
|
-
{ name: 'Application Logging', value: APPLICATION_LOGGING },
|
|
56
|
-
{ name: 'SAP BTP Event Mesh', value: ENTERPRISE_MESSAGING }
|
|
56
|
+
{ name: 'SAP HANA Cloud', value: HANA },
|
|
57
|
+
{ name: 'PostgreSQL', value: POSTGRES }
|
|
57
58
|
],
|
|
58
59
|
multiple: true,
|
|
59
60
|
target: 'add',
|
|
60
61
|
guiOptions: {
|
|
61
|
-
|
|
62
|
+
breadcrumb: 'Databases',
|
|
63
|
+
hint: `Choose any combination of features enriching the capabilities of your project. You can also add features later with ${bold('cds add')}`
|
|
62
64
|
}
|
|
63
65
|
}, {
|
|
64
66
|
title: 'Deployment',
|
|
65
67
|
description: 'Choose which way to deploy your project.',
|
|
66
68
|
default: '',
|
|
67
69
|
choices: [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
{ name: 'Cloud Foundry: MTA Deployment', value: MTA },
|
|
71
|
+
{ name: 'Kyma: Helm Deployment', value: HELM },
|
|
72
|
+
{ name: 'Kyma: Containerization using ctz CLI', value: CONTAINERIZE },
|
|
73
|
+
{ name: 'Multitenancy', value: MULTITENANCY },
|
|
74
|
+
{ name: 'CI/CD Pipeline Integration', value: PIPELINE }
|
|
75
|
+
],
|
|
76
|
+
multiple: true,
|
|
77
|
+
target: 'add',
|
|
78
|
+
guiOptions: {
|
|
79
|
+
breadcrumb: 'Deployment',
|
|
80
|
+
hint: `Choose any combination of features enriching the capabilities of your project. You can also add features later with ${bold('cds add')}`
|
|
81
|
+
}
|
|
82
|
+
}, {
|
|
83
|
+
title: 'Runtime Features',
|
|
84
|
+
description: 'Choose productive runtime capabilities for your application.',
|
|
85
|
+
default: '',
|
|
86
|
+
choices: [
|
|
87
|
+
{ name: 'SAP BTP Authorization and Trust Management Service (XSUAA)', value: XSUAA },
|
|
88
|
+
{ name: 'SAP BTP Application Router', value: APPROUTER },
|
|
89
|
+
{ name: 'SAP BTP Connectivity Service', value: CONNECTIVITY },
|
|
90
|
+
{ name: 'SAP BTP Destination Service', value: DESTINATION },
|
|
91
|
+
{ name: 'SAP BTP HTML5 Application Repository', value: HTML5_REPO },
|
|
92
|
+
{ name: 'SAP BTP Portal Service', value: PORTAL },
|
|
93
|
+
{ name: 'SAP BTP Application Logging Service', value: APPLICATION_LOGGING },
|
|
94
|
+
{ name: 'SAP BTP Audit Logging Service', value: AUDIT_LOGGING },
|
|
95
|
+
{ name: 'SAP BTP Notification Service', value: NOTIFICATIONS }
|
|
96
|
+
],
|
|
97
|
+
multiple: true,
|
|
98
|
+
target: 'add',
|
|
99
|
+
guiOptions: {
|
|
100
|
+
breadcrumb: 'Runtime Features',
|
|
101
|
+
hint: `Choose any combination of features enriching the capabilities of your project. You can also add features later with ${bold('cds add')}`
|
|
102
|
+
}
|
|
103
|
+
}, {
|
|
104
|
+
title: 'Messaging',
|
|
105
|
+
description: 'Choose messaging capabilities for your application.',
|
|
106
|
+
default: '',
|
|
107
|
+
choices: [
|
|
108
|
+
{ name: 'SAP BTP Event Mesh', value: ENTERPRISE_MESSAGING },
|
|
109
|
+
{ name: 'Apache Kafka', value: KAFKA },
|
|
110
|
+
{ name: 'Redis', value: REDIS_MESSAGING }
|
|
72
111
|
],
|
|
73
112
|
multiple: true,
|
|
74
113
|
target: 'add',
|
|
75
114
|
guiOptions: {
|
|
76
|
-
|
|
115
|
+
breadcrumb: 'Messaging',
|
|
116
|
+
hint: `Choose any combination of features enriching the capabilities of your project. You can also add features later with ${bold('cds add')}`
|
|
77
117
|
}
|
|
78
118
|
}, {
|
|
79
119
|
title: 'Samples',
|
|
80
120
|
description: `Add sample content to your project.`,
|
|
81
121
|
default: '',
|
|
82
122
|
choices: [
|
|
83
|
-
|
|
84
|
-
|
|
123
|
+
{ name: 'Minimal Sample', value: TINY_SAMPLE },
|
|
124
|
+
{ name: 'Extended Sample with UI', value: SAMPLE }
|
|
85
125
|
],
|
|
86
126
|
multiple: true,
|
|
87
127
|
target: 'add',
|
|
88
128
|
guiOptions: {
|
|
89
|
-
|
|
129
|
+
breadcrumb: 'Samples',
|
|
130
|
+
hint: 'Speed up your ramp-up phase with sample files in your project.'
|
|
90
131
|
}
|
|
91
132
|
}]
|
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.1.1',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -69,7 +69,10 @@ module.exports = {
|
|
|
69
69
|
SAMPLES: 'https://github.com/SAP-samples/cloud-cap-samples',
|
|
70
70
|
CAPIRE: 'https://cap.cloud.sap',
|
|
71
71
|
MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/developing-applications/building#the-maven-archetype`,
|
|
72
|
-
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html
|
|
72
|
+
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
|
|
73
|
+
CAP_JS_COMMUNITY_ODATA_V2: 'https://github.com/cap-js-community/odata-v2-adapter',
|
|
74
|
+
CAP_JS_COMMUNITY_EVENT_QUEUE: 'https://github.com/cap-js-community/event-queue',
|
|
75
|
+
CAP_JS_COMMUNITY_WEBSOCKET: 'https://github.com/cap-js-community/websocket'
|
|
73
76
|
}),
|
|
74
77
|
|
|
75
78
|
PROJECT_FILES: ['package.json', 'pom.xml', '.cdsrc.json']
|
package/lib/init/index.js
CHANGED
|
@@ -22,14 +22,16 @@ module.exports = class CDSGenerator {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
static help({ exclude = [] } = {}) {
|
|
25
|
+
if (!cds.add) return // use case shell completion: cds.add is undefined and not needed
|
|
26
|
+
|
|
25
27
|
const plugins = this.readPlugins(exclude)
|
|
26
28
|
const nameFixedLength = Math.max(...plugins.map(plugin => plugin.name.length))
|
|
27
29
|
return plugins
|
|
30
|
+
.filter(({module}) => module.help())
|
|
28
31
|
.map(({name, module}) => {
|
|
29
|
-
|
|
30
|
-
return ` *${name}*${' '.repeat(nameFixedLength - name.length)} - ${help}`
|
|
32
|
+
return ` *${name}*${' '.repeat(nameFixedLength - name.length)} - ${module.help()}`
|
|
31
33
|
})
|
|
32
|
-
.join('\n
|
|
34
|
+
.join('\n')
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -176,7 +178,7 @@ module.exports = class CDSGenerator {
|
|
|
176
178
|
const fuzzySearch = require('../../bin/util/fuzzySearch')
|
|
177
179
|
const [bestMatch] = fuzzySearch(plugin, entries.map(e => e.name))
|
|
178
180
|
const max = entries.reduce((max, {name}) => Math.max(max, name.length), 0)
|
|
179
|
-
const allFacetsText = entries.map(({name, module}) => {
|
|
181
|
+
const allFacetsText = entries.filter(({module})=> module.help()).map(({name, module}) => {
|
|
180
182
|
const help = module.help?.() ?? ''
|
|
181
183
|
return `${term.bold(name) + ' '.repeat(max - name.length)} ${term.dim(help)}`
|
|
182
184
|
}).join('\n')
|
package/lib/init/merge.js
CHANGED
|
@@ -70,7 +70,8 @@ module.exports.mergeGitignore = async function(into, from) {
|
|
|
70
70
|
/**
|
|
71
71
|
* @typedef {Object} Deletion
|
|
72
72
|
* @property {MatchedNode} item - matched node to delete
|
|
73
|
-
* @property {
|
|
73
|
+
* @property {MatchedNode} [key] - key path to property to delete in node (optional)
|
|
74
|
+
* @property {Object[]} [relationships] - list of relationships to delete
|
|
74
75
|
* @property {string} relationships[].removeProperty - key path to to-be-removed property
|
|
75
76
|
* @property {string[]} relationships[].allWithin - key path to relationship
|
|
76
77
|
*/
|
|
@@ -285,10 +286,11 @@ async function _mergeYAML(into, from, project, semantics = {}) {
|
|
|
285
286
|
})
|
|
286
287
|
|
|
287
288
|
// 4. Delete entries from the project (e.g. separate deployer module when adding mtx)
|
|
288
|
-
deletions?.forEach(({ item, relationships }) => {
|
|
289
|
+
deletions?.forEach(({ item, relationships, key }) => {
|
|
289
290
|
const entry = entryMap.get(item)
|
|
290
291
|
if (!entry) return
|
|
291
|
-
entry.collection.
|
|
292
|
+
if (key) entry.collection.deleteIn([entry.index, ...key.split('.')])
|
|
293
|
+
else entry.collection.delete(entry.index)
|
|
292
294
|
|
|
293
295
|
relationships?.forEach(relationship => {
|
|
294
296
|
const [allWithinKeyPath, inKeyPath, into] = relationship.allWithin
|
|
@@ -3,7 +3,7 @@ const { join, resolve, basename } = require('path')
|
|
|
3
3
|
const { fs, exists, isdir, path } = cds.utils
|
|
4
4
|
const { parseXml } = require('./xml')
|
|
5
5
|
const { REGEX_JAVA_VERSION, JAVA_LTS_VERSIONS } = require('./constants')
|
|
6
|
-
const
|
|
6
|
+
const TRACE = cds.debug('trace')
|
|
7
7
|
|
|
8
8
|
module.exports = new class ProjectReader {
|
|
9
9
|
|
|
@@ -88,7 +88,7 @@ module.exports = new class ProjectReader {
|
|
|
88
88
|
* @property {boolean} hasAttachments Indicates if the project uses SAP BTP Object Store Service.
|
|
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
|
-
* @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal
|
|
91
|
+
* @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal service.
|
|
92
92
|
* @property {boolean} hasDestination Indicates if the project uses the SAP BTP Destination service.
|
|
93
93
|
* @property {boolean} hasMTXRoute Indicates if the approuter should have a multitenant route.
|
|
94
94
|
* @property {boolean} hasMTXRouteJava Indicates if the approuter should have a multitenant subscription route (java).
|
|
@@ -106,7 +106,7 @@ module.exports = new class ProjectReader {
|
|
|
106
106
|
if (!this.appName) this.#setPackageInfo()
|
|
107
107
|
const env = this.env4('production')
|
|
108
108
|
const { appVersion, appName, appId, appDescription, jdkVersion } = this
|
|
109
|
-
|
|
109
|
+
TRACE?.({ env })
|
|
110
110
|
|
|
111
111
|
const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env)
|
|
112
112
|
const _ui5 = () => exists('package.json') && !!JSON.parse(fs.readFileSync(path.resolve(cds.root, 'package.json'))).sapux
|
|
@@ -187,7 +187,6 @@ module.exports = new class ProjectReader {
|
|
|
187
187
|
})
|
|
188
188
|
})()
|
|
189
189
|
|
|
190
|
-
DEBUG?.({ project })
|
|
191
190
|
return project
|
|
192
191
|
}
|
|
193
192
|
}
|
|
@@ -36,7 +36,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
|
|
|
36
36
|
const { hasMta, hasHelm, hasMultitenancy, srvPath } = project
|
|
37
37
|
const { plan = 'premium' } = cds.cli.options
|
|
38
38
|
project.servicePlan = plan
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
if (hasMultitenancy) {
|
|
41
41
|
await merge(__dirname, 'files/package.sidecar.json').into('mtx/sidecar/package.json')
|
|
42
42
|
}
|
|
@@ -12,6 +12,10 @@ module.exports = class CfManifestTemplate extends require('../../plugin') {
|
|
|
12
12
|
return 'Cloud Foundry deployment using manifest files'
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
static hasInProduction() {
|
|
16
|
+
return exists(MANIFEST_FILE)
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
async canRun() {
|
|
16
20
|
if (cds.cli.options.force) {
|
|
17
21
|
return true
|
|
@@ -15,7 +15,7 @@ const MAX_ASSOC_DEPTH = 5
|
|
|
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 } = options
|
|
18
|
+
let { referenceData, contentType } = options
|
|
19
19
|
|
|
20
20
|
// if no entity restrictions are given, use all entities
|
|
21
21
|
if (!data) data = csn.all('entity').reduce((all, e) => { all[e.name] = []; return all }, {})
|
|
@@ -63,6 +63,14 @@ const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
|
|
|
63
63
|
// remove records w/ duplicate keys. This can happen if the key is an enum (and we have less enum values than requested records).
|
|
64
64
|
data[name] = uniqRecords(data[name], entity)
|
|
65
65
|
}
|
|
66
|
+
for (let [key, d] of Object.entries(data)) {
|
|
67
|
+
if (contentType === 'json' && d.deleteReference && !d.keepReferece) {
|
|
68
|
+
delete data[key]
|
|
69
|
+
}
|
|
70
|
+
delete d.keepReferece
|
|
71
|
+
delete d.deleteReference
|
|
72
|
+
uniqRecords(d, csn.definitions[key])
|
|
73
|
+
}
|
|
66
74
|
return data
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -222,17 +230,16 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
222
230
|
}
|
|
223
231
|
case 'cds.Composition': {
|
|
224
232
|
const target = csn.definitions[element.target]
|
|
225
|
-
if (!target)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (target
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const targetRecords = []
|
|
233
|
+
if (!target || entity.name === target.name || depth > MAX_ASSOC_DEPTH) return
|
|
234
|
+
|
|
235
|
+
let targetRecords = [] // in findKeysInOnCondition target records are linked to the entity, we don't want to overwrite any existing data
|
|
236
|
+
if (data[target.name]) data[target.name].deleteReference = true // for json composites references are nested and will be deleted when writing to file
|
|
237
|
+
|
|
232
238
|
const targetRecordCount = element.is2many ? 2 : 1 // TODO: make this configurable
|
|
233
239
|
const targetRecordID = randomNumber()
|
|
234
240
|
for (let i = 0; i < targetRecordCount; i++) {
|
|
235
|
-
|
|
241
|
+
const newRecord = fillRecord({}, targetRecordID+i, target, csn, data, entity, depth+1, referenceData)
|
|
242
|
+
if (newRecord) targetRecords.push(newRecord)
|
|
236
243
|
}
|
|
237
244
|
|
|
238
245
|
const [ownKey, ownKeyStructured, targetKey] = findKeysInOnCondition(element, target)
|
|
@@ -268,11 +275,14 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
|
|
|
268
275
|
return
|
|
269
276
|
|
|
270
277
|
if (targetRecords.length > 0) { // use record generated by following asociation
|
|
271
|
-
targetRecord = targetRecords
|
|
278
|
+
targetRecord = targetRecords.at(-1) // make sure the localized record references the newly generated texts
|
|
272
279
|
} else {
|
|
273
280
|
targetRecord = fillRecord({}, randomNumber(), target, csn, data, entity, depth+1, referenceData)
|
|
274
|
-
|
|
275
|
-
|
|
281
|
+
if (targetRecord) {
|
|
282
|
+
targetRecords.push(targetRecord)
|
|
283
|
+
uniqRecords(targetRecords, target)
|
|
284
|
+
}
|
|
285
|
+
if (parentEntity && (parentEntity.name != entity.name)) data[target.name].keepReferece = true
|
|
276
286
|
}
|
|
277
287
|
}
|
|
278
288
|
|
|
@@ -77,7 +77,7 @@ module.exports = class DataTemplate extends require('../../plugin') {
|
|
|
77
77
|
.filter (e => queries || !e.query) // remove all projection-like entities unless specified differently
|
|
78
78
|
.reduce((all, e) => { all[e.name] = []; return all }, {})
|
|
79
79
|
|
|
80
|
-
await asJson(data, csn, records)
|
|
80
|
+
await asJson(data, csn, records, {contentType})
|
|
81
81
|
|
|
82
82
|
if (contentType === 'csv') {
|
|
83
83
|
const headerOnly = !cds.cli.options['records'] // compat to old behavior which only creates headers
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const cds = require('../../..')
|
|
2
2
|
const { rimraf, exists, readFileSync, yaml } = cds.utils
|
|
3
3
|
const { join } = require('path')
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
const { readProject } = require('../../projectReader')
|
|
6
|
+
const { merge } = require('../../merge')
|
|
5
7
|
|
|
6
8
|
module.exports = class HelmUnifiedRuntimeTemplate extends require('../helm') {
|
|
7
9
|
|
|
@@ -149,6 +149,7 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
149
149
|
}
|
|
150
150
|
varsDefined = true
|
|
151
151
|
const headers = auth ? `Content-Type: application/json\n{{auth}}` : `Content-Type: application/json`
|
|
152
|
+
const headerAuth = auth ? '{{auth}}' : ''
|
|
152
153
|
|
|
153
154
|
service.urlPaths.forEach((urlPath) => {
|
|
154
155
|
if (service.urlPaths.length > 1) {
|
|
@@ -156,36 +157,41 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
service.entities.forEach((e) => {
|
|
159
|
-
const entityNameSimple = e.name.slice(
|
|
160
|
+
const entityNameSimple = e.name.slice(serviceName.length + 1)
|
|
161
|
+
const nameSlug = slug(entityNameSimple)
|
|
160
162
|
const url = `{{server}}/${urlPath}${entityNameSimple}`
|
|
161
|
-
const requestTitle = `\n### ${
|
|
162
|
-
requests.push(`${requestTitle}\nGET ${url}\n${
|
|
163
|
+
const requestTitle = `\n### ${entityNameSimple}`
|
|
164
|
+
requests.push(`${requestTitle}\n# @name ${nameSlug}_GET\nGET ${url}\n${headerAuth}\n`)
|
|
163
165
|
if (e.readOnly) return
|
|
164
166
|
|
|
165
167
|
const entityData = payload[`${e.name}`] ? payload[`${e.name}`][0] : null
|
|
166
168
|
if (!entityData) return
|
|
167
|
-
const postBody = JSON.stringify(entityData, null, 2)
|
|
168
|
-
|
|
169
|
-
let id
|
|
170
|
-
const compositeKey = Object.keys(e.keys).length > 1
|
|
171
|
-
if (compositeKey) {
|
|
172
|
-
id = e.keys.map(k => k.type === 'cds.String' ? `${k.name}='${entityData[k.name]}'` : `${k.name}=${entityData[k.name]}`).join(',')
|
|
173
|
-
} else {
|
|
174
|
-
id = entityData[Object.keys(e.keys)[0]]
|
|
175
|
-
}
|
|
176
169
|
|
|
177
170
|
if (e.isDraft) {
|
|
178
|
-
|
|
179
|
-
|
|
171
|
+
const key = Object.keys(e.keys)[0] // draft keys are always single
|
|
172
|
+
delete entityData[key] // draft keys should always by auto generated (either UUID or through custom logic), so remove them from the payload
|
|
173
|
+
const postBody = JSON.stringify(entityData, null, 2)
|
|
174
|
+
generateDraftRequests(requests, requestTitle, url, headers, headerAuth, postBody, key)
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const postBody = JSON.stringify(entityData, null, 2)
|
|
178
|
+
let id
|
|
179
|
+
const compositeKey = Object.keys(e.keys).length > 1
|
|
180
|
+
if (compositeKey) {
|
|
181
|
+
id = e.keys.map(k => k.type === 'cds.String' ? `${k.name}='${entityData[k.name]}'` : `${k.name}=${entityData[k.name]}`).join(',')
|
|
182
|
+
} else {
|
|
183
|
+
id = entityData[Object.keys(e.keys)[0]]
|
|
184
|
+
}
|
|
180
185
|
compositeKey ? id = `(${id})` : id = `/${id}`
|
|
181
|
-
requests.push(`${requestTitle}\nPOST ${url}\n${headers}\n\n${postBody}\n`)
|
|
182
|
-
requests.push(`${requestTitle}\nPATCH ${url}${id}\n${headers}\n\n${postBody}\n`)
|
|
183
|
-
requests.push(`${requestTitle}\nDELETE ${url}${id}\n${headers}\n`)
|
|
186
|
+
requests.push(`${requestTitle}\n# @name ${nameSlug}_POST\nPOST ${url}\n${headers}\n\n${postBody}\n`)
|
|
187
|
+
requests.push(`${requestTitle}\n# @name ${nameSlug}_PATCH\nPATCH ${url}${id}\n${headers}\n\n${postBody}\n`)
|
|
188
|
+
requests.push(`${requestTitle}\n# @name ${nameSlug}_DELETE\nDELETE ${url}${id}\n${headers}\n`)
|
|
184
189
|
}
|
|
185
190
|
})
|
|
186
191
|
|
|
187
192
|
service.actions.forEach((a) => {
|
|
188
|
-
const
|
|
193
|
+
const simpleNameAction = a.name.slice(serviceName.length + 1)
|
|
194
|
+
const url = `{{server}}/${urlPath}${simpleNameAction}`
|
|
189
195
|
|
|
190
196
|
let actionPayload = {}
|
|
191
197
|
if (a.params) {
|
|
@@ -197,20 +203,25 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
197
203
|
: null
|
|
198
204
|
}
|
|
199
205
|
}
|
|
200
|
-
requests.push(`\n### ${
|
|
206
|
+
requests.push(`\n### ${simpleNameAction}\n# @name ${simpleNameAction}_POST\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
|
|
201
207
|
})
|
|
202
208
|
|
|
203
209
|
})
|
|
204
210
|
this.#write(requests, serviceName)
|
|
205
211
|
}
|
|
206
212
|
|
|
207
|
-
function generateDraftRequests(requests,
|
|
208
|
-
|
|
209
|
-
requests.push(`${
|
|
210
|
-
requests.push(
|
|
211
|
-
requests.push(
|
|
212
|
-
requests.push(`${
|
|
213
|
-
requests.push(`${
|
|
213
|
+
function generateDraftRequests(requests, title, url, headers, headerAuth, postBody, key) {
|
|
214
|
+
const nameDraftsCreate = slug(title+'_Draft_POST')
|
|
215
|
+
requests.push(`${title} Drafts GET\n# @name ${slug(title+'_Drafts_GET')}\nGET ${url}?$filter=(IsActiveEntity eq false)\n${headerAuth}\n`) // unencoded URL for better readability (client can handle this)
|
|
216
|
+
requests.push(`${title} Draft POST\n# @name ${nameDraftsCreate}\nPOST ${url}\n${headers}\n\n${postBody}\n`)
|
|
217
|
+
requests.push(`\n### Result from POST request above\n@draftID = {{${nameDraftsCreate}.response.body.$.${key}}}\n`)
|
|
218
|
+
requests.push(`${title} Draft PATCH\n# @name ${slug(title+'_Draft_Patch')}\nPATCH ${url}(${key}={{draftID}},IsActiveEntity=false)\n${headers}\n\n${postBody}\n`)
|
|
219
|
+
requests.push(`${title} Draft Prepare\n# @name ${slug(title+'_Draft_Prepare')}\nPOST ${url}(${key}={{draftID}},IsActiveEntity=false)/AdminService.draftPrepare\n${headers}\n\n{}\n`)
|
|
220
|
+
requests.push(`${title} Draft Activate\n# @name ${slug(title+'_Draft_Activate')}\nPOST ${url}(${key}={{draftID}},IsActiveEntity=false)/AdminService.draftActivate\n${headers}\n\n{}\n`)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function slug(str) {
|
|
224
|
+
return str.trim().replace(/[#\\. ]/g, '')
|
|
214
225
|
}
|
|
215
226
|
}
|
|
216
227
|
|
|
@@ -248,5 +259,3 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
248
259
|
return `${outputDir}/${serviceName}.http`
|
|
249
260
|
}
|
|
250
261
|
}
|
|
251
|
-
|
|
252
|
-
|
|
@@ -8,7 +8,7 @@ const { copyRenderedJSON } = require('../../../util/fs')
|
|
|
8
8
|
const {
|
|
9
9
|
srv4, // Server
|
|
10
10
|
mtxSidecar4, // Additional Modules
|
|
11
|
-
saasRegistry,
|
|
11
|
+
saasRegistry, // BTP Services
|
|
12
12
|
srvApi4, providedMtxApiSidecar4, requiredMtxSidecarApi4 // APIs
|
|
13
13
|
} = require('../../registries/mta')
|
|
14
14
|
|
|
@@ -47,13 +47,9 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
47
47
|
const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
|
|
48
48
|
const modules = [srv, mtxSidecar]
|
|
49
49
|
const services = [saasRegistry]
|
|
50
|
-
if (hasXsuaa) services.push(xsuaa)
|
|
51
50
|
const apis = [srvApi4(srv), providedMtxApiSidecar4(mtxSidecar)]
|
|
52
51
|
if (isJava) apis.push(requiredMtxSidecarApi4(srv))
|
|
53
52
|
const additions = [...modules, ...services, ...apis]
|
|
54
|
-
|
|
55
|
-
const overwrites = hasXsuaa ? [{ item: [xsuaa, 'parameters.config.tenant-mode'], withValue: 'shared' }] : []
|
|
56
|
-
|
|
57
53
|
const relationships = [{
|
|
58
54
|
insert: [saasRegistry, 'name'],
|
|
59
55
|
into: [isJava ? srv : mtxSidecar, 'requires', 'name']
|
|
@@ -64,34 +60,16 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
64
60
|
into: [mtxSidecar, 'build-parameters.requires', 'name']
|
|
65
61
|
})
|
|
66
62
|
}
|
|
67
|
-
if (hasXsuaa) {
|
|
68
|
-
relationships.push({
|
|
69
|
-
insert: [xsuaa, 'name'],
|
|
70
|
-
into: [mtxSidecar, 'requires', 'name']
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
if (hasHana) {
|
|
74
|
-
relationships.push({
|
|
75
|
-
insert: [serviceManager, 'name'],
|
|
76
|
-
into: [mtxSidecar, 'requires', 'name'],
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
63
|
|
|
80
|
-
const templateMtaPath = join(__dirname, 'files
|
|
81
|
-
await merge(templateMtaPath).into('mta.yaml', { with: project, additions,
|
|
64
|
+
const templateMtaPath = join(__dirname, 'files/mta.yaml.hbs')
|
|
65
|
+
await merge(templateMtaPath).into('mta.yaml', { with: project, additions, relationships })
|
|
82
66
|
}
|
|
83
67
|
|
|
84
68
|
if (hasHelm || hasHelmUnifiedRuntime) {
|
|
85
|
-
const overwrites = hasXsuaa ? [{
|
|
86
|
-
item: [{
|
|
87
|
-
in: 'xsuaa',
|
|
88
|
-
where: { serviceOfferingName: 'xsuaa' }
|
|
89
|
-
}, 'parameters.tenant-mode'], withValue: 'shared'
|
|
90
|
-
}] : []
|
|
91
69
|
|
|
92
|
-
await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project
|
|
70
|
+
await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
|
|
93
71
|
|
|
94
|
-
const domain = hasApprouter ? 'svc.cluster.local:8080' : '{{ .Values.global.domain }}'
|
|
72
|
+
const domain = hasApprouter ? 'svc.cluster.local:8080' : '{{ .Values.global.domain }}'
|
|
95
73
|
const applicationWorkload = hasApprouter ? 'approuter' : 'srv'
|
|
96
74
|
const serviceCommunicatingWithSaasReg = hasApprouter ? 'approuter' : isNodejs ? 'sidecar' : 'srv';
|
|
97
75
|
await merge({
|
|
@@ -102,7 +80,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
102
80
|
appUrls: {
|
|
103
81
|
getDependencies: `https://{{ .Release.Name }}-${serviceCommunicatingWithSaasReg}-{{ .Release.Namespace }}.{{ .Values.global.domain }}${isNodejs ? '/-/cds/saas-provisioning' : '/mt/v1.0/subscriptions'}/dependencies`,
|
|
104
82
|
onSubscription: `https://{{ .Release.Name }}-${serviceCommunicatingWithSaasReg}-{{ .Release.Namespace }}.{{ .Values.global.domain }}${isNodejs ? '/-/cds/saas-provisioning/tenant/{tenantId}' : '/mt/v1.0/subscriptions/tenants/{tenantId}'}`,
|
|
105
|
-
}
|
|
83
|
+
}
|
|
106
84
|
}
|
|
107
85
|
},
|
|
108
86
|
...(isNodejs) && { sidecar: {
|
|
@@ -110,12 +88,12 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
110
88
|
SUBSCRIPTION_URL: `https://\${tenant_subdomain}-{{ .Release.Name }}-${applicationWorkload}-{{ .Release.Namespace }}.{{ .Values.global.domain }}`
|
|
111
89
|
}
|
|
112
90
|
}},
|
|
113
|
-
...(isJava) && { srv: {
|
|
91
|
+
...(isJava) && { srv: {
|
|
114
92
|
env: {
|
|
115
93
|
CDS_MULTITENANCY_APPUI_URL: `https://{{ .Release.Name }}-${applicationWorkload}-{{ .Release.Namespace }}.{{ .Values.global.domain }}`,
|
|
116
94
|
CDS_MULTITENANCY_SIDECAR_URL: `http${hasApprouter ? '' : 's'}://{{ .Release.Name }}-sidecar${hasApprouter ? '.' : '-'}{{ .Release.Namespace }}.${domain}`
|
|
117
|
-
}
|
|
118
|
-
}}
|
|
95
|
+
}
|
|
96
|
+
}}
|
|
119
97
|
}).into('chart/values.yaml',{ forceOverwrite: true })
|
|
120
98
|
}
|
|
121
99
|
|