@sap/cds-dk 6.3.2 → 6.4.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 +31 -7
- package/bin/cds.js +2 -0
- package/bin/compile/index.js +2 -1
- package/bin/deploy/index.js +2 -2
- package/bin/deploy/to-sqlite.js +17 -15
- package/bin/help.js +7 -4
- package/bin/init.js +1 -1
- package/bin/login.js +3 -4
- package/bin/migrate.js +33 -0
- package/bin/pull.js +4 -4
- package/bin/push.js +1 -1
- package/bin/subscribe.js +58 -66
- package/bin/unsubscribe.js +67 -0
- package/bin/watch.js +4 -3
- package/lib/bind/bindingUtil.js +2 -0
- package/lib/bind/cfKeyProvider.js +5 -1
- package/lib/client/activate.js +3 -3
- package/lib/client/auth_manager.js +2 -2
- package/lib/client/base_command.js +7 -6
- package/lib/client/extend.js +2 -2
- package/lib/client/helper/errors.js +8 -2
- package/lib/client/helper/logging.js +13 -11
- package/lib/client/params.js +3 -0
- package/lib/client/pull.js +1 -1
- package/lib/client/push.js +4 -4
- package/lib/client/settings_manager.js +13 -6
- package/lib/client/subscription.js +81 -0
- package/lib/compile/to-xsuaa.js +25 -19
- package/lib/import/odata/metadataconverter/MetadataConverter.js +3 -3
- package/lib/import/odata/v2parser.js +37 -24
- package/lib/import/odata/v4parser.js +106 -37
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +2 -2
- package/lib/init/template/_merging/registry-mta.js +0 -3
- package/lib/init/template/approuter/files/approuter-configmap.yaml +10 -0
- package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +15 -0
- package/lib/init/template/approuter/files/chart.yaml +4 -0
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/values.yaml.hbs +31 -0
- package/lib/init/template/approuter/index.js +60 -12
- package/lib/init/template/auditlog/index.js +16 -8
- package/lib/init/template/cf-manifest/index.js +3 -3
- package/lib/init/template/connectivity/index.js +19 -10
- package/lib/init/template/destinations/index.js +23 -7
- package/lib/init/template/enterprise-messaging/files/cds.package.json +1 -2
- package/lib/init/template/enterprise-messaging/files/cds.profile.package.json.hbs +1 -2
- package/lib/init/template/enterprise-messaging/files/cdsrc.json +1 -2
- package/lib/init/template/enterprise-messaging/files/cdsrc.json.hbs +1 -2
- package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +31 -0
- package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +1 -0
- package/lib/init/template/enterprise-messaging/index.js +26 -13
- package/lib/init/template/extensibility/index.js +19 -10
- package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
- package/lib/init/template/hana/index.js +58 -22
- package/lib/init/template/helm/chart/templates/NOTES.txt +4 -3
- package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +25 -2
- package/lib/init/template/helm/chart/values.root.schema.json +5 -0
- package/lib/init/template/helm/chart/values.schema.json +27 -0
- package/lib/init/template/helm/files/values.yaml.hbs +1 -1
- package/lib/init/template/helm/index.js +25 -23
- package/lib/init/template/helm/subchart/templates/_helpers.tpl +29 -0
- package/lib/init/template/helm/subchart/templates/api-rule.yaml +5 -5
- package/lib/init/template/helm/subchart/templates/deployment.yaml +12 -9
- package/lib/init/template/helm/subchart/values.md +258 -88
- package/lib/init/template/helm/subchart/values.schema.json +96 -13
- package/lib/init/template/helm/subchart/values.yaml +6 -2
- package/lib/init/template/html5-repo/index.js +28 -11
- package/lib/init/template/java/index.js +5 -5
- package/lib/init/template/kibana-logging/index.js +5 -4
- package/lib/init/template/mta/index.js +2 -2
- package/lib/init/template/mtx/index.js +1 -1
- package/lib/init/template/multitenancy/files/chart.yaml +4 -0
- package/lib/init/template/multitenancy/files/java/mtxs-configmap.yaml +20 -0
- package/lib/init/template/multitenancy/files/java/package.json +6 -9
- package/lib/init/template/multitenancy/files/mtxs-configmap.yaml +15 -0
- package/lib/init/template/multitenancy/files/saas-registry.yaml +17 -19
- package/lib/init/template/multitenancy/files/values.yaml.hbs +47 -0
- package/lib/init/template/multitenancy/files/xs-security.json.hbs +0 -7
- package/lib/init/template/multitenancy/index.js +83 -37
- package/lib/init/template/nodejs/files/.eslintrc +26 -0
- package/lib/init/template/nodejs/files/package.json.hbs +0 -28
- package/lib/init/template/nodejs/index.js +7 -10
- package/lib/init/template/pipeline/index.js +4 -4
- package/lib/init/template/samples/index.js +5 -5
- package/lib/init/template/templateBase.js +1 -4
- package/lib/init/template/toggles/index.js +5 -5
- package/lib/init/template/xsuaa/files/values.yaml.hbs +21 -0
- package/lib/init/template/xsuaa/index.js +34 -24
- package/lib/init/util/md5Tracking.js +113 -0
- package/lib/init/util/merge.js +19 -2
- package/lib/init/util/projectReader.js +15 -12
- package/npm-shrinkwrap.json +630 -1057
- package/package.json +3 -3
|
@@ -2,22 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
function debug(debugText, error) {
|
|
4
4
|
if (process.env.DEBUG) {
|
|
5
|
-
console.log(getMessage(debugText, error));
|
|
5
|
+
console.log(getMessage(debugText, { error }));
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
function getMessage(errorText, error) {
|
|
9
|
+
function getMessage(errorText, { error, help } = {}) {
|
|
10
10
|
let message = errorText ? errorText : '';
|
|
11
|
-
if (
|
|
12
|
-
|
|
11
|
+
if (error) {
|
|
12
|
+
message += '\n' + (
|
|
13
|
+
error.response?.data?.message // response body == error
|
|
14
|
+
|| error.response?.data?.error?.message // response body == { error }
|
|
15
|
+
|| error.message // no response attached
|
|
16
|
+
);
|
|
17
|
+
if (process.env.DEBUG && error.stack) {
|
|
18
|
+
message += '\n' + error.stack;
|
|
19
|
+
}
|
|
13
20
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|| error.response?.data?.error?.message // response body == { error }
|
|
17
|
-
|| error.message // no response attached
|
|
18
|
-
);
|
|
19
|
-
if (process.env.DEBUG && error.stack) {
|
|
20
|
-
message += '\n' + error.stack;
|
|
21
|
+
if (typeof help === 'string') {
|
|
22
|
+
message += require('../../../bin/help').formatHelp(help);
|
|
21
23
|
}
|
|
22
24
|
return message;
|
|
23
25
|
}
|
package/lib/client/params.js
CHANGED
|
@@ -210,6 +210,9 @@ class AllParams extends BasicParams {
|
|
|
210
210
|
this.wsdl = new Param('wsdl', false);
|
|
211
211
|
this.sync = new Param('sync', false);
|
|
212
212
|
|
|
213
|
+
// `login` PARAMS
|
|
214
|
+
this.skipToken = new Param('skipToken', false);
|
|
215
|
+
|
|
213
216
|
// `logout` PARAMS
|
|
214
217
|
this.deleteSettings = new Param('deleteSettings', false);
|
|
215
218
|
this["delete-settings"] = this.deleteSettings.newAlias('delete-settings');
|
package/lib/client/pull.js
CHANGED
|
@@ -41,6 +41,6 @@ module.exports = class Pull extends BaseCommand {
|
|
|
41
41
|
|
|
42
42
|
return axios.post(pullUrl, {}, options)
|
|
43
43
|
.then(response => Buffer.from(response.data))
|
|
44
|
-
.catch(error => this.handleHttpError(error, params, pullUrl));
|
|
44
|
+
.catch(error => this.handleHttpError(error, params, { url: pullUrl }));
|
|
45
45
|
}
|
|
46
46
|
}
|
package/lib/client/push.js
CHANGED
|
@@ -33,12 +33,12 @@ module.exports = class Push extends BaseCommand {
|
|
|
33
33
|
src = local(src);
|
|
34
34
|
} else {
|
|
35
35
|
if (!schemaRegex.test(src)) {
|
|
36
|
-
throw new CliError(`Nonexistent path: ${src}
|
|
36
|
+
throw new CliError(`Nonexistent path: ${src}`, { command: 'push' });
|
|
37
37
|
}
|
|
38
38
|
try {
|
|
39
39
|
content = (await axios.get(src, { responseEncoding: 'base64' })).data;
|
|
40
40
|
} catch (error) {
|
|
41
|
-
this.handleHttpError(error, params, src);
|
|
41
|
+
this.handleHttpError(error, params, { url: src });
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
return { src, content };
|
|
@@ -48,7 +48,7 @@ module.exports = class Push extends BaseCommand {
|
|
|
48
48
|
try {
|
|
49
49
|
return require(join(projectFolder, 'package.json')).name;
|
|
50
50
|
} catch (error) {
|
|
51
|
-
throw new CliError(`Extension project at ${projectFolder} is missing package.json file`, error);
|
|
51
|
+
throw new CliError(`Extension project at ${projectFolder} is missing package.json file`, { error, command: 'push' });
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -60,6 +60,6 @@ module.exports = class Push extends BaseCommand {
|
|
|
60
60
|
extension: tgz,
|
|
61
61
|
tag: extensionName
|
|
62
62
|
}, options)
|
|
63
|
-
.catch(error => this.handleHttpError(error, params, pushUrl));
|
|
63
|
+
.catch(error => this.handleHttpError(error, params, { url: pushUrl }));
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -171,7 +171,7 @@ class SettingsManager {
|
|
|
171
171
|
}
|
|
172
172
|
await this._saveToFile(params.get('projectFolder'), paramsMap);
|
|
173
173
|
|
|
174
|
-
if (!params.has('token')) {
|
|
174
|
+
if (params.get('skipToken') || !params.has('token')) {
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -206,6 +206,9 @@ class SettingsManager {
|
|
|
206
206
|
|
|
207
207
|
params.mergeLower(loadedSettings);
|
|
208
208
|
|
|
209
|
+
if (params.has('username')) {
|
|
210
|
+
params.set('skipToken', true);
|
|
211
|
+
}
|
|
209
212
|
await this.updateUrls(params, logout, loadedAppUrl);
|
|
210
213
|
await this.checkKeytar(params, logout);
|
|
211
214
|
await this.addAuth(params, logout);
|
|
@@ -216,7 +219,7 @@ class SettingsManager {
|
|
|
216
219
|
async function addAppUrlAndSubdomain() {
|
|
217
220
|
function needsSubdomain() {
|
|
218
221
|
// Subdomain needed for token retrieval.
|
|
219
|
-
return params.has('appUrl') && !localhostRegex.test(params.get('appUrl')) && !params.
|
|
222
|
+
return params.has('appUrl') && !localhostRegex.test(params.get('appUrl')) && !params.get('skipToken');
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
// Prefix URL schema
|
|
@@ -273,12 +276,15 @@ class SettingsManager {
|
|
|
273
276
|
|
|
274
277
|
await addAppUrlAndSubdomain();
|
|
275
278
|
|
|
276
|
-
if (!logout) {
|
|
279
|
+
if (!logout && !params.get('skipToken')) {
|
|
277
280
|
await renewTokenUrl();
|
|
278
281
|
}
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
static async checkKeytar(params, logout) {
|
|
285
|
+
if (params.get('skipToken')) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
282
288
|
if (isBas()) {
|
|
283
289
|
if (params.get('tokenStorage') === 'keyring') {
|
|
284
290
|
console.log('NOTE:', capitalize(TOKEN_STORAGE_DESC.keyring), 'not supported on SAP Business Application Studio. Switching to', TOKEN_STORAGE_DESC.plain, '.');
|
|
@@ -286,9 +292,6 @@ class SettingsManager {
|
|
|
286
292
|
}
|
|
287
293
|
return;
|
|
288
294
|
}
|
|
289
|
-
if (params.get('username')) {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
295
|
keytar = requireGlobal('keytar');
|
|
293
296
|
if (keytar) {
|
|
294
297
|
return;
|
|
@@ -378,6 +381,10 @@ class SettingsManager {
|
|
|
378
381
|
params.delete('isEmptyPassword');
|
|
379
382
|
}
|
|
380
383
|
|
|
384
|
+
if (params.get('skipToken')) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
381
388
|
if (params.get('clientid') && !params.get('clientsecret') && !logout) {
|
|
382
389
|
await addClientSecret();
|
|
383
390
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
let cds;
|
|
2
|
+
const BaseCommand = require('./base_command');
|
|
3
|
+
const { CliError } = require('./helper/errors');
|
|
4
|
+
const { ParamCollection } = require('./params');
|
|
5
|
+
const AuthManager = require('./auth_manager');
|
|
6
|
+
const { capitalize } = require('./helper/string_helper');
|
|
7
|
+
|
|
8
|
+
module.exports = class ToggleSubscription extends BaseCommand {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param on {boolean} whether to switch subscription on
|
|
12
|
+
*/
|
|
13
|
+
constructor(on) {
|
|
14
|
+
super();
|
|
15
|
+
const prefix = on ? '' : 'un';
|
|
16
|
+
this.command = prefix + 'subscribe';
|
|
17
|
+
this.gerund = prefix + 'subscribing';
|
|
18
|
+
this.noun = prefix + 'subscription';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run(paramValues, { tenant, local } = {}) {
|
|
22
|
+
cds = require('../cds');
|
|
23
|
+
let params = new ParamCollection(paramValues);
|
|
24
|
+
params.set('skipToken', true); // Token would have insufficient scopes for this command.
|
|
25
|
+
if (local) {
|
|
26
|
+
// Connect to locally-served MTXS
|
|
27
|
+
await this.runWithServer(params, tenant);
|
|
28
|
+
} else {
|
|
29
|
+
// Connect to running MTXS server
|
|
30
|
+
params = await AuthManager.login(params.toValueMap());
|
|
31
|
+
if (!params.has('username')) {
|
|
32
|
+
throw new CliError('Username is required if app URL is given.', { command: this.command });
|
|
33
|
+
}
|
|
34
|
+
await this.runClientOnly(params, tenant);
|
|
35
|
+
}
|
|
36
|
+
console.log(`${capitalize(this.noun)} successful.`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async runClientOnly(params, tenant) {
|
|
40
|
+
console.log(capitalize(this.gerund), { tenant }, 'from', { url: params.get('appUrl') });
|
|
41
|
+
const url = `${params.get('appUrl')}/-/cds/deployment/${this.command}`;
|
|
42
|
+
try {
|
|
43
|
+
await require('axios').post(url, { tenant }, params.get('reqAuth'));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
BaseCommand.handleHttpError(error, params, { url });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async runWithServer(params, tenant) {
|
|
50
|
+
if (!this.hasMtEnv()) {
|
|
51
|
+
throw new CliError('Local multitenant application environment with @sap/cds-mtxs module not found.', { command: this.command });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log(capitalize(this.gerund), { tenant });
|
|
55
|
+
const { 'cds.xt.DeploymentService': deploymentService } = await cds.serve([
|
|
56
|
+
'@sap/cds-mtxs/srv/deployment-service',
|
|
57
|
+
'@sap/cds-mtxs/srv/model-provider'
|
|
58
|
+
]);
|
|
59
|
+
cds.emit('served');
|
|
60
|
+
if (this.command === 'unsubscribe') {
|
|
61
|
+
// MTXS doesn't connect automatically because unsubscribe requires no schema changes
|
|
62
|
+
await cds.connect.to('db');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await deploymentService[this.command](tenant);
|
|
66
|
+
|
|
67
|
+
if (cds.db) {
|
|
68
|
+
await cds.disconnect(cds.env.requires.multitenancy?.t0 ?? 't0'); // REVISIT: Ideally not necessary
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check for application environment.
|
|
74
|
+
* Fails for edge cases (ignored for now):
|
|
75
|
+
* - @sap/cds-mtx application with missing modules (no npm install)
|
|
76
|
+
* - dev scenario with both @sap/cds-mtx and @sap/cds-mtxs installed
|
|
77
|
+
*/
|
|
78
|
+
hasMtEnv() {
|
|
79
|
+
return cds.requires.multitenancy && !cds.mtx;
|
|
80
|
+
}
|
|
81
|
+
}
|
package/lib/compile/to-xsuaa.js
CHANGED
|
@@ -14,31 +14,37 @@ module.exports = function compile_to_xsuaa (model, options) {
|
|
|
14
14
|
const scopes = {}
|
|
15
15
|
const attributes = {}
|
|
16
16
|
|
|
17
|
-
const findUserAttrInExpression = (expr, attributes = []) => {
|
|
18
|
-
if (Array.isArray(expr)) { // descend arrays
|
|
19
|
-
expr.forEach(e => findUserAttrInExpression(e, attributes))
|
|
20
|
-
}
|
|
21
|
-
else if (typeof expr === 'object' && expr.ref) {
|
|
22
|
-
const userIdx = expr.ref.indexOf('$user')
|
|
23
|
-
if (userIdx >= 0 && userIdx < expr.ref.length - 1) {
|
|
24
|
-
const attr = expr.ref[userIdx + 1]
|
|
25
|
-
if (!hardcoded.Attributes.includes(attr)) attributes.push(attr)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return attributes
|
|
29
|
-
}
|
|
30
|
-
|
|
31
17
|
const parseAttributes = (condition) => { // e.g. 'foo = $user.bar or baz = $user.boo'
|
|
32
18
|
if (!condition) return []
|
|
33
19
|
|
|
20
|
+
// first validate basic syntax of expression by parsing it
|
|
21
|
+
let expr
|
|
34
22
|
try {
|
|
35
|
-
|
|
36
|
-
const expr = cds.parse.expr(condition)
|
|
37
|
-
// find paths value following $user, i.e. 'bar' and 'boo'
|
|
38
|
-
return findUserAttrInExpression(expr.xpr)
|
|
23
|
+
expr = cds.parse.expr(condition).xpr
|
|
39
24
|
} catch (err) {
|
|
40
|
-
throw new Error(
|
|
25
|
+
throw new Error(`Invalid expression': ${err.message}`)
|
|
41
26
|
}
|
|
27
|
+
|
|
28
|
+
// Reject expressions like `$user.foo.bar`, as xsuaa attributes are primitive, not structured
|
|
29
|
+
expr.forEach(expr => {
|
|
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`)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Reject expressions like `$user.foo-bar`
|
|
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
|
+
})
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
cds.reflect(model).foreach(def => {
|
|
@@ -446,7 +446,7 @@ class Expression {
|
|
|
446
446
|
if (args.includes('$IsFlags') && attribs.IsFlags === 'true') target.$IsFlags = true;
|
|
447
447
|
if (args.includes('$Alias') && attribs.Alias) target.$Alias = attribs.Alias;
|
|
448
448
|
if (args.includes('$Namespace') && attribs.Namespace) target.$Namespace = attribs.Namespace;
|
|
449
|
-
|
|
449
|
+
if (args.includes('$OpenType') && attribs.OpenType) target.$OpenType = attribs.OpenType;
|
|
450
450
|
if (args.includes('$BaseType') && attribs.BaseType) target.$BaseType = attribs.BaseType;
|
|
451
451
|
if (args.includes('$MaxLength') && attribs.MaxLength != null && attribs.MaxLength.toLowerCase() !== 'max') {
|
|
452
452
|
target.$MaxLength = parseInt(attribs.MaxLength, 10);
|
|
@@ -2003,7 +2003,7 @@ class EntityTypeExpression extends AbstractTypeExpression {
|
|
|
2003
2003
|
constructor(element, expression, context) { super(element, expression, context, 'EntityType'); }
|
|
2004
2004
|
interpret() {
|
|
2005
2005
|
const result = super.interpret();
|
|
2006
|
-
this.buildTypeCollectionAndFacets('$Type', '$BaseType', '$HasStream');
|
|
2006
|
+
this.buildTypeCollectionAndFacets('$Type', '$BaseType', '$HasStream', '$OpenType');
|
|
2007
2007
|
return result;
|
|
2008
2008
|
}
|
|
2009
2009
|
}
|
|
@@ -2012,7 +2012,7 @@ class ComplexTypeExpression extends AbstractTypeExpression {
|
|
|
2012
2012
|
constructor(element, expression, context) { super(element, expression, context, 'ComplexType'); }
|
|
2013
2013
|
interpret() {
|
|
2014
2014
|
const result = super.interpret();
|
|
2015
|
-
this.buildTypeCollectionAndFacets('$Type', '$BaseType', '$HasStream');
|
|
2015
|
+
this.buildTypeCollectionAndFacets('$Type', '$BaseType', '$HasStream', '$OpenType');
|
|
2016
2016
|
return result;
|
|
2017
2017
|
}
|
|
2018
2018
|
}
|
|
@@ -54,6 +54,7 @@ function _initialize(parserContext) {
|
|
|
54
54
|
parserContext.allComplexTypeDocs = {};
|
|
55
55
|
parserContext.allComplexTypeNamespaces = {};
|
|
56
56
|
parserContext.allInheritedComplexTypes = {};
|
|
57
|
+
parserContext.allInheritedEntityTypes = {};
|
|
57
58
|
parserContext.allowedNamespaces = [];
|
|
58
59
|
parserContext.allowAllNamespaces = false;
|
|
59
60
|
}
|
|
@@ -339,13 +340,16 @@ function _extractComplexType(complexType, parserContext) {
|
|
|
339
340
|
complexTypeKey = parserContext.serviceNamespace + "." + complexType[key].Name;
|
|
340
341
|
namespaceAttributes = namespaceAttributeFilter(complexType[key], parserContext);
|
|
341
342
|
baseType = complexType[key].BaseType;
|
|
343
|
+
if (complexType[key].Abstract || complexType[key].OpenType) {
|
|
344
|
+
complexType.open = true;
|
|
345
|
+
}
|
|
342
346
|
} else if (key === "Property") {
|
|
343
347
|
properties = complexType.Property;
|
|
344
348
|
} else if (key === "Documentation") {
|
|
345
349
|
parserContext.allComplexTypeDocs[complexTypeKey] = _parseDocumentationTag(complexType.Documentation);
|
|
346
350
|
}
|
|
347
351
|
});
|
|
348
|
-
parserContext.allComplexTypes[complexTypeKey] =
|
|
352
|
+
parserContext.allComplexTypes[complexTypeKey] = complexType;
|
|
349
353
|
if (complexTypeKey && baseType) {
|
|
350
354
|
parserContext.allInheritedComplexTypes[complexTypeKey] = baseType;
|
|
351
355
|
}
|
|
@@ -381,7 +385,7 @@ function _getServiceEntityProperty(
|
|
|
381
385
|
) {
|
|
382
386
|
let isCollection = -1;
|
|
383
387
|
//preprocessing of datatype in case it is a collection
|
|
384
|
-
if (dataType.substring(0, 10) === 'Collection') {
|
|
388
|
+
if (dataType && dataType.substring(0, 10) === 'Collection') {
|
|
385
389
|
dataType = dataType.substring(11, dataType.length - 1);
|
|
386
390
|
isCollection = 1;
|
|
387
391
|
}
|
|
@@ -483,15 +487,11 @@ function _getServiceEntityProperty(
|
|
|
483
487
|
propertyJson = propertyJson + ',\n"scale":' + 0;
|
|
484
488
|
}
|
|
485
489
|
}
|
|
486
|
-
if (nullable
|
|
490
|
+
if (nullable === "false")
|
|
487
491
|
propertyJson = propertyJson + ',\n"notNull": true';
|
|
488
|
-
}
|
|
489
|
-
else if(nullable=="true" || !nullable){
|
|
490
|
-
propertyJson = propertyJson + ',\n"notNull": false';
|
|
491
|
-
}
|
|
492
492
|
propertyJson = propertyJson + '\n}'
|
|
493
493
|
}
|
|
494
|
-
else{
|
|
494
|
+
else {
|
|
495
495
|
propertyJson = propertyJson + '"type":"' + cdsDataType + '"';
|
|
496
496
|
//if annotations to be added
|
|
497
497
|
if (edmxncdsdatatype[dataType + "_a"] && cdsDataType != "cds.Date") {
|
|
@@ -528,13 +528,8 @@ function _getServiceEntityProperty(
|
|
|
528
528
|
propertyJson += _generateNamespaceAttributes(allowedNamespaceAttributes, true);
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
if(isCollection
|
|
532
|
-
|
|
533
|
-
propertyJson = propertyJson + ',\n"notNull": true';
|
|
534
|
-
}
|
|
535
|
-
else if(nullable=="true" || !nullable){
|
|
536
|
-
propertyJson = propertyJson + ',\n"notNull": false';
|
|
537
|
-
}
|
|
531
|
+
if (isCollection === -1 && nullable === "false") {
|
|
532
|
+
propertyJson = propertyJson + ',\n"notNull": true';
|
|
538
533
|
}
|
|
539
534
|
|
|
540
535
|
if (propertyName)
|
|
@@ -542,11 +537,14 @@ function _getServiceEntityProperty(
|
|
|
542
537
|
return propertyJson;
|
|
543
538
|
}
|
|
544
539
|
|
|
545
|
-
function _getServiceComplexType(complexTypeKey, complexType, parserContext) {
|
|
540
|
+
function _getServiceComplexType(complexTypeKey, complexType, parserContext, isOpenType) {
|
|
546
541
|
let complexTypeCSN = '"' + complexTypeKey + '": {\n';
|
|
547
542
|
let complexTypeProperty;
|
|
548
|
-
let i;
|
|
549
543
|
complexTypeCSN = complexTypeCSN + '"kind": "type",\n';
|
|
544
|
+
// checking if the ComplexType is marked open or acts as a BaseType
|
|
545
|
+
if (isOpenType === true || Object.values(parserContext.allInheritedComplexTypes).indexOf(complexTypeKey) > -1) {
|
|
546
|
+
complexTypeCSN = complexTypeCSN + '"@open": true,\n';
|
|
547
|
+
}
|
|
550
548
|
complexTypeCSN = complexTypeCSN + '"@cds.external": true,\n';
|
|
551
549
|
if (parserContext.allComplexTypeNamespaces[complexTypeKey]) {
|
|
552
550
|
complexTypeCSN += _generateNamespaceAttributes(parserContext.allComplexTypeNamespaces[complexTypeKey], false);
|
|
@@ -557,14 +555,14 @@ function _getServiceComplexType(complexTypeKey, complexType, parserContext) {
|
|
|
557
555
|
if (complexType) {
|
|
558
556
|
if (complexType.length) {
|
|
559
557
|
// More than one complex types
|
|
560
|
-
for (i = 0; i < complexType.length; i++) {
|
|
558
|
+
for (let i = 0; i < complexType.length; i++) {
|
|
561
559
|
complexTypeProperty = complexType[i]._attributes;
|
|
562
560
|
if (complexType[i].Documentation)
|
|
563
561
|
complexTypeProperty.doc = _parseDocumentationTag(complexType[i].Documentation);
|
|
564
562
|
let namespaceAttributes = namespaceAttributeFilter(complexTypeProperty, parserContext);
|
|
565
563
|
const complexTypePropertyNamespace = namespaceAttributes.length ? namespaceAttributes : -1
|
|
566
564
|
|
|
567
|
-
let complexProperty =
|
|
565
|
+
let complexProperty =
|
|
568
566
|
_getServiceEntityProperty(
|
|
569
567
|
complexTypeProperty.Name,
|
|
570
568
|
complexTypeProperty.Type,
|
|
@@ -617,7 +615,7 @@ function _getServiceComplexType(complexTypeKey, complexType, parserContext) {
|
|
|
617
615
|
complexTypeCSN = complexTypeCSN + "}\n";
|
|
618
616
|
if (parserContext.allInheritedComplexTypes[complexTypeKey]) {
|
|
619
617
|
complexTypeCSN = complexTypeCSN + ",\n";
|
|
620
|
-
complexTypeCSN = complexTypeCSN +
|
|
618
|
+
complexTypeCSN = complexTypeCSN +
|
|
621
619
|
'"includes": ["' + parserContext.allInheritedComplexTypes[complexTypeKey] + '"]\n';
|
|
622
620
|
}
|
|
623
621
|
complexTypeCSN = complexTypeCSN + "}\n";
|
|
@@ -629,10 +627,12 @@ function _getServiceComplexTypes(parserContext) {
|
|
|
629
627
|
let complexTypesKeys = Object.keys(parserContext.allComplexTypes);
|
|
630
628
|
let complexTypeKey;
|
|
631
629
|
let complexType;
|
|
630
|
+
let isOpenType;
|
|
632
631
|
for (let i = 0; i < complexTypesKeys.length; i++) {
|
|
633
632
|
complexTypeKey = complexTypesKeys[i];
|
|
634
|
-
complexType = parserContext.allComplexTypes[
|
|
635
|
-
|
|
633
|
+
complexType = parserContext.allComplexTypes[complexTypeKey].Property;
|
|
634
|
+
isOpenType = parserContext.allComplexTypes[complexTypeKey].open;
|
|
635
|
+
complexTypeCSN = complexTypeCSN + _getServiceComplexType(complexTypeKey, complexType, parserContext, isOpenType);
|
|
636
636
|
if (i !== complexTypesKeys.length - 1) {
|
|
637
637
|
complexTypeCSN = complexTypeCSN + ",\n";
|
|
638
638
|
} else {
|
|
@@ -787,6 +787,7 @@ function _parseEntityAttributes(attributes, parserContext) {
|
|
|
787
787
|
entityAttributes.Name = attributes.Name;
|
|
788
788
|
entityAttributes.BaseType = attributes.BaseType;
|
|
789
789
|
entityAttributes.Abstract = attributes.Abstract;
|
|
790
|
+
entityAttributes.OpenType = attributes.OpenType;
|
|
790
791
|
entityAttributes.allowedNamespaceAttributes = namespaceAttributeFilter(attributes, parserContext);
|
|
791
792
|
|
|
792
793
|
return entityAttributes;
|
|
@@ -1304,6 +1305,11 @@ function _getServiceEntityNavigationProperty(navPropAttributes, mockServerUc, pa
|
|
|
1304
1305
|
}
|
|
1305
1306
|
// annotation for on conditions
|
|
1306
1307
|
csn = csn + `"@cds.ambiguous": "missing on condition?",\n`;
|
|
1308
|
+
|
|
1309
|
+
//Code for notNull working but change is not reflecting when we use: cds compile {file-path} -2 edmx > {newFileName}
|
|
1310
|
+
/*if (navPropAttributes.Nullable === "false")
|
|
1311
|
+
csn = csn + '"notNull": true,\n';*/
|
|
1312
|
+
|
|
1307
1313
|
// if documentation exists for navigation property
|
|
1308
1314
|
if (navPropDoc)
|
|
1309
1315
|
csn = csn + '"doc":' + ` "${navPropDoc}",\n`;
|
|
@@ -1396,8 +1402,8 @@ function _constructServiceEntity(
|
|
|
1396
1402
|
csnEntity = csnEntity + '"@cds.persistence.skip": true,\n';
|
|
1397
1403
|
}
|
|
1398
1404
|
|
|
1399
|
-
if (entityAttributes.Abstract && entityAttributes.Abstract.toUpperCase() === "TRUE") {
|
|
1400
|
-
csnEntity = csnEntity + '"
|
|
1405
|
+
if ((entityAttributes.Abstract && entityAttributes.Abstract.toUpperCase() === "TRUE") || (entityAttributes.OpenType && entityAttributes.OpenType.toUpperCase() === "TRUE")) {
|
|
1406
|
+
csnEntity = csnEntity + '"@open": true,\n'
|
|
1401
1407
|
}
|
|
1402
1408
|
|
|
1403
1409
|
/* Merging the namespace list from entity set and entity type
|
|
@@ -1525,6 +1531,7 @@ function _parseServiceEntity(entityName, entity, ignorePersistenceSkip, mockServ
|
|
|
1525
1531
|
// If inherited entity found
|
|
1526
1532
|
if (entityAttributes.BaseType) {
|
|
1527
1533
|
parserContext.allEntities.push(_extractEntityFromNamespace(entityAttributes.BaseType));
|
|
1534
|
+
parserContext.allInheritedEntityTypes[_extractEntityFromNamespace(entityAttributes.BaseType)] = entity["_attributes"].Name;
|
|
1528
1535
|
}
|
|
1529
1536
|
// adding blob element if entity has m:HasStream as true
|
|
1530
1537
|
const checkProperty = entityAttributes["allowedNamespaceAttributes"].filter(e => e["m.HasStream"] === 'true');
|
|
@@ -1614,6 +1621,12 @@ function _getServiceEntitites(entityJson = [], ignorePersistenceSkip, mockServer
|
|
|
1614
1621
|
if (entitiesWithNames[i].length) {
|
|
1615
1622
|
parsedEntities[i] = _parsingServiceEntities(entitiesWithNames[i],
|
|
1616
1623
|
ignorePersistenceSkip, mockServerUc, parserContext);
|
|
1624
|
+
// adding @open annotation to BaseTypes
|
|
1625
|
+
for (let j = 0; j < parsedEntities[i].length; j++) {
|
|
1626
|
+
if (parserContext.allInheritedEntityTypes[_extractEntityFromNamespace(parserContext.allEntitySetMap[entitiesWithNames[i][j].name])] != undefined || parserContext.allInheritedEntityTypes[entitiesWithNames[i][j].name]) {
|
|
1627
|
+
parsedEntities[i][j] = parsedEntities[i][j].replace(/}$/, ',"@open": true' + '}');
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1617
1630
|
}
|
|
1618
1631
|
}
|
|
1619
1632
|
// if entities from entity set and entity type both are parsed
|