@sap/cds-dk 9.6.1 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/bin/add.js +5 -2
- package/bin/cds.js +10 -25
- package/bin/data-products/import.js +3 -0
- package/bin/debug.js +76 -16
- package/bin/env.js +2 -1
- package/bin/export.js +18 -13
- package/bin/help/export.md +2 -1
- package/bin/import.js +21 -18
- package/bin/init.js +8 -1
- package/bin/minify.js +11 -12
- package/bin/mock.js +17 -9
- package/bin/repl.js +2 -2
- package/bin/up.js +34 -4
- package/bin/version.js +202 -131
- package/lib/build/buildTaskEngine.js +4 -0
- package/lib/build/buildTaskFactory.js +5 -35
- package/lib/build/index.js +10 -10
- package/lib/build/plugin.js +6 -3
- package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +3 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/_helpers.tpl +1 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +8 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +18 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +11 -1
- package/lib/build/plugins.js +8 -16
- package/lib/build/provider/hana/index.js +16 -19
- package/lib/build/provider/internalBuildPlugin.js +5 -4
- package/lib/build/provider/internalBuildTaskProvider.js +26 -88
- package/lib/build/provider/mtx/index.js +0 -8
- package/lib/build/provider/mtx-sidecar/index.js +88 -80
- package/lib/build/provider/nodejs/index.js +3 -8
- package/lib/build/util.js +52 -87
- package/lib/deploy/to-hana/hana.js +117 -109
- package/lib/env/schemas/cds-rc.js +0 -4
- package/lib/import/odata/v4parser.js +80 -56
- package/lib/init/constants.js +5 -2
- package/lib/init/index.js +42 -40
- package/lib/init/merge.js +7 -2
- package/lib/init/projectReader.js +2 -8
- package/lib/init/template/ams/index.js +1 -4
- package/lib/init/template/attachments/files/package.json.hbs +1 -1
- package/lib/init/template/data/index.js +1 -1
- package/lib/init/template/extension/index.js +4 -4
- package/lib/init/template/hana/files/package.json.hbs +3 -3
- package/lib/init/template/handler/index.js +1 -1
- package/lib/init/template/helm/index.js +3 -3
- package/lib/init/template/helm-unified-runtime/index.js +5 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +1 -1
- package/lib/init/template/ias/files/mta.yaml.hbs +3 -3
- package/lib/init/template/initial/files/.vscode/tasks.json +10 -0
- package/lib/init/template/{nodejs/files/README.md → initial/files/readme.md} +2 -3
- package/lib/init/template/initial/index.js +15 -0
- package/lib/init/template/java/index.js +4 -11
- package/lib/init/template/kyma/index.js +21 -20
- package/lib/init/template/lint/index.js +3 -1
- package/lib/init/template/mta/index.js +0 -5
- package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -2
- package/lib/init/template/multitenancy/files/values.yaml.hbs +30 -0
- package/lib/init/template/nodejs/files/.vscode/tasks.json +1 -14
- package/lib/init/template/nodejs/files/package.json.hbs +2 -4
- package/lib/init/template/nodejs/index.js +8 -12
- package/lib/init/template/sample/index.js +5 -6
- package/lib/init/template/sqlite/files/package.json.hbs +7 -1
- package/lib/init/template/test/index.js +1 -1
- package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +6 -0
- package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds +5 -0
- package/lib/init/template/tiny-sample/index.js +2 -2
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +6 -0
- package/lib/init/template/workzone-standard/index.js +13 -3
- package/lib/init/validate.js +11 -6
- package/lib/util/checksum.js +2 -2
- package/lib/util/fs.js +109 -53
- package/lib/util/glob.js +2 -1
- package/lib/util/term.js +1 -7
- package/lib/util/write.js +2 -4
- package/lib/watch/watched.js +1 -1
- package/npm-shrinkwrap.json +226 -259
- package/package.json +2 -2
- package/lib/init/template/nodejs/files/.vscode/settings.json +0 -11
- package/lib/init/template/tiny-sample/files/nodejs/db/data/my.bookshop-Books.csv +0 -3
- package/lib/init/template/tiny-sample/files/nodejs/db/schema.cds +0 -7
- package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds.hbs +0 -5
|
@@ -24,14 +24,15 @@ const allowList = [
|
|
|
24
24
|
'--auto-undeploy',
|
|
25
25
|
'--dry',
|
|
26
26
|
'--for',
|
|
27
|
+
'--no-build',
|
|
27
28
|
'--on',
|
|
29
|
+
'--out',
|
|
30
|
+
'--resolve-bindings',
|
|
28
31
|
'--store-credentials',
|
|
29
32
|
'--to',
|
|
30
33
|
'--tunnel-address',
|
|
31
34
|
'--vcap-file',
|
|
32
|
-
'--with-mocks'
|
|
33
|
-
'--no-build',
|
|
34
|
-
'--resolve-bindings'
|
|
35
|
+
'--with-mocks'
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
module.exports = new class HanaDeployer {
|
|
@@ -51,6 +52,11 @@ module.exports = new class HanaDeployer {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
async deploy(options = {}) {
|
|
55
|
+
|
|
56
|
+
if (options.dry && options.out) { // dry run equals cds compile to hana
|
|
57
|
+
return await this._runCompileToHana(options);
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
let unknownOptions = Object.keys(options)
|
|
55
61
|
.map(key => `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`)
|
|
56
62
|
.filter(key => allOptions.includes(key) && !allowList.includes(key));
|
|
@@ -59,7 +65,7 @@ module.exports = new class HanaDeployer {
|
|
|
59
65
|
throw `One or more options are not supported by cds deploy to hana: ${unknownOptions.join(', ')}`;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
let { model, serviceName: pServiceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions = {}
|
|
68
|
+
let { model, serviceName: pServiceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions = {} } = options;
|
|
63
69
|
|
|
64
70
|
options.for ??= 'hybrid';
|
|
65
71
|
|
|
@@ -69,10 +75,6 @@ module.exports = new class HanaDeployer {
|
|
|
69
75
|
Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
if (dry) {
|
|
73
|
-
console.log('--- running in dry mode ---');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
78
|
if (vcapFile) {
|
|
77
79
|
console.log(`Using VCAP_SERVICES from file ${vcapFile}.`);
|
|
78
80
|
}
|
|
@@ -97,36 +99,34 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
97
99
|
buildResult = await this._build();
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
if (options.out) {
|
|
103
|
+
await this._runCompileToHana(options);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (options.dry) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
if (process.env.TARGET_CONTAINER) {
|
|
101
111
|
vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
|
|
102
112
|
}
|
|
103
113
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
for (const file of sortedResult) {
|
|
108
|
-
const srcPath = path.join(buildResult.dest, file);
|
|
109
|
-
console.log(`-- ${path.relative(cds.root, srcPath)}`);
|
|
110
|
-
console.log(await read(srcPath));
|
|
111
|
-
console.log();
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
// the primary database is always built and deployed
|
|
115
|
-
// const dest = buildResult.dest
|
|
116
|
-
const dest = path.join(cds.root, cds.env.build.target, cds.env.folders.db);
|
|
114
|
+
// the primary database is always built and deployed
|
|
115
|
+
// const dest = buildResult.dest
|
|
116
|
+
const dest = path.join(cds.root, cds.env.build.target, cds.env.folders.db);
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
if (undeployWhitelist) {
|
|
119
|
+
console.log('Writing undeploy.json');
|
|
120
|
+
await write(undeployWhitelist).to(dest, 'undeploy.json')
|
|
121
|
+
}
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
let bindOptions
|
|
126
|
+
if (cds.cli.options.on === 'k8s') {
|
|
127
|
+
const cp = require('node:child_process')
|
|
128
|
+
const appName = pServiceName ?? path.basename(cds.root)
|
|
129
|
+
const instance =
|
|
130
130
|
`apiVersion: services.cloud.sap.com/v1
|
|
131
131
|
kind: ServiceInstance
|
|
132
132
|
metadata:
|
|
@@ -136,7 +136,7 @@ spec:
|
|
|
136
136
|
servicePlanName: hdi-shared
|
|
137
137
|
`
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
const binding =
|
|
140
140
|
`apiVersion: services.cloud.sap.com/v1
|
|
141
141
|
kind: ServiceBinding
|
|
142
142
|
metadata:
|
|
@@ -144,95 +144,93 @@ metadata:
|
|
|
144
144
|
spec:
|
|
145
145
|
serviceInstanceName: ${appName}-db
|
|
146
146
|
secretName: ${appName}-db-secret
|
|
147
|
-
|
|
148
147
|
`
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
try {
|
|
150
|
+
cp.execSync('kubectl apply -f -', { input: instance })
|
|
151
|
+
cp.execSync('kubectl apply -f -', { input: binding })
|
|
152
|
+
cp.execSync(`kubectl wait --for=condition=Ready servicebinding/${appName}-db-binding --timeout=180s`)
|
|
153
|
+
cp.execSync(`kubectl wait --for=condition=Ready serviceinstance/${appName}-db --timeout=180s`)
|
|
154
|
+
} catch (error) {
|
|
155
|
+
const descr = cp.execSync(`kubectl describe serviceinstance/${appName}-db`).toString()
|
|
156
|
+
const message = descr.match(/Message:\s+(.*)/)?.[1] || 'None'
|
|
157
|
+
throw error.message + `\nInstance Message: ${message}`
|
|
158
|
+
}
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
160
|
+
const { data: encoded } = JSON.parse(cp.execSync(`kubectl get secrets/${appName}-db-secret -o json`).toString())
|
|
161
|
+
const credentials = {}
|
|
162
|
+
for (const [key, value] of Object.entries(encoded)) {
|
|
163
|
+
credentials[key] = Buffer.from(value, 'base64').toString('utf-8');
|
|
164
|
+
}
|
|
165
|
+
vcapEnv.VCAP_SERVICES ??= {
|
|
166
|
+
hana: [{
|
|
167
|
+
tags: ['hana'],
|
|
168
|
+
credentials
|
|
169
|
+
}]
|
|
170
|
+
}
|
|
171
|
+
bindOptions = {
|
|
172
|
+
...options,
|
|
173
|
+
to: `${appName}-db-binding`,
|
|
174
|
+
for: options.for,
|
|
175
|
+
kind: BUILD_TASK_HANA
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
let serviceName = pServiceName;
|
|
179
|
+
let serviceKeyName;
|
|
180
|
+
const { instance, key } = cds.requires.db?.binding ?? {};
|
|
181
|
+
if (!serviceName && instance) {
|
|
182
|
+
serviceName = instance;
|
|
183
|
+
serviceKeyName = key;
|
|
184
|
+
}
|
|
186
185
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
for (const entry of hanaTagged) {
|
|
203
|
-
entry.name ??= cfServiceInstanceName;
|
|
204
|
-
entry.tags = Array.from(new Set([...entry.tags || [], 'hana']));
|
|
205
|
-
entry.credentials = { ...(entry.credentials || {}), ...serviceKey };
|
|
206
|
-
}
|
|
207
|
-
base.hana = hanas;
|
|
208
|
-
vcapEnv.VCAP_SERVICES = base;
|
|
209
|
-
process.env.VCAP_SERVICES = JSON.stringify(base);
|
|
186
|
+
if (!serviceName && hasVCAPEnv) {
|
|
187
|
+
await mkdirp(dest);
|
|
188
|
+
} else {
|
|
189
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } = await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
|
|
190
|
+
serviceName ??= cfServiceInstanceName;
|
|
191
|
+
serviceKeyName ??= cfServiceInstanceKeyName
|
|
192
|
+
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
193
|
+
const base = process.env.VCAP_SERVICES ? JSON.parse(process.env.VCAP_SERVICES) : (vcapEnv.VCAP_SERVICES ?? {});
|
|
194
|
+
const hanas = Array.isArray(base.hana) ? base.hana : [];
|
|
195
|
+
let hanaTagged = hanas.filter(s => s?.name === cfServiceInstanceName || (Array.isArray(s?.tags) && s.tags.includes('hana')));
|
|
196
|
+
if (hanaTagged.length === 0) {
|
|
197
|
+
const entry = { name: cfServiceInstanceName, label: 'hana', tags: ['hana'], credentials: {} };
|
|
198
|
+
hanas.push(entry);
|
|
199
|
+
hanaTagged = [entry];
|
|
210
200
|
}
|
|
201
|
+
for (const entry of hanaTagged) {
|
|
202
|
+
entry.name ??= cfServiceInstanceName;
|
|
203
|
+
entry.tags = Array.from(new Set([...entry.tags || [], 'hana']));
|
|
204
|
+
entry.credentials = { ...(entry.credentials || {}), ...serviceKey };
|
|
205
|
+
}
|
|
206
|
+
base.hana = hanas;
|
|
207
|
+
vcapEnv.VCAP_SERVICES = base;
|
|
208
|
+
process.env.VCAP_SERVICES = JSON.stringify(base);
|
|
209
|
+
}
|
|
211
210
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
211
|
+
if (!hasVCAPEnv) {
|
|
212
|
+
bindOptions = {
|
|
213
|
+
...options,
|
|
214
|
+
to: `${serviceName}:${serviceKeyName}`,
|
|
215
|
+
for: options.for,
|
|
216
|
+
kind: BUILD_TASK_HANA
|
|
219
217
|
}
|
|
220
218
|
}
|
|
221
219
|
}
|
|
220
|
+
}
|
|
222
221
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
console.log(`${bold('starting deployment to SAP HANA ...')}`);
|
|
223
|
+
DEBUG?.({ dest, vcapEnv, hdiOptions })
|
|
224
|
+
await hdi.deploy(dest, vcapEnv, hdiOptions);
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
if (!hasVCAPEnv) {
|
|
227
|
+
const services = await bind(bindOptions)
|
|
228
|
+
await storeBindings(services, bindOptions)
|
|
229
|
+
}
|
|
231
230
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
231
|
+
const { env4 } = require('../../init/projectReader')
|
|
232
|
+
if (env4('production').requires?.db?.kind !== 'hana') {
|
|
233
|
+
console.log(`use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
|
|
236
234
|
}
|
|
237
235
|
|
|
238
236
|
console.log(`successfully finished deployment`);
|
|
@@ -245,6 +243,16 @@ spec:
|
|
|
245
243
|
return buildResult;
|
|
246
244
|
}
|
|
247
245
|
|
|
246
|
+
async _runCompileToHana(options) {
|
|
247
|
+
const csn = await cds.load('*');
|
|
248
|
+
const files = cds.compile.to.hana(csn, options);
|
|
249
|
+
const outDir = path.resolve(cds.root, options.out);
|
|
250
|
+
for (const [content, { file }] of files) {
|
|
251
|
+
const filePath = path.join(outDir, file);
|
|
252
|
+
await cds.utils.write(content).to(filePath);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
248
256
|
async _getOrCreateCFService(currentModelFolder, serviceName, serviceKeyName, tunnelAddress) {
|
|
249
257
|
|
|
250
258
|
// get from param
|
|
@@ -285,8 +285,6 @@ module.exports = {
|
|
|
285
285
|
"mtx-sidecar",
|
|
286
286
|
"mtx-extension",
|
|
287
287
|
"mtx",
|
|
288
|
-
"node-cf",
|
|
289
|
-
"java-cf",
|
|
290
288
|
"helm"
|
|
291
289
|
]),
|
|
292
290
|
enumDescriptions: [
|
|
@@ -296,8 +294,6 @@ module.exports = {
|
|
|
296
294
|
"MTX sidecar build plugin",
|
|
297
295
|
"MTX extension build plugin",
|
|
298
296
|
"MTX build plugin",
|
|
299
|
-
"Node.js Cloud Foundry build plugin",
|
|
300
|
-
"Java Cloud Foundry build plugin",
|
|
301
297
|
"Helm chart build plugin"
|
|
302
298
|
]
|
|
303
299
|
},
|
|
@@ -122,7 +122,7 @@ function _getIndexAndNamespaceOfSchema(fqName, schemaName, parserContext) {
|
|
|
122
122
|
}
|
|
123
123
|
// the schema namespace is not resolvable, throw error
|
|
124
124
|
else {
|
|
125
|
-
throw new Error(messages.UNRESOLVED_SCHEMA_NAMESPACE + fqName
|
|
125
|
+
throw new Error(messages.UNRESOLVED_SCHEMA_NAMESPACE + fqName);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -290,7 +290,7 @@ function _replaceSpecialCharacters(text) {
|
|
|
290
290
|
function _resolveCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
291
291
|
if (annotationTerm === "@Core.Description" || annotationTerm === "@Core.LongDescription") {
|
|
292
292
|
// we don't capture `null` value for doc
|
|
293
|
-
if (annotationTermObject === null)
|
|
293
|
+
if (annotationTermObject === null) return;
|
|
294
294
|
|
|
295
295
|
annotationTermObject = _replaceSpecialCharacters(annotationTermObject);
|
|
296
296
|
|
|
@@ -474,9 +474,9 @@ function _resolveAnnotationTerm(schemaDataAnnotation, parserContext) {
|
|
|
474
474
|
let index = schemaDataAnnotation.indexOf('"@');
|
|
475
475
|
while (index != -1) {
|
|
476
476
|
let i = schemaDataAnnotation.indexOf('"', index + 1);
|
|
477
|
-
let annotationTerm = schemaDataAnnotation.substring(index+1, i);
|
|
477
|
+
let annotationTerm = schemaDataAnnotation.substring(index + 1, i);
|
|
478
478
|
_validateAnnotationNamespace(annotationTerm, parserContext.vocabNamespaceToAlias);
|
|
479
|
-
index = schemaDataAnnotation.indexOf('"@', i+1);
|
|
479
|
+
index = schemaDataAnnotation.indexOf('"@', i + 1);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
return JSON.parse(schemaDataAnnotation);
|
|
@@ -688,10 +688,23 @@ function _parseAssociationProperty(foreignReference, associationNode, parserCont
|
|
|
688
688
|
// "generation" of keys, that do not exist in the external service.
|
|
689
689
|
if (associationNode.$Partner && associationProperty.type === 'cds.Composition') {
|
|
690
690
|
associationProperty.on = [
|
|
691
|
-
{ref: [foreignReference, associationNode.$Partner]},
|
|
691
|
+
{ ref: [foreignReference, associationNode.$Partner] },
|
|
692
692
|
'=',
|
|
693
|
-
{ref: ['$self']}
|
|
693
|
+
{ ref: ['$self'] }
|
|
694
694
|
]
|
|
695
|
+
} else if (associationNode.$ReferentialConstraint) {
|
|
696
|
+
associationProperty.on = []
|
|
697
|
+
for (const constraint in associationNode.$ReferentialConstraint) {
|
|
698
|
+
if (associationProperty.on.length) {
|
|
699
|
+
associationProperty.on.push('and')
|
|
700
|
+
}
|
|
701
|
+
associationProperty.on.push(
|
|
702
|
+
{ ref: [foreignReference, associationNode.$ReferentialConstraint[constraint]] },
|
|
703
|
+
'=',
|
|
704
|
+
{ ref: [constraint] }
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
|
|
695
708
|
} else if (common.checkForEmptyKeys(associationProperty, "V4")) {
|
|
696
709
|
associationProperty.keys = [];
|
|
697
710
|
}
|
|
@@ -738,52 +751,63 @@ function _parsePropertyContent(csnObj, elementObj, parserContext) {
|
|
|
738
751
|
const isPrimaryKey = true;
|
|
739
752
|
let dataType, isPrimitiveType, keySet = [];
|
|
740
753
|
|
|
741
|
-
Object.keys(elementObj)
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
);
|
|
761
|
-
} else {
|
|
762
|
-
throw new Error(messages.INVALID_DATATYPE + `'${dataType}' in element: '${objKey}'`)
|
|
754
|
+
Object.keys(elementObj)
|
|
755
|
+
.sort((ele1, ele2) => {
|
|
756
|
+
if (elementObj[ele1]?.$Kind === elementObj[ele2]?.$Kind) {
|
|
757
|
+
return 0
|
|
758
|
+
} else if (elementObj[ele1]?.$Kind === 'NavigationProperty') {
|
|
759
|
+
return 1
|
|
760
|
+
} else if (elementObj[ele2]?.$Kind === 'NavigationProperty') {
|
|
761
|
+
return -1
|
|
762
|
+
}
|
|
763
|
+
return 0
|
|
764
|
+
})
|
|
765
|
+
.forEach(objKey => {
|
|
766
|
+
const contentKind = elementObj[objKey].$Kind;
|
|
767
|
+
contentObj = elementObj[objKey];
|
|
768
|
+
|
|
769
|
+
if ((objKey.indexOf('@') === -1) && (objKey.indexOf('.') === -1)) {
|
|
770
|
+
if (contentObj instanceof Object) {
|
|
771
|
+
if (contentObj.$Type) {
|
|
772
|
+
dataType = contentObj.$Type;
|
|
773
|
+
isPrimitiveType = dataType.startsWith('Edm') ? true : false;
|
|
763
774
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
else if (!isPrimitiveType && !(contentKind === 'NavigationProperty')) {
|
|
767
|
-
const dataKind = _checkIsStructureTypeAndReturnDataKind(dataType, contentObj, 'property', parserContext);
|
|
768
|
-
|
|
769
|
-
if (['EnumType', 'TypeDefinition'].includes(dataKind)) {
|
|
770
|
-
csnObj.elements[objKey] = _generateStructuredDataTypeCsn(dataType, contentObj,
|
|
771
|
-
(keySet.includes(dataType) ? isPrimaryKey : !isPrimaryKey),
|
|
772
|
-
dataKind, 'property', parserContext
|
|
773
|
-
);
|
|
775
|
+
if (objKey === '$Key') {
|
|
776
|
+
keySet.push(...elementObj.$Key);
|
|
774
777
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
778
|
+
// primitive datatypes
|
|
779
|
+
else if (isPrimitiveType) {
|
|
780
|
+
if (edmPrimitiveTypes.includes(dataType)) {
|
|
781
|
+
csnObj.elements[objKey] = _generatePrimitiveDataTypeCsn(contentObj,
|
|
782
|
+
(keySet.includes(objKey) ? isPrimaryKey : !isPrimaryKey),
|
|
783
|
+
parserContext
|
|
784
|
+
);
|
|
785
|
+
} else {
|
|
786
|
+
throw new Error(messages.INVALID_DATATYPE + `'${dataType}' in element: '${objKey}'`)
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
// structured datatypes
|
|
790
|
+
else if (!isPrimitiveType && !(contentKind === 'NavigationProperty')) {
|
|
791
|
+
const dataKind = _checkIsStructureTypeAndReturnDataKind(dataType, contentObj, 'property', parserContext);
|
|
792
|
+
|
|
793
|
+
if (['EnumType', 'TypeDefinition'].includes(dataKind)) {
|
|
794
|
+
csnObj.elements[objKey] = _generateStructuredDataTypeCsn(dataType, contentObj,
|
|
795
|
+
(keySet.includes(dataType) ? isPrimaryKey : !isPrimaryKey),
|
|
796
|
+
dataKind, 'property', parserContext
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
csnObj.elements[objKey] = _generateStructuredDataTypeCsn(dataType, contentObj,
|
|
801
|
+
!isPrimaryKey, dataKind, 'property', parserContext
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
else if (contentKind === 'NavigationProperty') {
|
|
806
|
+
csnObj.elements[objKey] = _parseAssociationProperty(objKey, elementObj[objKey], parserContext);
|
|
779
807
|
}
|
|
780
|
-
}
|
|
781
|
-
else if (contentKind === 'NavigationProperty') {
|
|
782
|
-
csnObj.elements[objKey] = _parseAssociationProperty(objKey, elementObj[objKey], parserContext);
|
|
783
808
|
}
|
|
784
809
|
}
|
|
785
|
-
}
|
|
786
|
-
});
|
|
810
|
+
});
|
|
787
811
|
}
|
|
788
812
|
|
|
789
813
|
function _addBlobElement() {
|
|
@@ -960,7 +984,7 @@ function _parseBoundActionFunction(schemaData, csnObj, kind, parserContext) {
|
|
|
960
984
|
const boundObject = (kind === 'action') ? parserContext.entityToBoundActions : parserContext.entityToBoundFunctions;
|
|
961
985
|
|
|
962
986
|
Object.keys(boundObject).forEach(fqEntityName => {
|
|
963
|
-
const [schemaName,
|
|
987
|
+
const [schemaName,] = _extractSchemaNameAndElementName(fqEntityName);
|
|
964
988
|
|
|
965
989
|
if (schemaName === schemaData.$SchemaNamespace) {
|
|
966
990
|
const actionFunctionObjectList = boundObject[fqEntityName];
|
|
@@ -982,7 +1006,7 @@ function _parseUnboundActionFunction(schemaData, kind, parserContext) {
|
|
|
982
1006
|
const csnObj = {};
|
|
983
1007
|
|
|
984
1008
|
Object.keys(unboundObject).forEach(fqName => {
|
|
985
|
-
const [schemaName,
|
|
1009
|
+
const [schemaName,] = _extractSchemaNameAndElementName(fqName);
|
|
986
1010
|
|
|
987
1011
|
if (schemaName === schemaData.$SchemaNamespace) {
|
|
988
1012
|
const actionFunctionObject = unboundObject[fqName];
|
|
@@ -1039,7 +1063,7 @@ function _parserCSNForComplexType(schemaData, parserContext) {
|
|
|
1039
1063
|
|
|
1040
1064
|
function _parseCSNForEnumType(schemaData, parserContext) {
|
|
1041
1065
|
const nonMemberList = ["$Kind", "$Type", "$UnderlyingType", "$IsFlags"];
|
|
1042
|
-
const possibleCdsTypes = ['cds.Integer',
|
|
1066
|
+
const possibleCdsTypes = ['cds.Integer', 'cds.Integer64'];
|
|
1043
1067
|
const enumCsn = {};
|
|
1044
1068
|
const isKey = true;
|
|
1045
1069
|
|
|
@@ -1058,7 +1082,7 @@ function _parseCSNForEnumType(schemaData, parserContext) {
|
|
|
1058
1082
|
let underlyingType = enumObj.$UnderlyingType;
|
|
1059
1083
|
enumObj.$Type = underlyingType;
|
|
1060
1084
|
|
|
1061
|
-
let cdsType = underlyingType ? (_generatePrimitiveDataTypeCsn(enumObj, !isKey, parserContext)) : { type
|
|
1085
|
+
let cdsType = underlyingType ? (_generatePrimitiveDataTypeCsn(enumObj, !isKey, parserContext)) : { type: 'cds.Integer' };
|
|
1062
1086
|
enumCsn[fqName].type = cdsType.type;
|
|
1063
1087
|
|
|
1064
1088
|
if (cdsType['@odata.Type']) {
|
|
@@ -1127,7 +1151,7 @@ function _resolveEntityContainerAnnotationTarget(targetPath, csnDefs, hasPropert
|
|
|
1127
1151
|
}
|
|
1128
1152
|
// direct path to service object
|
|
1129
1153
|
else {
|
|
1130
|
-
|
|
1154
|
+
return csnDefs[parserContext.primarySchema];
|
|
1131
1155
|
}
|
|
1132
1156
|
}
|
|
1133
1157
|
|
|
@@ -1396,7 +1420,7 @@ function _populateUnboundActionFunction(isAction, fqName, schemaDataList, parser
|
|
|
1396
1420
|
actionFunctionObject['$Parameter'] = [];
|
|
1397
1421
|
parserContext.unboundFunctions[fqName] = actionFunctionObject;
|
|
1398
1422
|
parserContext.unboundFunctions[fqName]["@cds.validate"] = false;
|
|
1399
|
-
if(parserContext.unboundFunctions[fqName].$ReturnType){
|
|
1423
|
+
if (parserContext.unboundFunctions[fqName].$ReturnType) {
|
|
1400
1424
|
parserContext.unboundFunctions[fqName].$ReturnType.$Type = "Edm.String";
|
|
1401
1425
|
}
|
|
1402
1426
|
}
|
|
@@ -1611,8 +1635,7 @@ function _preProcess(edmxAsJson, parserContext, importerContext) {
|
|
|
1611
1635
|
parserContext.entityToEntitySetMap._.size === 0 &&
|
|
1612
1636
|
parserContext.entityToSingletonMap._.size === 0 &&
|
|
1613
1637
|
parserContext.actionToActionImportMap._.size === 0 &&
|
|
1614
|
-
parserContext.functionToFunctionImportMap._.size === 0)
|
|
1615
|
-
{
|
|
1638
|
+
parserContext.functionToFunctionImportMap._.size === 0) {
|
|
1616
1639
|
parserContext.mockServerUc = false;
|
|
1617
1640
|
}
|
|
1618
1641
|
|
|
@@ -1686,7 +1709,8 @@ function _generateCSN(edmxAsJson, ignorePersistenceSkip, parserContext, importer
|
|
|
1686
1709
|
compTarget.elements[e].type === 'cds.Association' &&
|
|
1687
1710
|
compTarget.elements[e].target === eName
|
|
1688
1711
|
)
|
|
1689
|
-
|
|
1712
|
+
const entityKeys = Object.keys(entity.elements).filter(ele => entity.elements[ele].key)
|
|
1713
|
+
if (compTarget.elements[backLink]?.keys && !entityKeys.some(key => compTarget.elements[backLink + '_' + key])) {
|
|
1690
1714
|
delete compTarget.elements[backLink].keys
|
|
1691
1715
|
}
|
|
1692
1716
|
}
|
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: '4.6.
|
|
8
|
+
MAVEN_ARCHETYPE_VERSION: '4.6.2',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -82,7 +82,10 @@ module.exports = {
|
|
|
82
82
|
CAPIRE: 'https://cap.cloud.sap',
|
|
83
83
|
MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/developing-applications/building#the-maven-archetype`,
|
|
84
84
|
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
|
|
85
|
-
|
|
85
|
+
CF_INSTALL: 'https://docs.cloudfoundry.org/cf-cli/install-go-cli.html',
|
|
86
|
+
HELM_INSTALL: 'https://helm.sh/docs/intro/install/',
|
|
87
|
+
KUBECTL_INSTALL: 'https://kubernetes.io/docs/tasks/tools/',
|
|
88
|
+
DOCKER_INSTALL: 'https://docs.docker.com/get-docker/'
|
|
86
89
|
}),
|
|
87
90
|
|
|
88
91
|
PROJECT_FILES: ['package.json', 'pom.xml', '.cdsrc.json']
|