@sap/cds-dk 6.7.1 → 6.8.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 -0
- package/bin/deploy/index.js +1 -1
- package/bin/import.js +5 -3
- package/bin/login.js +29 -9
- package/bin/migrate.js +6 -2
- package/bin/watch.js +16 -0
- package/lib/bind/bindingUtil.js +2 -2
- package/lib/bind/userCdsrcJsonStorageProvider.js +1 -1
- package/lib/client/auth_manager.js +3 -1
- package/lib/client/helper/errors.js +6 -1
- package/lib/client/params.js +22 -20
- package/lib/client/settings_manager.js +17 -3
- package/lib/compile/openapi/csdl2openapi.js +4 -1
- package/lib/import/asyncapi/importAsyncAPI.js +32 -0
- package/lib/import/asyncapi/index.js +15 -0
- package/lib/import/asyncapi/parserContext.js +351 -0
- package/lib/import/asyncapi/utils.js +18 -0
- package/lib/import/common.js +21 -67
- package/lib/import/importapi.js +23 -2
- package/lib/import/message.js +11 -1
- package/lib/import/odata/index.js +14 -11
- package/lib/import/odata/metadataconverter/MetadataConverter.js +35 -35
- package/lib/import/odata/util/MultiMap.js +5 -5
- package/lib/import/odata/v2parser.js +1 -0
- package/lib/import/odata/v4parser.js +1281 -1139
- package/lib/init/constants.js +10 -3
- package/lib/init/template/_merging/registry-mta.js +33 -36
- package/lib/init/template/approuter/files/mtxs-configmap-java.yaml +20 -0
- package/lib/init/template/approuter/files/mtxs-configmap-nodejs.yaml +12 -0
- package/lib/init/template/approuter/index.js +24 -33
- package/lib/init/template/auditlog/files/package.json.hbs +27 -0
- package/lib/init/template/auditlog/index.js +12 -6
- package/lib/init/template/cf-manifest/index.js +1 -1
- package/lib/init/template/connectivity/index.js +1 -1
- package/lib/init/template/destinations/index.js +4 -2
- package/lib/init/template/enterprise-messaging/files/package.json.hbs +19 -0
- package/lib/init/template/enterprise-messaging/index.js +15 -13
- package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +31 -0
- package/lib/init/template/enterprise-messaging-shared/files/mta.yaml.hbs +15 -0
- package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +22 -0
- package/lib/init/template/enterprise-messaging-shared/files/values.yaml +7 -0
- package/lib/init/template/enterprise-messaging-shared/index.js +51 -0
- package/lib/init/template/extensibility/files/package.json.hbs +20 -0
- package/lib/init/template/extensibility/index.js +4 -16
- package/lib/init/template/file-based-messaging/files/package.json.hbs +19 -0
- package/lib/init/template/file-based-messaging/index.js +25 -0
- package/lib/init/template/hana/files/package.json.hbs +33 -0
- package/lib/init/template/hana/index.js +38 -65
- package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +0 -12
- package/lib/init/template/helm/chart/templates/_helpers.tpl +1 -121
- package/lib/init/template/helm/index.js +1 -1
- package/lib/init/template/html5-repo/index.js +1 -1
- package/lib/init/template/java/index.js +2 -2
- package/lib/init/template/kibana-logging/files/package.json.hbs +17 -0
- package/lib/init/template/kibana-logging/index.js +6 -19
- package/lib/init/template/lint/index.js +1 -1
- package/lib/init/template/mta/index.js +2 -2
- package/lib/init/template/multitenancy/files/mtxs-configmap-java.yaml +3 -5
- package/lib/init/template/multitenancy/files/mtxs-configmap-nodejs.yaml +3 -10
- package/lib/init/template/multitenancy/files/package.json.hbs +5 -4
- package/lib/init/template/multitenancy/index.js +22 -19
- package/lib/init/template/nodejs/files/.vscode/launch.json +11 -3
- package/lib/init/template/nodejs/index.js +1 -1
- package/lib/init/template/pipeline/index.js +1 -1
- package/lib/init/template/redis-messaging/files/mta.yaml.hbs +14 -0
- package/lib/init/template/redis-messaging/files/package.json.hbs +22 -0
- package/lib/init/template/redis-messaging/index.js +50 -0
- package/lib/init/template/samples/index.js +3 -3
- package/lib/init/template/sqlite/files/package.json.hbs +12 -4
- package/lib/init/template/sqlite/index.js +5 -1
- package/lib/init/template/toggles/files/package.json.hbs +20 -0
- package/lib/init/template/toggles/index.js +3 -9
- package/lib/init/template/xsuaa/files/package.json.hbs +28 -0
- package/lib/init/template/xsuaa/index.js +8 -23
- package/lib/init/util/{md5Tracking.js → md5.js} +1 -1
- package/lib/init/util/merge.js +4 -9
- package/lib/init/util/{mvnArchetypeUtil.js → mvn.js} +3 -0
- package/lib/init/util/projectReader.js +25 -107
- package/lib/init/util/xml.js +56 -0
- package/lib/lint/checks.js +1 -1
- package/lib/lint/io.js +1 -1
- package/lib/{init/util/commandUtil.js → util/command.js} +4 -2
- package/lib/{init/util/fsUtil.js → util/fs.js} +2 -2
- package/npm-shrinkwrap.json +335 -2212
- package/package.json +5 -4
- package/lib/init/template/approuter/files/cds.cdsrc.json +0 -7
- package/lib/init/template/approuter/files/cds.cdsrc.json.hbs +0 -9
- package/lib/init/template/approuter/files/cds.package.json +0 -9
- package/lib/init/template/approuter/files/cds.package.json.hbs +0 -11
- package/lib/init/template/auditlog/files/dependencies.package.json +0 -5
- package/lib/init/template/enterprise-messaging/files/cds.package.json +0 -9
- package/lib/init/template/enterprise-messaging/files/cds.profile.package.json.hbs +0 -11
- package/lib/init/template/enterprise-messaging/files/cdsrc.json +0 -7
- package/lib/init/template/enterprise-messaging/files/cdsrc.json.hbs +0 -9
- package/lib/init/template/extensibility/files/cds.package.json +0 -7
- package/lib/init/template/extensibility/files/cds.package.json.hbs +0 -9
- package/lib/init/template/extensibility/files/cdsrc.json +0 -5
- package/lib/init/template/extensibility/files/cdsrc.json.hbs +0 -7
- package/lib/init/template/hana/files/java/cds.json.hbs +0 -7
- package/lib/init/template/hana/files/java/cds.profile.json.hbs +0 -12
- package/lib/init/template/hana/files/java/cds.shortcut.json.hbs +0 -5
- package/lib/init/template/hana/files/java/cds.shortcut.profile.json.hbs +0 -8
- package/lib/init/template/hana/files/nodejs/cds.json.hbs +0 -9
- package/lib/init/template/hana/files/nodejs/cds.profile.json.hbs +0 -14
- package/lib/init/template/hana/files/nodejs/cds.shortcut.json.hbs +0 -7
- package/lib/init/template/hana/files/nodejs/cds.shortcut.profile.json.hbs +0 -10
- package/lib/init/template/hana/files/nodejs/dependencies.package.json +0 -5
- package/lib/init/template/helm/chart/templates/_sapcp_helpers.tpl +0 -103
- package/lib/init/template/kibana-logging/files/cds.profile.package.json.hbs +0 -9
- package/lib/init/template/toggles/files/cds.package.json +0 -7
- package/lib/init/template/toggles/files/cds.package.json.hbs +0 -9
- package/lib/init/template/toggles/files/cdsrc.json +0 -5
- package/lib/init/template/toggles/files/cdsrc.json.hbs +0 -7
- package/lib/init/template/xsuaa/files/cds.cdsrc.json +0 -7
- package/lib/init/template/xsuaa/files/cds.cdsrc.json.hbs +0 -9
- package/lib/init/template/xsuaa/files/cds.package.json +0 -9
- package/lib/init/template/xsuaa/files/cds.package.json.hbs +0 -11
- package/lib/init/template/xsuaa/files/dependencies.package.json +0 -6
- package/lib/init/util/versionCompare.js +0 -73
- /package/lib/init/template/hana/files/{java/db → db}/package.json +0 -0
- /package/lib/init/template/hana/files/{common/src → src}/.hdiconfig +0 -0
- /package/lib/init/template/hana/files/{common/undeploy.json → undeploy.json} +0 -0
- /package/lib/init/util/{templateUtil.js → template.js} +0 -0
|
@@ -9,58 +9,52 @@ let common = require("../common");
|
|
|
9
9
|
let versionInfo = require("../../../package.json").version;
|
|
10
10
|
let Multimap = require("./util/MultiMap");
|
|
11
11
|
const ignoreObjects = ["$Reference"]; // add all the objects that needs to be ignored for parsing.
|
|
12
|
-
const validEdmDatatypes = ["Edm.DateTimeOffset"];
|
|
13
12
|
const { warn } = require("../../util/term");
|
|
14
13
|
const MetadataConverterFactory = require("./metadataconverter/MetadataConverterFactory");
|
|
15
14
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"Edm.SByte_a": "cds.Integer",
|
|
40
|
-
"Edm.Single_a": "cds.Double",
|
|
41
|
-
"Edm.Stream_a": "cds.LargeBinary",
|
|
42
|
-
"Edm.DateTimeOffset_a": "cds.Timestamp",
|
|
15
|
+
const edmPrimitiveTypes = [
|
|
16
|
+
'Edm.Binary', 'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset',
|
|
17
|
+
'Edm.Decimal', 'Edm.Double', 'Edm.Duration', 'Edm.Guid', 'Edm.Int16', 'Edm.Int32',
|
|
18
|
+
'Edm.Int64', 'Edm.SByte', 'Edm.Single', 'Edm.Stream', 'Edm.String', 'Edm.TimeOfDay',
|
|
19
|
+
'Edm.Geography', 'Edm.GeographyPoint', 'Edm.GeographyLineString', 'Edm.GeographyPolygon',
|
|
20
|
+
'Edm.GeographyMultiPoint', 'Edm.GeographyMultiLineString', 'Edm.GeographyMultiPolygon',
|
|
21
|
+
'Edm.GeographyCollection', 'Edm.Geometry', 'Edm.GeometryPoint', 'Edm.GeometryLineString',
|
|
22
|
+
'Edm.GeometryPolygon', 'Edm.GeometryMultiPoint', 'Edm.GeometryMultiLineString',
|
|
23
|
+
'Edm.GeometryMultiPolygon', 'Edm.GeometryCollection'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const edmToCdsTypeMapping = {
|
|
27
|
+
'Edm.Binary': 'cds.LargeBinary',
|
|
28
|
+
'Edm.Boolean': 'cds.Boolean',
|
|
29
|
+
'Edm.Date': 'cds.Date',
|
|
30
|
+
'Edm.Decimal': 'cds.Decimal',
|
|
31
|
+
'Edm.Double': 'cds.Double',
|
|
32
|
+
'Edm.Guid': 'cds.UUID',
|
|
33
|
+
'Edm.Int16': 'cds.Integer',
|
|
34
|
+
'Edm.Int32': 'cds.Integer',
|
|
35
|
+
'Edm.Int64': 'cds.Integer64',
|
|
36
|
+
'Edm.String': 'cds.LargeString',
|
|
37
|
+
'Edm.TimeOfDay': 'cds.Time',
|
|
43
38
|
};
|
|
44
39
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
];
|
|
40
|
+
const edmToCdsTypeMappingWithMaxLength = {
|
|
41
|
+
'Edm.Binary': 'cds.Binary',
|
|
42
|
+
'Edm.Byte': 'cds.Binary', // couldn't find any example
|
|
43
|
+
'Edm.String': 'cds.String'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const edmToCdsTypeMappingWithPrecision = {
|
|
47
|
+
'Edm.Decimal': 'cds.Decimal'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const edmToCdsTypeSpecialHandling = {
|
|
51
|
+
'Edm.Byte': 'cds.Integer',
|
|
52
|
+
'Edm.DateTimeOffset': ['cds.DateTime', 'cds.Timestamp'],
|
|
53
|
+
'Edm.Double': 'cds.Double', // couldn't find an example
|
|
54
|
+
'Edm.SByte': 'cds.Integer',
|
|
55
|
+
'Edm.Single': 'cds.Double',
|
|
56
|
+
'Edm.Stream': 'cds.LargeBinary'
|
|
57
|
+
};
|
|
64
58
|
|
|
65
59
|
const known_vocabularies = {
|
|
66
60
|
'Org.OData.Authorization.V1': 'Authorization',
|
|
@@ -69,15 +63,20 @@ const known_vocabularies = {
|
|
|
69
63
|
'Org.OData.Capabilities.V1': 'Capabilities',
|
|
70
64
|
'Org.OData.Validation.V1': 'Validation',
|
|
71
65
|
'Org.OData.Measures.V1': 'Measures',
|
|
66
|
+
'Org.OData.JSON.V1': 'JSON',
|
|
72
67
|
'Org.OData.Repeatability.V1': 'Repeatability',
|
|
73
68
|
|
|
74
69
|
'com.sap.vocabularies.Analytics.v1': 'Analytics',
|
|
75
70
|
'com.sap.vocabularies.CodeList.v1': 'CodeList',
|
|
76
71
|
'com.sap.vocabularies.Common.v1': 'Common',
|
|
77
72
|
'com.sap.vocabularies.Communication.v1': 'Communication',
|
|
73
|
+
'com.sap.vocabularies.DataIntegration.v1': 'DataIntegration',
|
|
78
74
|
'com.sap.vocabularies.Graph.v1': 'Graph',
|
|
75
|
+
'com.sap.vocabularies.Hierarchy.v1': 'Hierarchy',
|
|
79
76
|
'com.sap.vocabularies.HTML5.v1': 'HTML5',
|
|
80
77
|
'com.sap.vocabularies.ODM.v1': 'ODM',
|
|
78
|
+
'com.sap.vocabularies.Offline.v1': 'Offline',
|
|
79
|
+
'com.sap.vocabularies.PDF.v1': 'PDF',
|
|
81
80
|
'com.sap.vocabularies.PersonalData.v1': 'PersonalData',
|
|
82
81
|
'com.sap.vocabularies.Session.v1': 'Session',
|
|
83
82
|
'com.sap.vocabularies.UI.v1': 'UI',
|
|
@@ -85,20 +84,25 @@ const known_vocabularies = {
|
|
|
85
84
|
|
|
86
85
|
function _initialize(parserContext) {
|
|
87
86
|
parserContext.unboundedActions = {};
|
|
88
|
-
parserContext.boundedActions = {};
|
|
89
87
|
parserContext.unboundedFunctions = {};
|
|
90
|
-
parserContext.boundedFunctions = {};
|
|
91
88
|
parserContext.entityTypes = [];
|
|
92
89
|
parserContext.entityTypeOpenEntries = [];
|
|
93
90
|
parserContext.complexTypeOpenEntries = [];
|
|
94
|
-
parserContext.
|
|
95
|
-
parserContext.
|
|
96
|
-
parserContext.
|
|
97
|
-
parserContext.
|
|
98
|
-
parserContext.
|
|
99
|
-
parserContext.
|
|
100
|
-
parserContext.
|
|
101
|
-
parserContext.
|
|
91
|
+
parserContext.complexTypes = [];
|
|
92
|
+
parserContext.enumTypes = [];
|
|
93
|
+
parserContext.typeDefinitions = [];
|
|
94
|
+
parserContext.entityContainerName = "";
|
|
95
|
+
parserContext.entityToEntitySetMap = new Multimap();
|
|
96
|
+
parserContext.entityToSingletonMap = new Multimap();
|
|
97
|
+
parserContext.actionToActionImportMap = new Multimap();
|
|
98
|
+
parserContext.functionToFunctionImportMap = new Multimap();
|
|
99
|
+
parserContext.primarySchema = "";
|
|
100
|
+
parserContext.schemaAliasToNamespace = {};
|
|
101
|
+
parserContext.schemaToSchemaDataIndex = {};
|
|
102
|
+
parserContext.vocabAliasToNamespace = {};
|
|
103
|
+
parserContext.entityToBoundedActions = {};
|
|
104
|
+
parserContext.entityToBoundedFunctions = {};
|
|
105
|
+
parserContext.entityContainerAnnotations = {};
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
function _isJson(edmx) {
|
|
@@ -112,15 +116,47 @@ function _isJson(edmx) {
|
|
|
112
116
|
return isJson;
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
function _extractSchemaNameAndElementName(name) {
|
|
120
|
+
let lastIndex = name.lastIndexOf('.');
|
|
121
|
+
let schemaName = name.substring(0, lastIndex);
|
|
122
|
+
let elementName = name.substring(lastIndex + 1);
|
|
123
|
+
return [schemaName, elementName];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {String} fqName
|
|
128
|
+
* @param {String} schemaName
|
|
129
|
+
* @param {Object} parserContext
|
|
130
|
+
* @returns index of schemaName in the schemaDataList
|
|
131
|
+
*/
|
|
132
|
+
function _getIndexAndNamespaceOfSchema(fqName, schemaName, parserContext) {
|
|
133
|
+
let aliasNameMapping = parserContext.schemaAliasToNamespace;
|
|
134
|
+
let schemaIndexMap = parserContext.schemaToSchemaDataIndex;
|
|
135
|
+
|
|
136
|
+
// if prefix is schema `alias` value
|
|
137
|
+
if (Object.keys(aliasNameMapping).includes(schemaName)) {
|
|
138
|
+
schemaName = aliasNameMapping[schemaName];
|
|
139
|
+
return [schemaIndexMap[schemaName], schemaName];
|
|
140
|
+
}
|
|
141
|
+
// if the prefix is schema `namespace` value
|
|
142
|
+
else if (Object.keys(schemaIndexMap).includes(schemaName)) {
|
|
143
|
+
return [schemaIndexMap[schemaName], schemaName];
|
|
144
|
+
}
|
|
145
|
+
// the schema namespace is not resolvable, throw error
|
|
146
|
+
else {
|
|
147
|
+
throw new Error(messages.UNRESOLVED_SCHEMA_NAMESPACE + fqName );
|
|
118
148
|
}
|
|
119
|
-
return false;
|
|
120
149
|
}
|
|
121
150
|
|
|
122
|
-
function
|
|
123
|
-
|
|
151
|
+
function _resolveAliasedElementName(fqName, parserContext) {
|
|
152
|
+
let [schemaName, elementName] = _extractSchemaNameAndElementName(fqName);
|
|
153
|
+
[, schemaName] = _getIndexAndNamespaceOfSchema(fqName, schemaName, parserContext);
|
|
154
|
+
|
|
155
|
+
return schemaName + '.' + elementName;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function _isValidJSON(jsonObj, parserContext, context) {
|
|
159
|
+
if (jsonObj.$Version && _hasEntityContainer(jsonObj, parserContext, context) && jsonObj[parserContext.primarySchema]) {
|
|
124
160
|
return true;
|
|
125
161
|
}
|
|
126
162
|
else {
|
|
@@ -128,17 +164,17 @@ function _isValidJSON(jsonObj, parserContext) {
|
|
|
128
164
|
}
|
|
129
165
|
}
|
|
130
166
|
|
|
131
|
-
function _hasEntityContainer(jsonObj, parserContext) {
|
|
167
|
+
function _hasEntityContainer(jsonObj, parserContext, context) {
|
|
132
168
|
let jsonObjEntityContainer = jsonObj.$EntityContainer;
|
|
133
169
|
try {
|
|
134
170
|
if (jsonObjEntityContainer) {
|
|
135
171
|
// scenario: Schema Namespace="A.B.C.D" & jsonObjEntityContainer is "A.B.C.D.EntityContainerName"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
[parserContext.primarySchema, parserContext.entityContainerName] = _extractSchemaNameAndElementName(jsonObjEntityContainer);
|
|
173
|
+
// required for `--keep-namespace` scenario
|
|
174
|
+
context.schemaNamespace = parserContext.primarySchema;
|
|
139
175
|
}
|
|
140
|
-
if (jsonObj[parserContext.
|
|
141
|
-
jsonObj[parserContext.
|
|
176
|
+
if (jsonObj[parserContext.primarySchema][parserContext.entityContainerName] &&
|
|
177
|
+
jsonObj[parserContext.primarySchema][parserContext.entityContainerName] instanceof Object
|
|
142
178
|
) {
|
|
143
179
|
return true;
|
|
144
180
|
}
|
|
@@ -147,164 +183,461 @@ function _hasEntityContainer(jsonObj, parserContext) {
|
|
|
147
183
|
}
|
|
148
184
|
}
|
|
149
185
|
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
186
|
+
function _updateElementObjectForCollection(dataTypeObj, isPrimaryKey, propertyDataType) {
|
|
187
|
+
if (isPrimaryKey) {
|
|
188
|
+
throw new Error(messages.COLLECTION_IN_KEY);
|
|
189
|
+
}
|
|
190
|
+
if (propertyDataType === 'Edm.Stream') {
|
|
191
|
+
throw new Error(messages.STREAM_NO_COLLECTION);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
dataTypeObj.items = {};
|
|
195
|
+
|
|
196
|
+
Object.keys(dataTypeObj).forEach(objKey => {
|
|
197
|
+
if (!objKey.startsWith('@') && !(dataTypeObj[objKey] instanceof Object)) {
|
|
198
|
+
dataTypeObj.items[objKey] = dataTypeObj[objKey];
|
|
199
|
+
delete dataTypeObj[objKey];
|
|
154
200
|
}
|
|
155
201
|
});
|
|
156
202
|
}
|
|
157
203
|
|
|
158
|
-
function
|
|
159
|
-
|
|
160
|
-
|
|
204
|
+
function _updateElementObjectForDefaultValue(dataTypeObj, defaultvalue) {
|
|
205
|
+
// for collection, default value isin't expected
|
|
206
|
+
if (dataTypeObj.items === undefined) {
|
|
207
|
+
dataTypeObj.default = {};
|
|
208
|
+
dataTypeObj.default.val = (defaultvalue) ? defaultvalue : null;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.log(warn("Collection type cannot have a Default value."));
|
|
212
|
+
}
|
|
161
213
|
}
|
|
162
214
|
|
|
163
|
-
function
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
property.items = type;
|
|
173
|
-
} else {
|
|
174
|
-
Object.assign(property, type);
|
|
215
|
+
function _updateDataTypeObjectForMaxLength(dataTypeObj, propertyDataType, maxLength) {
|
|
216
|
+
const mappedValue = edmToCdsTypeMappingWithMaxLength[propertyDataType];
|
|
217
|
+
|
|
218
|
+
if (mappedValue) {
|
|
219
|
+
dataTypeObj.type = mappedValue
|
|
220
|
+
dataTypeObj.length = maxLength;
|
|
221
|
+
}
|
|
222
|
+
if (propertyDataType === 'Edm.Binary' && maxLength >= 5000) {
|
|
223
|
+
console.log(warn("MaxLength for type Edm.Binary should not exceed 5000"));
|
|
175
224
|
}
|
|
176
225
|
}
|
|
177
226
|
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
227
|
+
function _updateDataTypeObjectForPrecision(dataTypeObj, propertyDataType, precision, scale) {
|
|
228
|
+
const mappedValue = edmToCdsTypeMappingWithPrecision[propertyDataType];
|
|
229
|
+
|
|
230
|
+
if (mappedValue) {
|
|
231
|
+
dataTypeObj.type = mappedValue;
|
|
232
|
+
if (Number(precision) > 0) {
|
|
233
|
+
dataTypeObj.precision = precision;
|
|
234
|
+
}
|
|
235
|
+
if (Number(scale) > 0 || scale === 'floating' || scale === 'variable') {
|
|
236
|
+
dataTypeObj.scale = scale;
|
|
188
237
|
}
|
|
189
|
-
typeProperties["notNull"] = true;
|
|
190
238
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function _updateDataTypeObjectForSpecialHandling(dataTypeObj, propertyDataType, maxLength, precision) {
|
|
242
|
+
if (propertyDataType === 'Edm.Byte' && maxLength) {
|
|
243
|
+
return; // already taken care with direct mapping with maxlength so returning
|
|
244
|
+
}
|
|
245
|
+
else if (['Edm.DateTimeOffset', 'Edm.Double'].includes(propertyDataType)) {
|
|
246
|
+
if (propertyDataType === 'Edm.Double' && precision) {
|
|
247
|
+
dataTypeObj['@odata.Precision'] = precision;
|
|
248
|
+
}
|
|
249
|
+
if (propertyDataType === 'Edm.DateTimeOffset') {
|
|
250
|
+
dataTypeObj['@odata.Precision'] = precision;
|
|
251
|
+
dataTypeObj['@odata.Type'] = propertyDataType;
|
|
252
|
+
dataTypeObj.type = (precision) ?
|
|
253
|
+
edmToCdsTypeSpecialHandling[propertyDataType][1] :
|
|
254
|
+
edmToCdsTypeSpecialHandling[propertyDataType][0];
|
|
194
255
|
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
else if (propertyDataType === 'Edm.Stream') {
|
|
259
|
+
dataTypeObj['@Core.MediaType'] = 'application/octet-stream';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
dataTypeObj['@odata.Type'] = propertyDataType;
|
|
263
|
+
dataTypeObj.type = edmToCdsTypeSpecialHandling[propertyDataType];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function _captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetObject) {
|
|
267
|
+
/**
|
|
268
|
+
* For scenarios like:
|
|
269
|
+
* <Annotation Term="Core.Computed" />
|
|
270
|
+
* Here, 'annotationObject' would be null but in CSN we need to treat it as:
|
|
271
|
+
* @Core.Computed : true
|
|
272
|
+
*/
|
|
273
|
+
if (annotationObject === null) {
|
|
274
|
+
annotationObject = true;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* if anntationObject is empty list or empty object, we won't capture it.
|
|
278
|
+
* eg., <Annotation Term="Core.OptimisticConcurrency"> <Collection /> </Annotation>
|
|
279
|
+
* TODO: we might have to revisit it later for nested scenario.
|
|
280
|
+
*/
|
|
281
|
+
if ((annotationObject instanceof Array && annotationObject.length === 0) ||
|
|
282
|
+
(annotationObject instanceof Object && Object.keys(annotationObject).length === 0)
|
|
283
|
+
) {
|
|
284
|
+
return;
|
|
195
285
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
286
|
+
/**
|
|
287
|
+
* <Annotation Term="Common.Text" Path="name"/> --> annotationTermObject = { "=": annotationTermObject.$Path };
|
|
288
|
+
*/
|
|
289
|
+
if (annotationObject.$Path) {
|
|
290
|
+
annotationObject = { "=": annotationObject.$Path };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
csnDefsTargetObject[targetString] = annotationObject;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function _replaceSpecialCharacters(text) {
|
|
297
|
+
return text.replace(/(?:\\[rn]|[\r\n]+)+/gm, "\n").replace(/\s+/g, ' ').replace(/"/g, """).trim();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function _resolveCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
301
|
+
if (annotationTerm === "@Core.Description" || annotationTerm === "@Core.LongDescription") {
|
|
302
|
+
// we don't capture `null` value for doc
|
|
303
|
+
if (annotationTermObject === null) return;
|
|
304
|
+
|
|
305
|
+
annotationTermObject = _replaceSpecialCharacters(annotationTermObject);
|
|
306
|
+
|
|
307
|
+
if (csnDefsTargetObject.doc) {
|
|
308
|
+
if (annotationTerm === "@Core.Description") {
|
|
309
|
+
annotationTermObject = annotationTermObject + '\n\n' + csnDefsTargetObject.doc;
|
|
310
|
+
} else {
|
|
311
|
+
annotationTermObject = csnDefsTargetObject.doc + '\n\n' + annotationTermObject;
|
|
202
312
|
}
|
|
203
|
-
typeProperties.type = edmxncdsdatatype[propertyType.$Type + '_m'];
|
|
204
|
-
} else {
|
|
205
|
-
typeProperties.type = 'cds.String'; // if type is missing, then it is String type
|
|
206
313
|
}
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
314
|
+
return _captureAnnotationInCSN('doc', annotationTermObject, csnDefsTargetObject);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* <Annotation Term="Core.XYZ"/>
|
|
318
|
+
* For such scenario, mark the `anntationTermObject` as `true`
|
|
319
|
+
*/
|
|
320
|
+
if (annotationTermObject === null) annotationTermObject = true;
|
|
321
|
+
|
|
322
|
+
if (annotationTerm === "@Core.OptionalParameter") {
|
|
323
|
+
_updateElementObjectForDefaultValue(csnDefsTargetObject, annotationTermObject.DefaultValue);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function _flattenAnnotationObject(annotationPrefix, annotationObject) {
|
|
331
|
+
let flattenedResult = [];
|
|
332
|
+
|
|
333
|
+
Object.entries(annotationObject).forEach(annotation => {
|
|
334
|
+
let currentResult = [];
|
|
335
|
+
/**
|
|
336
|
+
* if annotation looks like:
|
|
337
|
+
* { "#" : <value> } or { "=" : <value> }
|
|
338
|
+
* don't flatten because it is for the `EnumMember` & `Path` use cases respectively
|
|
339
|
+
*/
|
|
340
|
+
if (annotation[0] === '#' || annotation[0] === '=') {
|
|
341
|
+
currentResult[0] = annotationPrefix;
|
|
342
|
+
currentResult[1] = annotationObject;
|
|
212
343
|
} else {
|
|
213
|
-
|
|
214
|
-
|
|
344
|
+
currentResult[0] = annotationPrefix + '.' + annotation[0];
|
|
345
|
+
currentResult[1] = annotation[1];
|
|
215
346
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
347
|
+
flattenedResult.push(currentResult);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
return flattenedResult;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function _removeTypeInAnnotation(annotationObject) {
|
|
354
|
+
Object.keys(annotationObject).forEach(key => {
|
|
355
|
+
if (key === "$Type") delete annotationObject[key];
|
|
356
|
+
else if (annotationObject[key] instanceof Object) {
|
|
357
|
+
_removeTypeInAnnotation(annotationObject[key]);
|
|
221
358
|
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function _parseCapabilitiesTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
363
|
+
let targetString;
|
|
364
|
+
/**
|
|
365
|
+
* <Annotation Term="Capabilities.KeyAsSegmentSupported" />
|
|
366
|
+
* For such scenario, mark the `anntationTermObject` as `true`
|
|
367
|
+
*/
|
|
368
|
+
if (annotationTermObject === null) annotationTermObject = true;
|
|
369
|
+
|
|
370
|
+
_removeTypeInAnnotation(annotationTermObject);
|
|
371
|
+
/**
|
|
372
|
+
* For entries like:
|
|
373
|
+
* <Annotation Term="Capabilities.FilterFunctions"> <Collection> <String>eq</String> <String>ne</String> </Collection> </Annotation>
|
|
374
|
+
* <Annotation Term="Capabilities.DeleteRestrictions.Deletable" Bool="true"/>
|
|
375
|
+
*
|
|
376
|
+
* simply preserve the value as it is
|
|
377
|
+
*/
|
|
378
|
+
if (annotationTermObject instanceof Array || !(annotationTermObject instanceof Object)) {
|
|
379
|
+
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
Object.keys(annotationTermObject).forEach(annoKey => {
|
|
383
|
+
let currentAnnoValue = annotationTermObject[annoKey];
|
|
384
|
+
// if the currentAnnoValue is an Object, we need to flatten it
|
|
385
|
+
if (currentAnnoValue instanceof Object && !(currentAnnoValue instanceof Array)) {
|
|
386
|
+
targetString = annotationTerm + '.' + annoKey;
|
|
387
|
+
let flattenedResultList = _flattenAnnotationObject(targetString, currentAnnoValue);
|
|
388
|
+
|
|
389
|
+
for (let idx = 0; idx < flattenedResultList.length; idx++) {
|
|
390
|
+
_captureAnnotationInCSN(flattenedResultList[idx][0], flattenedResultList[idx][1], csnDefsTargetObject);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// if the currentAnnoValue is a list or direct value, we preserve as it is
|
|
394
|
+
else {
|
|
395
|
+
targetString = annotationTerm + '.' + annoKey;
|
|
396
|
+
_captureAnnotationInCSN(targetString, currentAnnoValue, csnDefsTargetObject);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function _parseValidationTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
403
|
+
const resolveCastFor = ["Minimum", "Maximum", "MaxItems"];
|
|
404
|
+
const term = annotationTerm.split('.')[1];
|
|
405
|
+
|
|
406
|
+
if (resolveCastFor.includes(term) && annotationTermObject.$Cast) {
|
|
407
|
+
annotationTermObject = Number(annotationTermObject.$Cast);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function _parseAnnotationTerms(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
414
|
+
const term = annotationTerm.split('.');
|
|
415
|
+
const vocabAlias = term[0];
|
|
416
|
+
|
|
417
|
+
switch (vocabAlias) {
|
|
418
|
+
case "@Core":
|
|
419
|
+
_resolveCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
420
|
+
break;
|
|
421
|
+
case "@Capabilities":
|
|
422
|
+
_parseCapabilitiesTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
423
|
+
break;
|
|
424
|
+
case "@Validation":
|
|
425
|
+
_parseValidationTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
426
|
+
break;
|
|
427
|
+
default:
|
|
428
|
+
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function _resolveAnotationTerm(schemaDataAnnotation, parserContext) {
|
|
434
|
+
// Case 1: We have the Namespace and Alias for the vocabulary
|
|
435
|
+
for (const [alias, namespace] of Object.entries(parserContext.vocabAliasToNamespace)) {
|
|
436
|
+
if (schemaDataAnnotation.includes(alias)) {
|
|
437
|
+
schemaDataAnnotation = schemaDataAnnotation.split(alias).join(known_vocabularies[namespace]);
|
|
227
438
|
}
|
|
228
|
-
|
|
229
|
-
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Case 2: We only have the Namespace for the vocabulary
|
|
442
|
+
* Case 3: We don't have the edmx:Reference at all
|
|
443
|
+
*/
|
|
444
|
+
for (const [vocabulary, alias] of Object.entries(known_vocabularies)) {
|
|
445
|
+
if (schemaDataAnnotation.includes(vocabulary)) {
|
|
446
|
+
schemaDataAnnotation = schemaDataAnnotation.split(vocabulary).join(alias);
|
|
230
447
|
}
|
|
231
|
-
} else {
|
|
232
|
-
typeProperties.type = 'cds.LargeString'; // if type is missing, then it is String type
|
|
233
448
|
}
|
|
234
449
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
450
|
+
return JSON.parse(schemaDataAnnotation);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Capture all the annotation cases withtin an Element
|
|
455
|
+
*
|
|
456
|
+
* UnKnown term: <Annotation Term="Org.OData.ASDF.V1.qwe"/>
|
|
457
|
+
* Known term: <Annotation Term="Org.OData.Measures.V1.Unit" String="Centimeters"/>
|
|
458
|
+
*
|
|
459
|
+
* if "--include-namespaces *" then we also capture for ABS and QWE
|
|
460
|
+
* <TypeDefinition Name="TD_2" UnderlyingType="Edm.Int32" ABS:asdf="testing" QWE:qwe="testing"/>
|
|
461
|
+
*/
|
|
462
|
+
function _captureAnnotationWithinElement(sourceObject, targetObject, parserContext) {
|
|
463
|
+
let element;
|
|
464
|
+
|
|
465
|
+
Object.keys(sourceObject).forEach(key => {
|
|
466
|
+
if (key.startsWith('@')) {
|
|
467
|
+
element = _resolveAnotationTerm(JSON.stringify(key), parserContext);
|
|
468
|
+
_parseAnnotationTerms(element, sourceObject[key], targetObject);
|
|
238
469
|
}
|
|
239
|
-
|
|
240
|
-
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function _generatePrimitiveDataTypeCsn(propertyObject, isPrimaryKey, parserContext) {
|
|
474
|
+
const dataTypeObj = {};
|
|
475
|
+
const propertyDataType = propertyObject.$Type;
|
|
476
|
+
const maxLength = Number(propertyObject.$MaxLength);
|
|
477
|
+
const precision = Number(propertyObject.$Precision);
|
|
478
|
+
const scale = propertyObject.$Scale;
|
|
479
|
+
const underlyingType = propertyObject.$UnderlyingType;
|
|
480
|
+
const isCollection = propertyObject.$Collection;
|
|
481
|
+
const nullable = propertyObject.$Nullable;
|
|
482
|
+
const defaultvalue = propertyObject.$DefaultValue;
|
|
483
|
+
|
|
484
|
+
if (isPrimaryKey) {
|
|
485
|
+
dataTypeObj.key = true;
|
|
486
|
+
if (nullable) {
|
|
487
|
+
console.log(warn("Key element cannot be Nullable."));
|
|
241
488
|
}
|
|
242
489
|
}
|
|
243
|
-
|
|
490
|
+
_captureAnnotationWithinElement(propertyObject, dataTypeObj, parserContext);
|
|
244
491
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
492
|
+
dataTypeObj.type = edmToCdsTypeMapping[propertyDataType];
|
|
493
|
+
|
|
494
|
+
if (maxLength) {
|
|
495
|
+
_updateDataTypeObjectForMaxLength(dataTypeObj, propertyDataType, maxLength);
|
|
496
|
+
}
|
|
497
|
+
if (precision || scale) {
|
|
498
|
+
_updateDataTypeObjectForPrecision(dataTypeObj, propertyDataType, precision, scale);
|
|
499
|
+
}
|
|
500
|
+
if (edmToCdsTypeSpecialHandling[propertyDataType]) {
|
|
501
|
+
_updateDataTypeObjectForSpecialHandling(dataTypeObj, propertyDataType, maxLength, precision);
|
|
502
|
+
}
|
|
503
|
+
if (!edmToCdsTypeMapping[propertyDataType] && !edmToCdsTypeSpecialHandling[propertyDataType]) {
|
|
504
|
+
dataTypeObj['@odata.Type'] = propertyDataType;
|
|
505
|
+
dataTypeObj.type = 'cds.String';
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* When called from TypeDefinition or EnumType we,
|
|
509
|
+
* don't want to process Nullable, Collection, DefaultValue.
|
|
510
|
+
*/
|
|
511
|
+
if (underlyingType) {
|
|
512
|
+
return dataTypeObj;
|
|
248
513
|
}
|
|
249
514
|
|
|
250
|
-
|
|
251
|
-
|
|
515
|
+
if (!nullable) {
|
|
516
|
+
dataTypeObj['notNull'] = true;
|
|
517
|
+
}
|
|
518
|
+
if (isCollection) {
|
|
519
|
+
_updateElementObjectForCollection(dataTypeObj, isPrimaryKey, propertyDataType);
|
|
520
|
+
}
|
|
521
|
+
if (defaultvalue) {
|
|
522
|
+
_updateElementObjectForDefaultValue(dataTypeObj, defaultvalue);
|
|
523
|
+
}
|
|
252
524
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
let type = { type: propertyType.$Type }
|
|
256
|
-
_typeWithCollections(property, type, propertyType.$Collection);
|
|
257
|
-
if (!propertyType.$Nullable) _addNotNull(property, propertyType.$Collection);
|
|
525
|
+
return dataTypeObj;
|
|
526
|
+
}
|
|
258
527
|
|
|
259
|
-
|
|
260
|
-
|
|
528
|
+
function _getEntityTypeMappedName(entityType, parserContext) {
|
|
529
|
+
const mappedResult = parserContext.entityToEntitySetMap.get(entityType);
|
|
530
|
+
// if one entity type has mapping to multiple entity sets, use the first entity set
|
|
531
|
+
if (mappedResult.length > 1) {
|
|
532
|
+
return parserContext.primarySchema + '.' + mappedResult[0];
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
return parserContext.primarySchema + '.' + mappedResult;
|
|
261
536
|
}
|
|
262
|
-
|
|
263
|
-
return property;
|
|
264
537
|
}
|
|
265
538
|
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
539
|
+
function _generateStructuredDataTypeCsn(dataType, propertyObject, isPrimaryKey, dataTypeKind, elementKind, mockServerUc, parserContext) {
|
|
540
|
+
const dataTypeObj = {};
|
|
541
|
+
const isCollection = propertyObject.$Collection;
|
|
542
|
+
const nullable = propertyObject.$Nullable;
|
|
543
|
+
const defaultvalue = propertyObject.$DefaultValue;
|
|
544
|
+
let propertyDataType = propertyObject.$Type;
|
|
545
|
+
// const underlyingType = propertyObject.$UnderlyingType; // might have to use later
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* for a typeDefintion to be a Primary key, it has to be
|
|
549
|
+
* based out of one of the entries in the edmPrimitiveTypes list
|
|
550
|
+
* We might have to decide if we want to throw an error if we get
|
|
551
|
+
* an entry not following this rule.
|
|
552
|
+
*/
|
|
553
|
+
if (isPrimaryKey) {
|
|
554
|
+
dataTypeObj.key = true;
|
|
555
|
+
if (nullable) {
|
|
556
|
+
console.log(warn("Key element cannot be Nullable."));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// if dataTypeKind is EntityType, also check for mapped entity name
|
|
561
|
+
if (mockServerUc && dataTypeKind === 'EntityType') {
|
|
562
|
+
// propertyDataType is resolved fully qualified name
|
|
563
|
+
if (parserContext.entityToEntitySetMap?.get(propertyDataType)) {
|
|
564
|
+
propertyDataType = _getEntityTypeMappedName(propertyDataType, parserContext);
|
|
565
|
+
}
|
|
566
|
+
// dataType can be aliased name, so check that too.
|
|
567
|
+
else if (parserContext.entityToEntitySetMap?.get(dataType)) {
|
|
568
|
+
propertyDataType = _getEntityTypeMappedName(dataType, parserContext);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
dataTypeObj.type = propertyDataType;
|
|
269
572
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
573
|
+
if (!nullable) {
|
|
574
|
+
dataTypeObj['notNull'] = true;
|
|
575
|
+
}
|
|
576
|
+
if (isCollection) {
|
|
577
|
+
_updateElementObjectForCollection(dataTypeObj, isPrimaryKey, propertyDataType);
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* only Primitive type and EnumType can have DefaultValue facet as per documentation
|
|
581
|
+
* but compiler accepts it for TypeDefintion and ComplexType as well,
|
|
582
|
+
* do we also need to handle it for TypeDefintion and ComplexType?
|
|
583
|
+
*
|
|
584
|
+
* For parameters, default value is added via OptionalParamter Annotation
|
|
585
|
+
*/
|
|
586
|
+
if (defaultvalue && dataTypeKind === 'EnumType' && elementKind === 'property') {
|
|
587
|
+
_updateElementObjectForDefaultValue(dataTypeObj, defaultvalue);
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
// throw error, because only primitive or enumeration type can have DefaultValue.
|
|
591
|
+
}
|
|
592
|
+
_captureAnnotationWithinElement(propertyObject, dataTypeObj, parserContext);
|
|
274
593
|
|
|
275
|
-
return
|
|
594
|
+
return dataTypeObj;
|
|
276
595
|
}
|
|
277
596
|
|
|
278
597
|
function _parseAssociationProperty(foreignReference, associationNode, mockServerUc, parserContext) {
|
|
279
|
-
|
|
598
|
+
const associationProperty = {};
|
|
280
599
|
let foreignKeys;
|
|
281
600
|
// var foreignReference = associationNode.$Type;
|
|
282
601
|
let refConstraint = associationNode.$ReferentialConstraint;
|
|
283
|
-
let i;
|
|
284
602
|
let data;
|
|
285
603
|
let ref;
|
|
604
|
+
|
|
286
605
|
associationProperty.type = (associationNode.$OnDelete === 'Cascade') ? 'cds.Composition' : 'cds.Association';
|
|
287
606
|
// annotation for ON condition
|
|
288
607
|
associationProperty["@cds.ambiguous"] = 'missing on condition?';
|
|
289
608
|
// associationProperty.target = associationNode.$Type;
|
|
290
609
|
|
|
291
|
-
//Code for NavigationProperty for notNull
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
610
|
+
// Code for NavigationProperty for notNull
|
|
611
|
+
// if (!associationProperty.$Nullable) {
|
|
612
|
+
// associationProperty["notNull"] = true;
|
|
613
|
+
// }
|
|
614
|
+
|
|
615
|
+
let resolvedEntityName;
|
|
616
|
+
let entityTypeMapping = (
|
|
617
|
+
parserContext.entityToEntitySetMap?.get(associationNode.$Type) ||
|
|
618
|
+
parserContext.entityToSingletonMap?.get(associationNode.$Type)
|
|
619
|
+
);
|
|
620
|
+
// if entityTypeMapping is `undefined`, means the associationNode.$Type could be aliased
|
|
621
|
+
if (!entityTypeMapping) {
|
|
622
|
+
resolvedEntityName = _resolveAliasedElementName(associationNode.$Type, parserContext);
|
|
623
|
+
entityTypeMapping = (
|
|
624
|
+
parserContext.entityToEntitySetMap?.get(resolvedEntityName) ||
|
|
625
|
+
parserContext.entityToSingletonMap?.get(resolvedEntityName)
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (mockServerUc && entityTypeMapping) {
|
|
630
|
+
if (entityTypeMapping.length > 1) {
|
|
631
|
+
for (let j = 0; j < entityTypeMapping.length; j++) {
|
|
632
|
+
if (entityTypeMapping[j] === foreignReference) {
|
|
633
|
+
associationProperty.target = parserContext.primarySchema + '.' + entityTypeMapping[j];
|
|
301
634
|
}
|
|
302
635
|
}
|
|
303
636
|
} else {
|
|
304
|
-
associationProperty.target = parserContext.
|
|
637
|
+
associationProperty.target = parserContext.primarySchema + '.' + entityTypeMapping;
|
|
305
638
|
}
|
|
306
639
|
} else {
|
|
307
|
-
associationProperty.target =
|
|
640
|
+
associationProperty.target = resolvedEntityName;
|
|
308
641
|
}
|
|
309
642
|
// cardinality
|
|
310
643
|
associationProperty.cardinality = {};
|
|
@@ -317,7 +650,8 @@ function _parseAssociationProperty(foreignReference, associationNode, mockServer
|
|
|
317
650
|
if (refConstraint) {
|
|
318
651
|
associationProperty.on = [];
|
|
319
652
|
foreignKeys = Object.keys(refConstraint);
|
|
320
|
-
|
|
653
|
+
|
|
654
|
+
for (let i = 0; i < foreignKeys.length; i++) {
|
|
321
655
|
data = {};
|
|
322
656
|
ref = [];
|
|
323
657
|
ref.push(foreignReference);
|
|
@@ -340,18 +674,95 @@ function _parseAssociationProperty(foreignReference, associationNode, mockServer
|
|
|
340
674
|
if (common.checkForEmptyKeys(associationProperty, "V4")) {
|
|
341
675
|
associationProperty.keys = [];
|
|
342
676
|
}
|
|
343
|
-
|
|
677
|
+
_captureAnnotationWithinElement(associationNode, associationProperty, parserContext);
|
|
678
|
+
|
|
344
679
|
return associationProperty;
|
|
345
680
|
}
|
|
346
681
|
|
|
347
|
-
function
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
682
|
+
function _checkIsStructureTypeAndReturnDataKind(dataType, contentObject, elementKind, parserContext) {
|
|
683
|
+
let fqName = dataType;
|
|
684
|
+
let [schemaName, elementName] = _extractSchemaNameAndElementName(dataType);
|
|
685
|
+
|
|
686
|
+
if (parserContext.schemaAliasToNamespace[schemaName]) {
|
|
687
|
+
fqName = parserContext.schemaAliasToNamespace[schemaName] + "." + elementName;
|
|
688
|
+
contentObject.$Type = fqName;
|
|
689
|
+
}
|
|
690
|
+
if (parserContext.complexTypes.includes(fqName)) {
|
|
691
|
+
return 'ComplexType';
|
|
692
|
+
}
|
|
693
|
+
else if (parserContext.typeDefinitions.includes(fqName)) {
|
|
694
|
+
return 'TypeDefinition';
|
|
354
695
|
}
|
|
696
|
+
else if (parserContext.enumTypes.includes(fqName)) {
|
|
697
|
+
return 'EnumType';
|
|
698
|
+
}
|
|
699
|
+
else if (elementKind === 'property' && parserContext.entityTypes.includes(fqName)) {
|
|
700
|
+
throw new Error(messages.UNSUPPORTED_PROPERTY_TYPE + dataType);
|
|
701
|
+
}
|
|
702
|
+
else if (elementKind === 'parameter' && parserContext.entityTypes.includes(fqName)) {
|
|
703
|
+
return 'EntityType';
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
throw new Error(messages.UNRESOLVED_TYPE + dataType);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* @param {Object} csnObj
|
|
712
|
+
* @param {Object} elementObj
|
|
713
|
+
* @param {Object} parserContext
|
|
714
|
+
*/
|
|
715
|
+
function _parsePropertyContent(csnObj, elementObj, mockServerUc, parserContext) {
|
|
716
|
+
let contentObj = {};
|
|
717
|
+
const isPrimaryKey = true;
|
|
718
|
+
let dataType, isPrimitiveType, keySet = [];
|
|
719
|
+
|
|
720
|
+
Object.keys(elementObj).forEach(objKey => {
|
|
721
|
+
const contentKind = elementObj[objKey].$Kind;
|
|
722
|
+
contentObj = elementObj[objKey];
|
|
723
|
+
|
|
724
|
+
if ((objKey.indexOf('@') === -1) && (objKey.indexOf('.') === -1)) {
|
|
725
|
+
if (contentObj instanceof Object) {
|
|
726
|
+
if (contentObj.$Type) {
|
|
727
|
+
dataType = contentObj.$Type;
|
|
728
|
+
isPrimitiveType = dataType.startsWith('Edm') ? true : false;
|
|
729
|
+
}
|
|
730
|
+
if (objKey === '$Key') {
|
|
731
|
+
keySet.push(...elementObj.$Key);
|
|
732
|
+
}
|
|
733
|
+
// primitive datatypes
|
|
734
|
+
else if (isPrimitiveType) {
|
|
735
|
+
if (edmPrimitiveTypes.includes(dataType)) {
|
|
736
|
+
csnObj.elements[objKey] = _generatePrimitiveDataTypeCsn(contentObj,
|
|
737
|
+
(keySet.includes(objKey) ? isPrimaryKey : !isPrimaryKey),
|
|
738
|
+
parserContext
|
|
739
|
+
);
|
|
740
|
+
} else {
|
|
741
|
+
throw new Error(messages.INVALID_DATATYPE + `'${dataType}' in element: '${objKey}'`)
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
// structured datatypes
|
|
745
|
+
else if (!isPrimitiveType && !(contentKind === 'NavigationProperty')) {
|
|
746
|
+
const dataKind = _checkIsStructureTypeAndReturnDataKind(dataType, contentObj, 'property', parserContext);
|
|
747
|
+
|
|
748
|
+
if (['EnumType', 'TypeDefinition'].includes(dataKind)) {
|
|
749
|
+
csnObj.elements[objKey] = _generateStructuredDataTypeCsn(dataType, contentObj,
|
|
750
|
+
(keySet.includes(dataType) ? isPrimaryKey : !isPrimaryKey),
|
|
751
|
+
dataKind, 'property', mockServerUc, parserContext
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
csnObj.elements[objKey] = _generateStructuredDataTypeCsn(dataType, contentObj,
|
|
756
|
+
!isPrimaryKey, dataKind, 'property', mockServerUc, parserContext
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
else if (contentKind === 'NavigationProperty') {
|
|
761
|
+
csnObj.elements[objKey] = _parseAssociationProperty(objKey, elementObj[objKey], mockServerUc, parserContext);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
});
|
|
355
766
|
}
|
|
356
767
|
|
|
357
768
|
function _addBlobElement() {
|
|
@@ -363,1085 +774,813 @@ function _addBlobElement() {
|
|
|
363
774
|
}
|
|
364
775
|
}
|
|
365
776
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
let
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
entityCsn[key]['@cds.external'] = true;
|
|
386
|
-
if (!ignorePersistenceSkip) {
|
|
387
|
-
entityCsn[key]['@cds.persistence.skip'] = true;
|
|
388
|
-
}
|
|
389
|
-
if (element[key].$Singleton) {
|
|
390
|
-
entityCsn[key]['@odata.singleton'] = true;
|
|
391
|
-
}
|
|
392
|
-
if (element[key].$SingletonNullable) {
|
|
393
|
-
entityCsn[key]['@odata.singleton.nullable'] = true;
|
|
394
|
-
}
|
|
395
|
-
_includeExternalNamespaceAnnotation(element[key], entityCsn[key]);
|
|
396
|
-
// inheritance
|
|
397
|
-
if (baseType) {
|
|
398
|
-
entityCsn[key].includes = [];
|
|
399
|
-
if (entityTypeNames.includes(baseType)) {
|
|
400
|
-
entityCsn[key].includes.push(baseType);
|
|
401
|
-
}
|
|
402
|
-
else if (parserContext.entitySetToEntityTypeMap && parserContext.entitySetToEntityTypeMap.get(baseType)) {
|
|
403
|
-
baseType = _getBaseTypeEntityName(baseType, parserContext);
|
|
404
|
-
entityCsn[key].includes.push(baseType);
|
|
405
|
-
} else {
|
|
406
|
-
delete entityCsn[key].includes;
|
|
407
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${baseType}' in element: '${element}'.`);
|
|
777
|
+
/**
|
|
778
|
+
* to handle the Abstract, Open & Base Type scenarios
|
|
779
|
+
* @param {String} fqName, element name
|
|
780
|
+
* @param {Object} csnObj,
|
|
781
|
+
* @param {Object} elementObj,
|
|
782
|
+
* @param {Object} parserContext
|
|
783
|
+
*/
|
|
784
|
+
function _captureOpenTypes(fqName, csnObj, elementObj, isEntity, mockServerUc, parserContext) {
|
|
785
|
+
let baseTypeName = elementObj.$BaseType;
|
|
786
|
+
let isAbstract = elementObj.$Abstract;
|
|
787
|
+
let isOpenType = elementObj.$OpenType;
|
|
788
|
+
|
|
789
|
+
if (baseTypeName) {
|
|
790
|
+
let resolvedBaseTypeName = _resolveAliasedElementName(baseTypeName, parserContext);
|
|
791
|
+
|
|
792
|
+
if (isEntity) {
|
|
793
|
+
if (parserContext.entityTypes.includes(resolvedBaseTypeName)) {
|
|
794
|
+
if (!parserContext.entityTypeOpenEntries.includes(resolvedBaseTypeName)) {
|
|
795
|
+
parserContext.entityTypeOpenEntries.push(resolvedBaseTypeName);
|
|
408
796
|
}
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
keySet.push(curKey[j]);
|
|
425
|
-
}
|
|
426
|
-
} else if (!element[key][property].$Type || edmxncdsdatatype[element[key][property].$Type] ||
|
|
427
|
-
edmxncdsdatatype[element[key][property].$Type + "_a"]
|
|
428
|
-
) {
|
|
429
|
-
propertyType = element[key][property];
|
|
430
|
-
if (keySet.includes(property)) {
|
|
431
|
-
entityCsn[key].elements[property] = generatePropertyCsn(propertyType, true);
|
|
432
|
-
} else {
|
|
433
|
-
entityCsn[key].elements[property] = generatePropertyCsn(propertyType, false);
|
|
434
|
-
}
|
|
435
|
-
} else if (!element[key][property].$Type || parserContext.enumTypes[element[key][property].$Type]) {
|
|
436
|
-
enumType = element[key][property];
|
|
437
|
-
if (keySet.includes(property)) {
|
|
438
|
-
entityCsn[key].elements[property] = generatePropertyCsn(enumType, true);
|
|
439
|
-
} else {
|
|
440
|
-
entityCsn[key].elements[property] = generatePropertyCsn(enumType, false);
|
|
441
|
-
}
|
|
442
|
-
} else if (parserContext.complexTypes[element[key][property].$Type]) {
|
|
443
|
-
propertyType = element[key][property];
|
|
444
|
-
entityCsn[key].elements[property] = _generateComplexTypePropertyCsn(propertyType);
|
|
445
|
-
} else if (element[key][property].$Kind === 'NavigationProperty') {
|
|
446
|
-
entityCsn[key].elements[property] = _parseAssociationProperty(property, element[key][property], mockServerUc, parserContext);
|
|
447
|
-
} else if (parserContext.typeDefinitions[element[key][property].$Type]) {
|
|
448
|
-
// for TypeDefinitions
|
|
449
|
-
propertyType = element[key][property];
|
|
450
|
-
entityCsn[key].elements[property] = _generateTypeDefinitionPropertyCsn(propertyType);
|
|
451
|
-
} else {
|
|
452
|
-
// let message = '"' + element[key][property].$Type + '" is not supported (in element:"' + property + '")';
|
|
453
|
-
// console.log(warn(message));
|
|
454
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${element[key][property].$Type}' in element: '${property}'.`);
|
|
797
|
+
if (mockServerUc) {
|
|
798
|
+
let entityMapping = (
|
|
799
|
+
parserContext.entityToEntitySetMap?.get(baseTypeName) ||
|
|
800
|
+
parserContext.entityToSingletonMap?.get(baseTypeName)
|
|
801
|
+
);
|
|
802
|
+
if (entityMapping) {
|
|
803
|
+
resolvedBaseTypeName = _getEntityTypeMappedName(baseTypeName, parserContext);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
entityMapping = (
|
|
807
|
+
parserContext.entityToEntitySetMap?.get(resolvedBaseTypeName) ||
|
|
808
|
+
parserContext.entityToSingletonMap?.get(resolvedBaseTypeName)
|
|
809
|
+
);
|
|
810
|
+
if (entityMapping) {
|
|
811
|
+
resolvedBaseTypeName = parserContext.primarySchema + '.' + entityMapping[0];
|
|
455
812
|
}
|
|
456
813
|
}
|
|
457
814
|
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
|
|
815
|
+
} else {
|
|
816
|
+
throw new Error(messages.UNRESOLVED_TYPE + `'${baseTypeName}'`);
|
|
817
|
+
}
|
|
818
|
+
} else {
|
|
819
|
+
if (parserContext.complexTypes.includes(resolvedBaseTypeName) &&
|
|
820
|
+
!parserContext.complexTypeOpenEntries.includes(resolvedBaseTypeName)
|
|
821
|
+
) {
|
|
822
|
+
parserContext.complexTypeOpenEntries.push(resolvedBaseTypeName);
|
|
823
|
+
}
|
|
824
|
+
else if (!parserContext.complexTypes.includes(resolvedBaseTypeName)) {
|
|
825
|
+
throw new Error(messages.UNRESOLVED_TYPE + `'${baseTypeName}'`);
|
|
461
826
|
}
|
|
462
827
|
}
|
|
463
|
-
|
|
828
|
+
csnObj.includes = [resolvedBaseTypeName];
|
|
829
|
+
}
|
|
830
|
+
if (isAbstract || isOpenType) {
|
|
831
|
+
if (isEntity && !parserContext.entityTypeOpenEntries.includes(fqName)) {
|
|
832
|
+
parserContext.entityTypeOpenEntries.push(fqName);
|
|
833
|
+
}
|
|
834
|
+
else if (!parserContext.complexTypeOpenEntries.includes(fqName)) {
|
|
835
|
+
parserContext.complexTypeOpenEntries.push(fqName);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
464
839
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
840
|
+
function _generateCSNForEntityType(ignorePersistenceSkip, mockServerUc, entityList, parserContext) {
|
|
841
|
+
const csnEntity = {};
|
|
842
|
+
let isEntity = true;
|
|
843
|
+
entityList.forEach(entityElement => {
|
|
844
|
+
let entityName = Object.keys(entityElement)[0];
|
|
469
845
|
|
|
470
|
-
|
|
471
|
-
|
|
846
|
+
csnEntity[entityName] = {};
|
|
847
|
+
csnEntity[entityName].kind = 'entity';
|
|
848
|
+
csnEntity[entityName]['@cds.external'] = true;
|
|
472
849
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
let temp;
|
|
476
|
-
Object.keys(schemaData).forEach(entityType => {
|
|
477
|
-
entity = {};
|
|
478
|
-
if (schemaData[entityType] instanceof Object) {
|
|
479
|
-
if (schemaData[entityType].$Kind && schemaData[entityType].$Kind === 'EntityType') {
|
|
480
|
-
if (mockServerUc) {
|
|
481
|
-
let entityTypeName = parserContext.namespace + '.' + entityType;
|
|
482
|
-
if (parserContext.entitySetToEntityTypeMap && parserContext.entitySetToEntityTypeMap.get(entityTypeName)) {
|
|
483
|
-
if (parserContext.entitySetToEntityTypeMap.get(entityTypeName).length > 1) {
|
|
484
|
-
let entity1 = {};
|
|
485
|
-
for (let i = 0; i < parserContext.entitySetToEntityTypeMap.get(entityTypeName).length; i++) {
|
|
486
|
-
entity1 = {};
|
|
487
|
-
let entityDetails = {};
|
|
488
|
-
let temp1 = parserContext.namespace + '.' + parserContext.entitySetToEntityTypeMap.get(entityTypeName)[i];
|
|
489
|
-
entity1[temp1] = Object.assign({}, schemaData[entityType]);
|
|
490
|
-
entityDetails = schemaData[parserContext.jsonEntityContainerName][parserContext.entitySetToEntityTypeMap.get(entityTypeName)[i]];
|
|
491
|
-
if (entityDetails.$Singleton)
|
|
492
|
-
entity1[temp1].$Singleton = entityDetails.$Singleton;
|
|
493
|
-
if (entityDetails.$SingletonNullable)
|
|
494
|
-
entity1[temp1].$SingletonNullable = entityDetails.$SingletonNullable;
|
|
495
|
-
parserContext.entityTypes.push(entity1);
|
|
496
|
-
}
|
|
497
|
-
} else {
|
|
498
|
-
temp = parserContext.namespace + '.' + parserContext.entitySetToEntityTypeMap.get(entityTypeName);
|
|
499
|
-
entity[temp] = schemaData[entityType];
|
|
500
|
-
let entityDetails = schemaData[parserContext.jsonEntityContainerName][parserContext.entitySetToEntityTypeMap.get(entityTypeName)];
|
|
501
|
-
if (entityDetails.$Singleton)
|
|
502
|
-
entity[temp].$Singleton = entityDetails.$Singleton;
|
|
503
|
-
if (entityDetails.$SingletonNullable)
|
|
504
|
-
entity[temp].$SingletonNullable = entityDetails.$SingletonNullable;
|
|
505
|
-
parserContext.entityTypes.push(entity);
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
508
|
-
temp = parserContext.namespace + '.' + entityType;
|
|
509
|
-
entity[temp] = schemaData[entityType];
|
|
510
|
-
parserContext.entityTypes.push(entity);
|
|
511
|
-
}
|
|
512
|
-
} else {
|
|
513
|
-
temp = parserContext.namespace + '.' + entityType;
|
|
514
|
-
entity[temp] = schemaData[entityType];
|
|
515
|
-
parserContext.entityTypes.push(entity);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
850
|
+
if (!ignorePersistenceSkip) {
|
|
851
|
+
csnEntity[entityName]['@cds.persistence.skip'] = true;
|
|
518
852
|
}
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
853
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
return _generateCSNForEntityType(ignorePersistenceSkip, mockServerUc, parserContext);
|
|
530
|
-
}
|
|
854
|
+
_captureOpenTypes(entityName, csnEntity[entityName], entityElement[entityName], isEntity, mockServerUc, parserContext);
|
|
855
|
+
_captureAnnotationWithinElement(entityElement[entityName], csnEntity[entityName], parserContext);
|
|
856
|
+
|
|
857
|
+
csnEntity[entityName].elements = {};
|
|
858
|
+
|
|
859
|
+
_parsePropertyContent(csnEntity[entityName], entityElement[entityName], mockServerUc, parserContext);
|
|
531
860
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
// Captured the annotations at entity set and entity type name level
|
|
536
|
-
_includeExternalNamespaceAnnotation(entry[1], entityCSN[parserContext.namespace + '.' + entry[0]]);
|
|
537
|
-
if (entry[0].startsWith('@')) {
|
|
538
|
-
// Captured the annotations at entity container name level
|
|
539
|
-
entityCSN[parserContext.namespace][entry[0]] = entry[1];
|
|
861
|
+
// needs to be checked if this is necessary
|
|
862
|
+
if (entityElement[entityName].$HasStream) {
|
|
863
|
+
csnEntity[entityName].elements = { ...csnEntity[entityName].elements, ..._addBlobElement() };
|
|
540
864
|
}
|
|
541
865
|
});
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
function _generateEnumType(schemaData, parserContext) {
|
|
546
|
-
let enumTypeDict = {};
|
|
547
|
-
let temp;
|
|
548
|
-
Object.keys(schemaData).forEach(curEnumType => {
|
|
549
|
-
if (schemaData[curEnumType] instanceof Object) {
|
|
550
|
-
if (schemaData[curEnumType].$Kind && schemaData[curEnumType].$Kind === 'EnumType') {
|
|
551
|
-
temp = parserContext.namespace + '.' + curEnumType;
|
|
552
|
-
enumTypeDict[temp] = schemaData[curEnumType];
|
|
553
|
-
}
|
|
554
|
-
}
|
|
866
|
+
// add @open annotations
|
|
867
|
+
parserContext.entityTypeOpenEntries.forEach(entityName => {
|
|
868
|
+
if (csnEntity[entityName]) csnEntity[entityName]['@open'] = true;
|
|
555
869
|
});
|
|
556
|
-
|
|
870
|
+
|
|
871
|
+
return csnEntity;
|
|
557
872
|
}
|
|
558
873
|
|
|
559
|
-
function
|
|
560
|
-
let
|
|
561
|
-
for (let
|
|
562
|
-
|
|
563
|
-
|
|
874
|
+
function _parseCSNForEntityType(schemaData, ignorePersistenceSkip, mockServerUc, parserContext) {
|
|
875
|
+
let entitiesList = [];
|
|
876
|
+
for (let fqName of parserContext.entityTypes) {
|
|
877
|
+
let entity = {};
|
|
878
|
+
let [schemaName, elementName] = _extractSchemaNameAndElementName(fqName);
|
|
879
|
+
let entityObj = schemaData[elementName];
|
|
880
|
+
|
|
881
|
+
if (entityObj?.$Kind === 'EntityType' && schemaName === schemaData.$SchemaNamespace) {
|
|
882
|
+
entity[fqName] = entityObj;
|
|
883
|
+
entitiesList.push(entity);
|
|
884
|
+
}
|
|
564
885
|
}
|
|
565
|
-
|
|
886
|
+
|
|
887
|
+
return _generateCSNForEntityType(ignorePersistenceSkip, mockServerUc, entitiesList, parserContext);
|
|
566
888
|
}
|
|
567
889
|
|
|
568
|
-
function
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
eTypes = _collectEnumType(schemaData, parserContext);
|
|
573
|
-
Object.keys(eTypes).forEach(element => {
|
|
574
|
-
enumCsn[element] = {};
|
|
575
|
-
enumCsn[element].kind = 'type';
|
|
576
|
-
enumCsn[element]['@cds.external'] = true;
|
|
577
|
-
enumCsn[element].enum = {};
|
|
578
|
-
/**
|
|
579
|
-
* If UnderlyingType not specified, default is Edm.Int32 --> cds.Integer
|
|
580
|
-
*/
|
|
581
|
-
let underlyingType = eTypes[element].$UnderlyingType;
|
|
582
|
-
enumCsn[element].type = underlyingType ? edmxncdsdatatype[underlyingType] : 'cds.Integer';
|
|
890
|
+
function _parseParameterAndReturnTypeContent(elementObject, mockServerUc, parserContext) {
|
|
891
|
+
let dataType = elementObject.$Type;
|
|
892
|
+
const isPrimitiveType = dataType.startsWith('Edm') ? true : false;
|
|
893
|
+
const isPrimaryKey = true;
|
|
583
894
|
|
|
584
|
-
|
|
585
|
-
|
|
895
|
+
// primitive type
|
|
896
|
+
if (isPrimitiveType) {
|
|
897
|
+
if (edmPrimitiveTypes.includes(dataType)) {
|
|
898
|
+
return _generatePrimitiveDataTypeCsn(elementObject, !isPrimaryKey, parserContext);
|
|
586
899
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
900
|
+
else {
|
|
901
|
+
throw new Error(messages.INVALID_DATATYPE + `'${dataType}' in element: '${elementObject.$Name}'`)
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
// structured type
|
|
905
|
+
else {
|
|
906
|
+
const dataKind = _checkIsStructureTypeAndReturnDataKind(dataType, elementObject, 'parameter', parserContext);
|
|
907
|
+
return _generateStructuredDataTypeCsn(dataType, elementObject, !isPrimaryKey, dataKind, 'parameter', mockServerUc, parserContext);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function _generateCSNForBoundedActionFunction(actionFunctionObject, kind, mockServerUc, parserContext) {
|
|
912
|
+
const csnObj = {};
|
|
913
|
+
const isBounded = actionFunctionObject.$IsBound;
|
|
914
|
+
const parameters = actionFunctionObject.$Parameter;
|
|
915
|
+
const returnType = actionFunctionObject.$ReturnType;
|
|
916
|
+
|
|
917
|
+
csnObj.kind = kind;
|
|
918
|
+
_captureAnnotationWithinElement(actionFunctionObject, csnObj, parserContext);
|
|
919
|
+
|
|
920
|
+
if (parameters) {
|
|
921
|
+
csnObj.params = {};
|
|
922
|
+
|
|
923
|
+
for (let index = 0; index < parameters.length; index++) {
|
|
924
|
+
let isCollection = parameters[index].$Collection;
|
|
925
|
+
csnObj.params[parameters[index].$Name] = _parseParameterAndReturnTypeContent(parameters[index], mockServerUc, parserContext);
|
|
926
|
+
/**
|
|
927
|
+
* binding parameter handling - only first parameter will contain the binding
|
|
928
|
+
* parameter information, so only make change to the first parameter information.
|
|
929
|
+
* NOTE: Edm.Stream can act as the binding parameter for action/function
|
|
930
|
+
* We don't have any example for it, so not handled as of now
|
|
931
|
+
*/
|
|
932
|
+
if (isBounded && index === 0) {
|
|
933
|
+
if (isCollection) {
|
|
934
|
+
csnObj.params[parameters[index].$Name].items.type = '$self';
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
csnObj.params[parameters[index].$Name].type = '$self';
|
|
938
|
+
}
|
|
591
939
|
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (returnType) {
|
|
943
|
+
csnObj.returns = _parseParameterAndReturnTypeContent(returnType, mockServerUc, parserContext);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return csnObj;
|
|
595
947
|
}
|
|
596
948
|
|
|
597
|
-
function
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
Object.keys(
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
949
|
+
function _parseBoundedActionFunction(schemaData, csnObj, kind, mockServerUc, parserContext) {
|
|
950
|
+
const boundedObject = (kind === 'action') ? parserContext.entityToBoundedActions : parserContext.entityToBoundedFunctions;
|
|
951
|
+
|
|
952
|
+
Object.keys(boundedObject).forEach(fqEntityName => {
|
|
953
|
+
const [schemaName, ] = _extractSchemaNameAndElementName(fqEntityName);
|
|
954
|
+
|
|
955
|
+
if (schemaName === schemaData.$SchemaNamespace) {
|
|
956
|
+
const actionFunctionObjectList = boundedObject[fqEntityName];
|
|
957
|
+
|
|
958
|
+
if (!csnObj[fqEntityName].actions) {
|
|
959
|
+
csnObj[fqEntityName].actions = {};
|
|
605
960
|
}
|
|
961
|
+
Object.keys(actionFunctionObjectList).forEach(actionFunctionName => {
|
|
962
|
+
csnObj[fqEntityName].actions[actionFunctionName] = _generateCSNForBoundedActionFunction(
|
|
963
|
+
actionFunctionObjectList[actionFunctionName], kind, mockServerUc, parserContext
|
|
964
|
+
);
|
|
965
|
+
});
|
|
606
966
|
}
|
|
607
967
|
});
|
|
608
|
-
return typeDefDict;
|
|
609
968
|
}
|
|
610
969
|
|
|
611
|
-
function
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
schemaData = schemaDataList[i];
|
|
615
|
-
parserContext.typeDefinitions = _generateTypeDefinition(schemaData, parserContext);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
970
|
+
function _parseUnBoundedActionFunction(schemaData, kind, mockServerUc, parserContext) {
|
|
971
|
+
const unBoundedObject = (kind === 'action') ? parserContext.unboundedActions : parserContext.unboundedFunctions;
|
|
972
|
+
const csnObj = {};
|
|
618
973
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
974
|
+
Object.keys(unBoundedObject).forEach(fqName => {
|
|
975
|
+
const [schemaName, ] = _extractSchemaNameAndElementName(fqName);
|
|
976
|
+
|
|
977
|
+
if (schemaName === schemaData.$SchemaNamespace) {
|
|
978
|
+
const actionFunctionObject = unBoundedObject[fqName];
|
|
979
|
+
const parameters = actionFunctionObject.$Parameter;
|
|
980
|
+
const returnType = actionFunctionObject.$ReturnType;
|
|
981
|
+
|
|
982
|
+
csnObj[fqName] = {};
|
|
983
|
+
csnObj[fqName].kind = kind;
|
|
984
|
+
csnObj[fqName]['@cds.external'] = true;
|
|
985
|
+
_captureAnnotationWithinElement(actionFunctionObject, csnObj[fqName], parserContext);
|
|
986
|
+
|
|
987
|
+
if (parameters) {
|
|
988
|
+
csnObj[fqName].params = {};
|
|
989
|
+
|
|
990
|
+
for (let paramObj of parameters) {
|
|
991
|
+
csnObj[fqName].params[paramObj.$Name] = _parseParameterAndReturnTypeContent(paramObj, mockServerUc, parserContext);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
if (returnType) {
|
|
995
|
+
csnObj[fqName].returns = _parseParameterAndReturnTypeContent(returnType, mockServerUc, parserContext);
|
|
640
996
|
}
|
|
641
997
|
}
|
|
642
998
|
});
|
|
643
|
-
|
|
999
|
+
|
|
1000
|
+
return csnObj;
|
|
644
1001
|
}
|
|
645
1002
|
|
|
646
|
-
function
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1003
|
+
function _parserCSNForComplexType(schemaData, mockServerUc, parserContext) {
|
|
1004
|
+
const complexCsn = {};
|
|
1005
|
+
const isEntity = true;
|
|
1006
|
+
|
|
1007
|
+
for (let fqName of parserContext.complexTypes) {
|
|
1008
|
+
let [schemaName, elementName] = _extractSchemaNameAndElementName(fqName);
|
|
1009
|
+
let complexObj = schemaData[elementName];
|
|
1010
|
+
|
|
1011
|
+
if (complexObj?.$Kind === 'ComplexType' && schemaName === schemaData.$SchemaNamespace) {
|
|
1012
|
+
complexCsn[fqName] = {};
|
|
1013
|
+
complexCsn[fqName].kind = 'type';
|
|
1014
|
+
complexCsn[fqName]['@cds.external'] = true;
|
|
1015
|
+
complexCsn[fqName].elements = {};
|
|
1016
|
+
// inheritance handling
|
|
1017
|
+
_captureOpenTypes(fqName, complexCsn[fqName], complexObj, !isEntity, !mockServerUc, parserContext);
|
|
1018
|
+
_captureAnnotationWithinElement(complexObj, complexCsn[fqName], parserContext);
|
|
1019
|
+
_parsePropertyContent(complexCsn[fqName], complexObj, mockServerUc, parserContext);
|
|
1020
|
+
}
|
|
651
1021
|
}
|
|
652
|
-
|
|
653
|
-
|
|
1022
|
+
// add @open annotations
|
|
1023
|
+
parserContext.complexTypeOpenEntries.forEach(key => {
|
|
1024
|
+
if (complexCsn[key]) complexCsn[key]['@open'] = true;
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
return complexCsn;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
function _parseCSNForEnumType(schemaData, parserContext) {
|
|
1031
|
+
const nonMemberList = ["$Kind", "$Type", "$UnderlyingType", "$IsFlags"];
|
|
1032
|
+
const possibleCdsTypes = ['cds.Integer', 'cds.Integer64'];
|
|
1033
|
+
const enumCsn = {};
|
|
1034
|
+
const isKey = true;
|
|
1035
|
+
|
|
1036
|
+
for (let fqName of parserContext.enumTypes) {
|
|
1037
|
+
let [schemaName, elementName] = _extractSchemaNameAndElementName(fqName);
|
|
1038
|
+
let enumObj = schemaData[elementName];
|
|
1039
|
+
|
|
1040
|
+
if (enumObj?.$Kind === 'EnumType' && schemaName === schemaData.$SchemaNamespace) {
|
|
1041
|
+
enumCsn[fqName] = {};
|
|
1042
|
+
enumCsn[fqName].kind = 'type';
|
|
1043
|
+
enumCsn[fqName]['@cds.external'] = true;
|
|
1044
|
+
enumCsn[fqName].enum = {};
|
|
1045
|
+
/**
|
|
1046
|
+
* If UnderlyingType not specified, default is Edm.Int32 --> cds.Integer
|
|
1047
|
+
*/
|
|
1048
|
+
let underlyingType = enumObj.$UnderlyingType;
|
|
1049
|
+
enumObj.$Type = underlyingType;
|
|
1050
|
+
|
|
1051
|
+
let cdsType = underlyingType ? (_generatePrimitiveDataTypeCsn(enumObj, !isKey, parserContext)) : { type : 'cds.Integer' };
|
|
1052
|
+
enumCsn[fqName].type = cdsType.type;
|
|
1053
|
+
|
|
1054
|
+
if (cdsType['@odata.Type']) {
|
|
1055
|
+
enumCsn[fqName]['@odata.Type'] = cdsType['@odata.Type'];
|
|
1056
|
+
}
|
|
1057
|
+
if (!possibleCdsTypes.includes(enumCsn[fqName].type)) {
|
|
1058
|
+
throw new Error(messages.INVALID_ENUM_TYPE + `'${underlyingType}'`);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
Object.keys(enumObj).forEach(member => {
|
|
1062
|
+
if (!nonMemberList.includes(member)) {
|
|
1063
|
+
enumCsn[fqName].enum[member] = {};
|
|
1064
|
+
enumCsn[fqName].enum[member].val = enumObj[member];
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
654
1068
|
}
|
|
655
|
-
|
|
1069
|
+
|
|
1070
|
+
return enumCsn;
|
|
656
1071
|
}
|
|
657
1072
|
|
|
658
|
-
function
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1073
|
+
function _parseCSNForTypeDefinition(schemaData, parserContext) {
|
|
1074
|
+
const typeDef = {};
|
|
1075
|
+
const isKey = true;
|
|
1076
|
+
|
|
1077
|
+
for (let fqName of parserContext.typeDefinitions) {
|
|
1078
|
+
let [schemaName, elementName] = _extractSchemaNameAndElementName(fqName);
|
|
1079
|
+
let typeDefObj = schemaData[elementName];
|
|
1080
|
+
|
|
1081
|
+
if (typeDefObj?.$Kind === 'TypeDefinition' && schemaName === schemaData.$SchemaNamespace) {
|
|
1082
|
+
typeDef[fqName] = {};
|
|
1083
|
+
typeDef[fqName].kind = 'type';
|
|
1084
|
+
typeDef[fqName]['@cds.external'] = true;
|
|
1085
|
+
|
|
1086
|
+
let underlyingType = typeDefObj.$UnderlyingType;
|
|
1087
|
+
typeDefObj.$Type = underlyingType;
|
|
1088
|
+
|
|
1089
|
+
if (edmPrimitiveTypes.includes(underlyingType)) {
|
|
1090
|
+
typeDef[fqName] = Object.assign(typeDef[fqName], _generatePrimitiveDataTypeCsn(typeDefObj, !isKey, parserContext));
|
|
1091
|
+
}
|
|
1092
|
+
else {
|
|
1093
|
+
throw new Error(messages.UNRESOLVED_TYPE + `'${underlyingType}'`);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
663
1096
|
}
|
|
1097
|
+
|
|
1098
|
+
return typeDef;
|
|
664
1099
|
}
|
|
665
1100
|
|
|
666
|
-
function
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1101
|
+
function _resolveEntityContainerAnnotationTarget(targetPath, csnDefs, hasProperty, parserContext) {
|
|
1102
|
+
/**
|
|
1103
|
+
* format: <schemaName_or_Alias><EntityContainerName>/<ContainerElement>
|
|
1104
|
+
* <schemaName_or_Alias><EntityContainerName>/<ContainerElement>/...
|
|
1105
|
+
*
|
|
1106
|
+
* needs to be handled without 'EntityContainerName'
|
|
1107
|
+
*/
|
|
1108
|
+
if (hasProperty) {
|
|
1109
|
+
const eleNameList = targetPath.split('/');
|
|
1110
|
+
// based on the length of eleName list, do the processing
|
|
1111
|
+
if (eleNameList.length === 2) {
|
|
1112
|
+
return csnDefs[parserContext.primarySchema + '.' + eleNameList[1]];
|
|
672
1113
|
}
|
|
673
1114
|
else {
|
|
674
|
-
|
|
1115
|
+
// TODO: We might have to take care of such senarios in the future.
|
|
675
1116
|
}
|
|
676
1117
|
}
|
|
1118
|
+
// direct path to service object
|
|
1119
|
+
else {
|
|
1120
|
+
return csnDefs[parserContext.primarySchema];
|
|
1121
|
+
}
|
|
677
1122
|
}
|
|
678
1123
|
|
|
679
|
-
function
|
|
680
|
-
let
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
//
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
type = datatype;
|
|
1124
|
+
function _resolveActionFunctionAnnotationTarget(targetPath, csnDefs, hasParameter, parserContext) {
|
|
1125
|
+
let elemNameList = targetPath.split('(');
|
|
1126
|
+
const fqActFunName = _resolveAliasedElementName(elemNameList[0], parserContext);
|
|
1127
|
+
const paramName = hasParameter ? targetPath.split('/')[1] : null;
|
|
1128
|
+
|
|
1129
|
+
// unbounded action or function
|
|
1130
|
+
if (csnDefs[fqActFunName]) {
|
|
1131
|
+
// with parameter
|
|
1132
|
+
if (hasParameter && csnDefs[fqActFunName].params[paramName]) {
|
|
1133
|
+
return csnDefs[fqActFunName]?.params[paramName];
|
|
1134
|
+
}
|
|
1135
|
+
// without parameter
|
|
1136
|
+
else if (!hasParameter) {
|
|
1137
|
+
return csnDefs[fqActFunName];
|
|
694
1138
|
}
|
|
695
|
-
parserContext.entityTypes.forEach(entity => {
|
|
696
|
-
if (entity[datatype])
|
|
697
|
-
type = datatype;
|
|
698
|
-
})
|
|
699
1139
|
}
|
|
1140
|
+
// bounded action or function
|
|
1141
|
+
else {
|
|
1142
|
+
const [, elementName] = _extractSchemaNameAndElementName(fqActFunName);
|
|
1143
|
+
const isCollection = elemNameList[1] === 'Collection' ? true : false;
|
|
1144
|
+
let parameterList = isCollection ? elemNameList[2].split(')') : elemNameList[1].split(')');
|
|
1145
|
+
let bindingParameter = parameterList[0];
|
|
700
1146
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
1147
|
+
if (bindingParameter.includes(',')) {
|
|
1148
|
+
bindingParameter = bindingParameter.split(',')[0];
|
|
1149
|
+
}
|
|
1150
|
+
const entityType = _resolveAliasedElementName(bindingParameter, parserContext);
|
|
704
1151
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
boundedCsnKind = "action";
|
|
710
|
-
boundedObj = parserContext.boundedActions;
|
|
711
|
-
}
|
|
712
|
-
else {
|
|
713
|
-
boundedEdmxKind = "Function";
|
|
714
|
-
boundedCsnKind = "function";
|
|
715
|
-
boundedObj = parserContext.boundedFunctions;
|
|
716
|
-
}
|
|
717
|
-
if (Object.keys(boundedObj).length > 0) {
|
|
718
|
-
Object.keys(boundedObj).forEach(b => {
|
|
719
|
-
for (let j = 0; j < boundedObj[b].length; j++) {
|
|
720
|
-
if (boundedObj[b][j].$Kind === boundedEdmxKind) {
|
|
721
|
-
name = b.substring(b.lastIndexOf(".") + 1);
|
|
722
|
-
obj = boundedObj[b];
|
|
723
|
-
boundedEntityName = obj[j].$Parameter[0].$Type;
|
|
724
|
-
if (csnDefs[boundedEntityName] === undefined) {
|
|
725
|
-
boundedEntityName = parserContext.namespace + "." + parserContext.entitySetToEntityTypeMap.get(boundedEntityName)[0];
|
|
726
|
-
}
|
|
727
|
-
if (csnDefs[boundedEntityName]) {
|
|
728
|
-
if (csnDefs[boundedEntityName].actions === undefined)
|
|
729
|
-
csnDefs[boundedEntityName].actions = {};
|
|
730
|
-
csnDefs[boundedEntityName].actions[name] = {};
|
|
731
|
-
_includeExternalNamespaceAnnotation(obj[0], csnDefs[boundedEntityName].actions[name]);
|
|
732
|
-
csnDefs[boundedEntityName].actions[name].kind = boundedCsnKind;
|
|
733
|
-
// add params
|
|
734
|
-
if (obj[j].$Parameter && obj[j].$Parameter.length >= 2) {
|
|
735
|
-
/**
|
|
736
|
-
* 1. Considering length >= 2 because we need to consider from the 2nd
|
|
737
|
-
* parameter onwards. 1st parameter only tells which entity is this
|
|
738
|
-
* action/function bounded to and that parameter is no where used
|
|
739
|
-
* to retrieve any data during service runtime. So skipping it and
|
|
740
|
-
* the details of 1st parameter will not be preserved in the csn.
|
|
741
|
-
* 2. The 1st parameter will be reflected back as parameter name "in"
|
|
742
|
-
* during csn to edmx conversion by the compiler.
|
|
743
|
-
* */
|
|
744
|
-
csnDefs[boundedEntityName].actions[name].params = {};
|
|
745
|
-
for (let i = 1; i < obj[j].$Parameter.length; i++) {
|
|
746
|
-
paramObj = obj[j].$Parameter[i];
|
|
747
|
-
paramName = obj[j].$Parameter[i].$Name;
|
|
748
|
-
let paramType = {};
|
|
749
|
-
let annotations = {};
|
|
750
|
-
_includeExternalNamespaceAnnotation(paramObj, annotations);
|
|
751
|
-
// check if the parameter type is pre-defined, undefined or custom defined
|
|
752
|
-
if (edmxncdsdatatype[paramObj.$Type] || edmxncdsdatatype[paramObj.$Type + "_a"])
|
|
753
|
-
paramType = generatePropertyCsn(paramObj, false);
|
|
754
|
-
else {
|
|
755
|
-
let type = _getDatatype(paramObj.$Type, parserContext);
|
|
756
|
-
if (type) paramType["type"] = type;
|
|
757
|
-
}
|
|
758
|
-
if (Object.keys(paramType).length > 0) {
|
|
759
|
-
if (paramType.type || paramType.items.type) {
|
|
760
|
-
// check if parameter type is a collection
|
|
761
|
-
if (paramObj.$Collection && !paramType.items) {
|
|
762
|
-
csnDefs[boundedEntityName].actions[name].params[paramName] = {};
|
|
763
|
-
csnDefs[boundedEntityName].actions[name].params[paramName].items = paramType;
|
|
764
|
-
if (!paramObj.$Nullable) {
|
|
765
|
-
csnDefs[boundedEntityName].actions[name].params[paramName].items["notNull"] = true;
|
|
766
|
-
}
|
|
767
|
-
} else {
|
|
768
|
-
csnDefs[boundedEntityName].actions[name].params[paramName] = paramType;
|
|
769
|
-
if (!paramType.items) {
|
|
770
|
-
if (!paramObj.$Nullable) {
|
|
771
|
-
csnDefs[boundedEntityName].actions[name].params[paramName].notNull = true;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
else {
|
|
777
|
-
// let message = '"' + paramObj.$Type + '" is not supported (in element:"' + name + '")';
|
|
778
|
-
// console.log(warn(message));
|
|
779
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${paramObj.$Type}' in element: '${name}'.`);
|
|
780
|
-
}
|
|
781
|
-
if (csnDefs[boundedEntityName].actions[name].params[paramName])
|
|
782
|
-
Object.assign(csnDefs[boundedEntityName].actions[name].params[paramName], annotations);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
// if parameters has no entries
|
|
787
|
-
if (csnDefs[boundedEntityName].actions[name].params && Object.keys(csnDefs[boundedEntityName].actions[name].params).length === 0) {
|
|
788
|
-
delete csnDefs[boundedEntityName].actions[name].params;
|
|
789
|
-
}
|
|
790
|
-
// add returns
|
|
791
|
-
if (obj[j].$ReturnType) {
|
|
792
|
-
let returnType = {};
|
|
793
|
-
let annotations = {};
|
|
794
|
-
let hasCdsTypeMapping = false;
|
|
795
|
-
// check if the return type is pre-defined, undefined or custom defined
|
|
796
|
-
if (edmxncdsdatatype[obj[j].$ReturnType.$Type] || edmxncdsdatatype[obj[j].$ReturnType.$Type + "_a"] ||
|
|
797
|
-
(!obj[j].$ReturnType.$Type && obj[j].$ReturnType.$MaxLength)
|
|
798
|
-
)
|
|
799
|
-
returnType = generatePropertyCsn(obj[j].$ReturnType, false);
|
|
800
|
-
else {
|
|
801
|
-
let type;
|
|
802
|
-
if (extendedPrimitiveTypes.includes(obj[j].$ReturnType.$Type)) {
|
|
803
|
-
hasCdsTypeMapping = true;
|
|
804
|
-
}
|
|
805
|
-
else {
|
|
806
|
-
type = _getDatatype(obj[j].$ReturnType.$Type, parserContext);
|
|
807
|
-
}
|
|
808
|
-
_includeExternalNamespaceAnnotation(obj[j].$ReturnType, annotations);
|
|
809
|
-
if (type) returnType["type"] = type;
|
|
810
|
-
if (!type && !hasCdsTypeMapping) {
|
|
811
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${obj[j].$ReturnType.$Type}'`);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
if (Object.keys(returnType).length > 0) {
|
|
815
|
-
if (returnType.type || returnType.items.type) {
|
|
816
|
-
csnDefs[boundedEntityName].actions[name].returns = {};
|
|
817
|
-
// check if return type is a collection
|
|
818
|
-
if (obj[j].$ReturnType.$Collection && !returnType.items) {
|
|
819
|
-
csnDefs[boundedEntityName].actions[name].returns.items = returnType;
|
|
820
|
-
if (!obj[j].$ReturnType.$Nullable) {
|
|
821
|
-
csnDefs[boundedEntityName].actions[name].returns.items["notNull"] = true;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
else {
|
|
825
|
-
csnDefs[boundedEntityName].actions[name].returns = returnType;
|
|
826
|
-
if (!returnType.items) {
|
|
827
|
-
if (!obj[j].$ReturnType.$Nullable){
|
|
828
|
-
csnDefs[boundedEntityName].actions[name].returns["notNull"] = true;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
} else {
|
|
834
|
-
csnDefs[boundedEntityName].actions[name].returns = {};
|
|
835
|
-
if (obj[j].$ReturnType.$Collection && !returnType.items) {
|
|
836
|
-
csnDefs[boundedEntityName].actions[name].returns = { "@odata.Type": obj[j].$ReturnType.$Type };
|
|
837
|
-
csnDefs[boundedEntityName].actions[name].returns.items = { "type": "cds.String" };
|
|
838
|
-
}
|
|
839
|
-
else
|
|
840
|
-
csnDefs[boundedEntityName].actions[name].returns = { "type": "cds.String", "@odata.Type": obj[j].$ReturnType.$Type };
|
|
841
|
-
}
|
|
842
|
-
if (csnDefs[boundedEntityName].actions[name].returns)
|
|
843
|
-
Object.assign(csnDefs[boundedEntityName].actions[name].returns, annotations);
|
|
844
|
-
} else {
|
|
845
|
-
if (obj[j].$Kind === 'Function')
|
|
846
|
-
throw new Error(messages.NO_RETURN_TYPE + `'${name}'.`);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
1152
|
+
if (csnDefs[entityType]?.actions[elementName]) {
|
|
1153
|
+
// with parameter
|
|
1154
|
+
if (hasParameter && csnDefs[entityType].actions[elementName].params[paramName]) {
|
|
1155
|
+
return csnDefs[entityType].actions[elementName].params[paramName];
|
|
850
1156
|
}
|
|
851
|
-
|
|
1157
|
+
// without parameter
|
|
1158
|
+
else if (!hasParameter) {
|
|
1159
|
+
return csnDefs[entityType].actions[elementName];
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
852
1162
|
}
|
|
853
1163
|
}
|
|
854
1164
|
|
|
855
|
-
function
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1165
|
+
function _resolveAnnotationTarget(targetPath, csnDefs, parserContext) {
|
|
1166
|
+
const indexOfSlash = targetPath.indexOf('/');
|
|
1167
|
+
const indexOfOpenBrace = targetPath.indexOf('(');
|
|
1168
|
+
const hasPropOrParam = (indexOfSlash > 0);
|
|
1169
|
+
let elemNameList, fqName;
|
|
1170
|
+
|
|
1171
|
+
// Case 1: Direct path
|
|
1172
|
+
if (indexOfOpenBrace === -1 && indexOfSlash === -1) {
|
|
1173
|
+
fqName = _resolveAliasedElementName(targetPath, parserContext);
|
|
1174
|
+
|
|
1175
|
+
if (csnDefs[fqName]) {
|
|
1176
|
+
return csnDefs[fqName];
|
|
1177
|
+
}
|
|
1178
|
+
else if (parserContext.entityContainerAnnotations[targetPath]) {
|
|
1179
|
+
return _resolveEntityContainerAnnotationTarget(fqName, csnDefs, hasPropOrParam, parserContext);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
// Case 2: Direct path with /
|
|
1183
|
+
else if (indexOfOpenBrace === -1 && indexOfSlash !== -1) {
|
|
1184
|
+
elemNameList = targetPath.split('/');
|
|
1185
|
+
fqName = _resolveAliasedElementName(elemNameList[0], parserContext);
|
|
1186
|
+
|
|
1187
|
+
if (csnDefs[fqName]) {
|
|
1188
|
+
if (csnDefs[fqName]?.elements?.[elemNameList[1]]) {
|
|
1189
|
+
return csnDefs[fqName].elements[elemNameList[1]];
|
|
1190
|
+
}
|
|
1191
|
+
else if (csnDefs[fqName]?.enum?.[elemNameList[1]]) {
|
|
1192
|
+
return csnDefs[fqName].enum[elemNameList[1]];
|
|
863
1193
|
}
|
|
864
1194
|
}
|
|
865
|
-
|
|
866
|
-
|
|
1195
|
+
// Entity Container path with /
|
|
1196
|
+
else if (parserContext.entityContainerAnnotations[targetPath]) {
|
|
1197
|
+
return _resolveEntityContainerAnnotationTarget(targetPath, csnDefs, hasPropOrParam, parserContext);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
// Case 3: actions and functions path with or without parameter
|
|
1201
|
+
else {
|
|
1202
|
+
return _resolveActionFunctionAnnotationTarget(targetPath, csnDefs, hasPropOrParam, parserContext);
|
|
1203
|
+
}
|
|
867
1204
|
}
|
|
868
1205
|
|
|
869
|
-
function
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1206
|
+
function _parseAnnotations(schemaAnnotations, csnDefs, parserContext) {
|
|
1207
|
+
Object.keys(schemaAnnotations).forEach(annotationTargetPath => {
|
|
1208
|
+
let csnDefsTargetObject = _resolveAnnotationTarget(annotationTargetPath, csnDefs, parserContext);
|
|
1209
|
+
// if the target object exists, process the annotation terms
|
|
1210
|
+
if (csnDefsTargetObject) {
|
|
1211
|
+
let annotationTerms = schemaAnnotations[annotationTargetPath];
|
|
1212
|
+
Object.keys(annotationTerms).forEach(annotationTerm => {
|
|
1213
|
+
let annotationTermObject = schemaAnnotations[annotationTargetPath][annotationTerm];
|
|
1214
|
+
_parseAnnotationTerms(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
877
1218
|
}
|
|
878
1219
|
|
|
879
|
-
function
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
isOpenType = cTypes[element].$OpenType;
|
|
895
|
-
if (baseType) {
|
|
896
|
-
if (parserContext.complexTypes[baseType]) {
|
|
897
|
-
complexCsn[element].includes = [];
|
|
898
|
-
complexCsn[element].includes.push(baseType);
|
|
899
|
-
if (!parserContext.complexTypeOpenEntries.includes(baseType)) parserContext.complexTypeOpenEntries.push(baseType);
|
|
1220
|
+
function _parseEntityContainer(csnDefs, entityContainerElements, parserContext) {
|
|
1221
|
+
const entityContainerElementNames = Object.keys(entityContainerElements);
|
|
1222
|
+
const redundantElementNames = [];
|
|
1223
|
+
|
|
1224
|
+
entityContainerElementNames.forEach(ecElement => {
|
|
1225
|
+
if (entityContainerElements[ecElement] instanceof Object) {
|
|
1226
|
+
let definitionRefName, kind;
|
|
1227
|
+
|
|
1228
|
+
if (entityContainerElements[ecElement].$Action) {
|
|
1229
|
+
definitionRefName = entityContainerElements[ecElement].$Action;
|
|
1230
|
+
kind = 'action';
|
|
1231
|
+
}
|
|
1232
|
+
else if (entityContainerElements[ecElement].$Function) {
|
|
1233
|
+
definitionRefName = entityContainerElements[ecElement].$Function;
|
|
1234
|
+
kind = 'function';
|
|
900
1235
|
}
|
|
901
1236
|
else {
|
|
902
|
-
|
|
1237
|
+
definitionRefName = entityContainerElements[ecElement].$Type;
|
|
1238
|
+
kind = 'entity';
|
|
903
1239
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
parserContext
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
} else if (parserContext.complexTypes[cTypes[element][property].$Type]) {
|
|
917
|
-
propertyType = cTypes[element][property];
|
|
918
|
-
complexCsn[element].elements[property] = _generateComplexTypePropertyCsn(propertyType);
|
|
919
|
-
} else if (parserContext.typeDefinitions[cTypes[element][property].$Type]) {
|
|
920
|
-
propertyType = cTypes[element][property];
|
|
921
|
-
complexCsn[element].elements[property] = _generateTypeDefinitionPropertyCsn(propertyType);
|
|
922
|
-
} else if (parserContext.enumTypes[cTypes[element][property].$Type]) {
|
|
923
|
-
enumType = cTypes[element][property];
|
|
924
|
-
complexCsn[element].elements[property] = generatePropertyCsn(enumType, false);
|
|
925
|
-
} else {
|
|
926
|
-
// let message = '"' + cTypes[element][property].$Type + '" is not supported (in element:"' + property + '")';
|
|
927
|
-
// console.log(warn(message));
|
|
928
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${cTypes[element][property].$Type}' in element: '${property}'.`);
|
|
929
|
-
}
|
|
930
|
-
} else if (property.startsWith("@")) {
|
|
931
|
-
complexCsn[element][property] = cTypes[element][property];
|
|
1240
|
+
|
|
1241
|
+
let [, elementName] = _extractSchemaNameAndElementName(definitionRefName);
|
|
1242
|
+
let fqDefRefName = _resolveAliasedElementName(definitionRefName, parserContext);
|
|
1243
|
+
let fqElementName = parserContext.primarySchema + "." + ecElement;
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* For now we don't support the collision between entitycontainer element name
|
|
1247
|
+
* and any of the schema elemetents like entitytype, complextype, etc.
|
|
1248
|
+
* TODO: In future we might have to support it.
|
|
1249
|
+
*/
|
|
1250
|
+
if (csnDefs[fqElementName] && csnDefs[fqElementName].kind !== kind) {
|
|
1251
|
+
throw new Error(messages.ELEMENT_NAME_COLLISION + ecElement);
|
|
932
1252
|
}
|
|
933
|
-
});
|
|
934
|
-
});
|
|
935
1253
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1254
|
+
if (fqElementName !== fqDefRefName) {
|
|
1255
|
+
csnDefs[fqElementName] = Object.assign({}, csnDefs[fqDefRefName]);
|
|
1256
|
+
/**
|
|
1257
|
+
* Capture all the defintion reference element name which are no
|
|
1258
|
+
* longer required after the entity container elements are populated.
|
|
1259
|
+
* We will delete their entries from the csnDefs later.
|
|
1260
|
+
*/
|
|
1261
|
+
if (!entityContainerElementNames.includes(elementName)) {
|
|
1262
|
+
redundantElementNames.push(fqDefRefName);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
if (entityContainerElements[ecElement].$Singleton) {
|
|
1266
|
+
csnDefs[fqElementName]['@odata.singleton'] = true;
|
|
1267
|
+
}
|
|
1268
|
+
if (entityContainerElements[ecElement].$SingletonNullable) {
|
|
1269
|
+
csnDefs[fqElementName]['@odata.singleton.nullable'] = true;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
939
1272
|
});
|
|
940
1273
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
Object.keys(parserContext.typeDefinitions).forEach(element => {
|
|
948
|
-
typeDef[element] = {};
|
|
949
|
-
typeDef[element].kind = 'type';
|
|
950
|
-
typeDef[element]['@cds.external'] = true;
|
|
951
|
-
|
|
952
|
-
let underlyingType = parserContext.typeDefinitions[element].$UnderlyingType;
|
|
953
|
-
parserContext.typeDefinitions[element].$Type = underlyingType;
|
|
954
|
-
|
|
955
|
-
if (edmxncdsdatatype[underlyingType] ) {
|
|
956
|
-
propertyType = parserContext.typeDefinitions[element];
|
|
957
|
-
typeDef[element] = Object.assign(typeDef[element], generatePropertyCsn(propertyType, false));
|
|
958
|
-
if (typeDef[element].type === "cds.LargeString") typeDef[element].type = "cds.String";
|
|
959
|
-
} else {
|
|
960
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${underlyingType}'`);
|
|
961
|
-
}
|
|
1274
|
+
/**
|
|
1275
|
+
* remove all the entries from the csnDefs which had a mapping in entity
|
|
1276
|
+
* container and we have successfully copied their content to mapped elements
|
|
1277
|
+
*/
|
|
1278
|
+
redundantElementNames.forEach(name => {
|
|
1279
|
+
delete csnDefs[name];
|
|
962
1280
|
});
|
|
963
|
-
return typeDef;
|
|
964
|
-
}
|
|
965
1281
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
if (flag == 0) {
|
|
970
|
-
unbounded = parserContext.unboundedActions;
|
|
971
|
-
importToMap = parserContext.actionImportToActionMap;
|
|
972
|
-
unboundedCsnKind = "action";
|
|
973
|
-
}
|
|
974
|
-
else {
|
|
975
|
-
unbounded = parserContext.unboundedFunctions;
|
|
976
|
-
importToMap = parserContext.functionImportToFunctionMap;
|
|
977
|
-
unboundedCsnKind = "function";
|
|
978
|
-
}
|
|
979
|
-
if (Object.keys(unbounded).length > 0) {
|
|
980
|
-
Object.keys(unbounded).forEach(ub => {
|
|
981
|
-
let obj = {};
|
|
982
|
-
let objType = {}, objData = {}, temp = {};
|
|
983
|
-
if (importToMap && importToMap.get(ub)) {
|
|
984
|
-
temp = ub.split(".").pop();
|
|
985
|
-
objType = parserContext.namespace + "." + temp;
|
|
986
|
-
objData = schemaData[0][temp][0];
|
|
987
|
-
obj[objType] = {};
|
|
988
|
-
obj[objType].kind = unboundedCsnKind;
|
|
989
|
-
obj[objType]['@cds.external'] = true;
|
|
990
|
-
_includeExternalNamespaceAnnotation(objData, obj[objType]);
|
|
991
|
-
// add params
|
|
992
|
-
if (objData.$Parameter && objData.$Parameter.length > 0) {
|
|
993
|
-
obj[objType].params = {};
|
|
994
|
-
for (let i = 0; i < objData.$Parameter.length; i++) {
|
|
995
|
-
let paramType = {};
|
|
996
|
-
let annotations = {};
|
|
997
|
-
_includeExternalNamespaceAnnotation(objData.$Parameter[i], annotations);
|
|
998
|
-
// check if the parameter type is pre-defined, undefined or custom defined
|
|
999
|
-
if (edmxncdsdatatype[objData.$Parameter[i].$Type] || edmxncdsdatatype[objData.$Parameter[i].$Type + "_a"]) {
|
|
1000
|
-
paramType = generatePropertyCsn(objData.$Parameter[i], false)
|
|
1001
|
-
} else {
|
|
1002
|
-
let type = _getDatatype(objData.$Parameter[i].$Type, parserContext);
|
|
1003
|
-
if (type) paramType["type"] = type;
|
|
1004
|
-
}
|
|
1005
|
-
if (Object.keys(paramType).length > 0) {
|
|
1006
|
-
if (paramType.type || paramType.items.type) {
|
|
1007
|
-
// check if parameter type is a collection
|
|
1008
|
-
if (objData.$Parameter[i].$Collection && !paramType.items) {
|
|
1009
|
-
obj[objType].params[objData.$Parameter[i].$Name] = {};
|
|
1010
|
-
obj[objType].params[objData.$Parameter[i].$Name].items = paramType;
|
|
1011
|
-
if (!objData.$Parameter[i].$Nullable) {
|
|
1012
|
-
obj[objType].params[objData.$Parameter[i].$Name].items["notNull"] = true;
|
|
1013
|
-
}
|
|
1014
|
-
} else {
|
|
1015
|
-
obj[objType].params[objData.$Parameter[i].$Name] = paramType;
|
|
1016
|
-
if (!paramType.items) {
|
|
1017
|
-
if (!objData.$Parameter[i].$Nullable) {
|
|
1018
|
-
obj[objType].params[objData.$Parameter[i].$Name]["notNull"] = true;
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
} else {
|
|
1024
|
-
// let message = '"' + objData.$Parameter[i].$Type + '" is not supported (in element:"' + temp + '")';
|
|
1025
|
-
// console.log(warn(message));
|
|
1026
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${objData.$Parameter[i].$Type}' in element: '${temp}'.`);
|
|
1027
|
-
}
|
|
1028
|
-
if (obj[objType].params[objData.$Parameter[i].$Name])
|
|
1029
|
-
Object.assign(obj[objType].params[objData.$Parameter[i].$Name], annotations)
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
// if parameters has no entries
|
|
1033
|
-
if (obj[objType].params && Object.keys(obj[objType].params).length === 0) {
|
|
1034
|
-
delete obj[objType].params;
|
|
1035
|
-
}
|
|
1036
|
-
// add returns
|
|
1037
|
-
if (objData.$ReturnType && Object.keys(objData.$ReturnType).length > 0 && obj) {
|
|
1038
|
-
let returnValue = {};
|
|
1039
|
-
let annotations = {};
|
|
1040
|
-
let hasCdsTypeMapping = false;
|
|
1041
|
-
// check if the return type is pre-defined, undefined or custom defined
|
|
1042
|
-
if (edmxncdsdatatype[objData.$ReturnType.$Type] || edmxncdsdatatype[objData.$ReturnType.$Type + "_a"] ||
|
|
1043
|
-
(!objData.$ReturnType.$Type && objData.$ReturnType.$MaxLength)
|
|
1044
|
-
){
|
|
1045
|
-
returnValue = generatePropertyCsn(objData.$ReturnType, false);
|
|
1046
|
-
} else {
|
|
1047
|
-
let type;
|
|
1048
|
-
if (extendedPrimitiveTypes.includes(objData.$ReturnType.$Type)) {
|
|
1049
|
-
hasCdsTypeMapping = true;
|
|
1050
|
-
} else {
|
|
1051
|
-
type = _getDatatype(objData.$ReturnType.$Type, parserContext);
|
|
1052
|
-
}
|
|
1053
|
-
_includeExternalNamespaceAnnotation(objData.$ReturnType, annotations);
|
|
1054
|
-
if (type) returnValue["type"] = type;
|
|
1055
|
-
if (!type && !hasCdsTypeMapping) {
|
|
1056
|
-
throw new Error(messages.UNRESOLVED_TYPE + `'${objData.$ReturnType.$Type}'`);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
if (Object.keys(returnValue).length > 0) {
|
|
1060
|
-
if (returnValue.type || returnValue.items.type) {
|
|
1061
|
-
obj[objType].returns = {};
|
|
1062
|
-
// check if return type is a collection
|
|
1063
|
-
if (objData.$ReturnType.$Collection && !returnValue.items) {
|
|
1064
|
-
obj[objType].returns.items = {};
|
|
1065
|
-
obj[objType].returns.items = returnValue;
|
|
1066
|
-
if (!objData.$ReturnType.$Nullable) {
|
|
1067
|
-
obj[objType].returns.items["notNull"] = true;
|
|
1068
|
-
}
|
|
1069
|
-
} else {
|
|
1070
|
-
obj[objType].returns = returnValue;
|
|
1071
|
-
if (!returnValue.items) {
|
|
1072
|
-
if (!objData.$ReturnType.$Nullable) {
|
|
1073
|
-
obj[objType].returns["notNull"] = true;
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
else {
|
|
1080
|
-
obj[objType].returns = {};
|
|
1081
|
-
// check if return type is a collection
|
|
1082
|
-
if ((objData.$ReturnType.$Collection && !returnValue.items)) {
|
|
1083
|
-
obj[objType].returns = { "@odata.Type":objData.$ReturnType.$Type };
|
|
1084
|
-
obj[objType].returns.items = {};
|
|
1085
|
-
obj[objType].returns.items = { "type": "cds.String" };
|
|
1086
|
-
} else
|
|
1087
|
-
obj[objType].returns = { "type": "cds.String", "@odata.Type": objData.$ReturnType.$Type };
|
|
1088
|
-
}
|
|
1089
|
-
if (obj[objType].returns)
|
|
1090
|
-
Object.assign(obj[objType].returns, annotations);
|
|
1091
|
-
} else {
|
|
1092
|
-
if (objData.$Kind === 'Function')
|
|
1093
|
-
throw new Error(messages.NO_RETURN_TYPE + `'${ub}'.`);
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
unboundedObj = Object.assign(unboundedObj, obj);
|
|
1097
|
-
});
|
|
1282
|
+
// parse all the entity container annotations
|
|
1283
|
+
if (parserContext.entityContainerAnnotations) {
|
|
1284
|
+
_parseAnnotations(parserContext.entityContainerAnnotations, csnDefs, parserContext);
|
|
1098
1285
|
}
|
|
1099
|
-
|
|
1286
|
+
|
|
1287
|
+
return csnDefs;
|
|
1100
1288
|
}
|
|
1101
1289
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
csn.definitions = {};
|
|
1290
|
+
/**
|
|
1291
|
+
* @param {List<Object>} dataObjList, action/function object list
|
|
1292
|
+
* @returns unique parameter list
|
|
1293
|
+
*/
|
|
1294
|
+
function _collectAllParameters(dataObjList) {
|
|
1295
|
+
let uniqueParams = [], paramNames = [], params;
|
|
1109
1296
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
// replace the namespace alias value with original namespace value
|
|
1113
|
-
if (edmxAsJson[data].$Alias)
|
|
1114
|
-
edmxAsJson[data] = common.replaceAliasValue(JSON.stringify(edmxAsJson[data]), data, edmxAsJson[data].$Alias);
|
|
1115
|
-
schemaData.push(edmxAsJson[data]);
|
|
1116
|
-
}
|
|
1117
|
-
});
|
|
1297
|
+
for (let dataObj of dataObjList) {
|
|
1298
|
+
params = dataObj.$Parameter;
|
|
1118
1299
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
if (entitySetData[data].$Kind && entitySetData[data].$Kind === 'EntityContainer') {
|
|
1125
|
-
entityContainer = entitySetData[data];
|
|
1300
|
+
if (params) {
|
|
1301
|
+
for (let i = 0; i < params.length; i++) {
|
|
1302
|
+
if (!paramNames.includes(params[i].$Name)) {
|
|
1303
|
+
paramNames.push(params[i].$Name);
|
|
1304
|
+
uniqueParams.push(params[i]);
|
|
1126
1305
|
}
|
|
1127
1306
|
}
|
|
1128
|
-
}
|
|
1129
|
-
Object.keys(entityContainer).forEach(data => {
|
|
1130
|
-
if (ignoreObjects.indexOf(data) === -1 && entityContainer[data] instanceof Object &&
|
|
1131
|
-
entityContainer[data].$Type && (entityContainer[data].$Collection || entityContainer[data].$Singleton)) {
|
|
1132
|
-
parserContext.entitySetToEntityTypeMap.set(entityContainer[data].$Type, data);
|
|
1133
|
-
} else if (ignoreObjects.indexOf(data) === -1 && entityContainer[data] instanceof Object &&
|
|
1134
|
-
entityContainer[data].$Action) {
|
|
1135
|
-
parserContext.actionImportToActionMap.set(entityContainer[data].$Action, data);
|
|
1136
|
-
parserContext.unboundedActions[entityContainer[data].$Action] = entitySetData[data];
|
|
1137
|
-
} else if (ignoreObjects.indexOf(data) === -1 && entityContainer[data] instanceof Object &&
|
|
1138
|
-
entityContainer[data].$Function) {
|
|
1139
|
-
parserContext.functionImportToFunctionMap.set(entityContainer[data].$Function, data);
|
|
1140
|
-
parserContext.unboundedFunctions[entityContainer[data].$Function] = entitySetData[data];
|
|
1141
|
-
}
|
|
1142
|
-
});
|
|
1307
|
+
}
|
|
1143
1308
|
}
|
|
1144
1309
|
|
|
1145
|
-
|
|
1146
|
-
csn.definitions[parserContext.namespace].kind = 'service';
|
|
1147
|
-
csn.definitions[parserContext.namespace]['@cds.external'] = true;
|
|
1148
|
-
|
|
1149
|
-
_collectEnumType(schemaData, parserContext);
|
|
1150
|
-
_collectTypeDefinition(schemaData, parserContext);
|
|
1151
|
-
_collectBounded(schemaData, 0, parserContext);
|
|
1152
|
-
_collectBounded(schemaData, 1, parserContext);
|
|
1153
|
-
|
|
1154
|
-
// Step 1: get all complex types.
|
|
1155
|
-
csn.definitions = Object.assign(_generateCSNForComplexType(schemaData, parserContext), csn.definitions);
|
|
1156
|
-
// Step 2: get all type definition
|
|
1157
|
-
csn.definitions = Object.assign(_parseTypeDefinition(parserContext), csn.definitions);
|
|
1158
|
-
// Step 3: get all entity type
|
|
1159
|
-
csn.definitions = Object.assign(_parseEntityType(schemaData, ignorePersistenceSkip, mockServerUc, parserContext), csn.definitions);
|
|
1160
|
-
// Step 4: get all unbounded action & function
|
|
1161
|
-
csn.definitions = Object.assign(_parseUnbounded(schemaData, 0, parserContext), csn.definitions);
|
|
1162
|
-
csn.definitions = Object.assign(_parseUnbounded(schemaData, 1, parserContext), csn.definitions);
|
|
1163
|
-
// Step 5: get all enum types
|
|
1164
|
-
csn.definitions = Object.assign(_generateCSNForEnumType(schemaData, parserContext), csn.definitions);
|
|
1165
|
-
// Step 6: add entity container properties
|
|
1166
|
-
csn.definitions = Object.assign(_parseEntityContainer(csn.definitions, schemaData, parserContext, parserContext), csn.definitions);
|
|
1167
|
-
// Step 7: get all bounded action & function
|
|
1168
|
-
_parseBounded(csn.definitions, 0, parserContext);
|
|
1169
|
-
_parseBounded(csn.definitions, 1, parserContext);
|
|
1170
|
-
// Step 8: get all annotations
|
|
1171
|
-
_generateDocumentation(edmxAsJson, schemaData, csn.definitions, parserContext);
|
|
1172
|
-
|
|
1173
|
-
return JSON.stringify(csn, null, 4);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
function _replaceSpecialCharacters(text) {
|
|
1177
|
-
return text.replace(/(?:\\[rn]|[\r\n]+)+/gm, "\n").replace(/\s+/g, ' ').replace(/"/g, """).trim();
|
|
1310
|
+
return uniqueParams;
|
|
1178
1311
|
}
|
|
1179
1312
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1313
|
+
/**
|
|
1314
|
+
* @param {Boolean} isAction, to identify action/function
|
|
1315
|
+
* @param {String} fqName, fully qualified name
|
|
1316
|
+
* @param {List<Object>} schemaDataList
|
|
1317
|
+
* @param {Object} parserContext
|
|
1318
|
+
*
|
|
1319
|
+
* Scenarios:
|
|
1320
|
+
* 1. UnBounded Actions can't have collision. Action name must be unique.
|
|
1321
|
+
* 2. UnBounded Functions can have collision; capture the max number of parameters.
|
|
1322
|
+
*/
|
|
1323
|
+
function _populateUnboundedActionFunction(isAction, fqName, schemaDataList, parserContext) {
|
|
1324
|
+
let [schemaName, actFunName] = _extractSchemaNameAndElementName(fqName);
|
|
1325
|
+
let indexOfSchema;
|
|
1326
|
+
[indexOfSchema, schemaName] = _getIndexAndNamespaceOfSchema(fqName, schemaName, parserContext);
|
|
1327
|
+
const dataList = schemaDataList[indexOfSchema][actFunName];
|
|
1328
|
+
let actionFunctionObject = dataList[0];
|
|
1329
|
+
fqName = schemaName + '.' + actFunName; // modified fqName as per new schemaName
|
|
1330
|
+
|
|
1331
|
+
if (isAction) {
|
|
1332
|
+
if (dataList.length === 1) {
|
|
1333
|
+
parserContext.unboundedActions[fqName] = actionFunctionObject;
|
|
1334
|
+
}
|
|
1335
|
+
else {
|
|
1336
|
+
throw new Error(messages.UNBOUNDED_ACTION_COLLISION + `'${actFunName}' of Schema: ${schemaName} `);
|
|
1188
1337
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
if (dataList.length > 1) {
|
|
1341
|
+
actionFunctionObject['$Parameter'] = _collectAllParameters(dataList);
|
|
1342
|
+
parserContext.unboundedFunctions[fqName] = actionFunctionObject;
|
|
1191
1343
|
}
|
|
1192
|
-
else
|
|
1193
|
-
|
|
1344
|
+
else {
|
|
1345
|
+
parserContext.unboundedFunctions[fqName] = actionFunctionObject;
|
|
1194
1346
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
path[targetString] = annotation[1][1];
|
|
1199
|
-
}
|
|
1200
|
-
else if (annotation[1][targetString]?.$Cast) {
|
|
1201
|
-
path[targetString] = Number(annotation[1][targetString].$Cast);
|
|
1202
|
-
}
|
|
1203
|
-
else if (annotation[1][targetString]?.$Path) {
|
|
1204
|
-
path[targetString] = { "=": annotation[1][targetString].$Path };
|
|
1205
|
-
}
|
|
1206
|
-
else {
|
|
1207
|
-
path[targetString] = annotation[1][targetString] === null ? true : annotation[1][targetString];
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1347
|
+
// throw error if the return type is missing
|
|
1348
|
+
if (!parserContext.unboundedFunctions[fqName].$ReturnType) {
|
|
1349
|
+
throw new Error(messages.NO_RETURN_TYPE + actFunName);
|
|
1210
1350
|
}
|
|
1211
1351
|
}
|
|
1212
1352
|
}
|
|
1213
1353
|
|
|
1214
|
-
function
|
|
1215
|
-
|
|
1216
|
-
|
|
1354
|
+
function _collectUnboundedActionFunction(schemaDataList, parserContext) {
|
|
1355
|
+
for (let actionName of parserContext.actionToActionImportMap.keys()) {
|
|
1356
|
+
_populateUnboundedActionFunction(true, actionName, schemaDataList, parserContext);
|
|
1357
|
+
}
|
|
1358
|
+
for (let functionName of parserContext.functionToFunctionImportMap.keys()) {
|
|
1359
|
+
_populateUnboundedActionFunction(false, functionName, schemaDataList, parserContext);
|
|
1217
1360
|
}
|
|
1218
|
-
return pathName;
|
|
1219
1361
|
}
|
|
1220
1362
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
else {
|
|
1237
|
-
_checkAnnotatationTerm(annotation, csnDefs[annotation[0]], no_of_terms, str, vocab);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1363
|
+
/**
|
|
1364
|
+
* @param {Boolean} isAction
|
|
1365
|
+
* @param {String} currNamespace
|
|
1366
|
+
* @param {String} elementName
|
|
1367
|
+
* @param {List<Object>} elementDataList, bounded action/function details
|
|
1368
|
+
* @param {Object} parserContext
|
|
1369
|
+
*
|
|
1370
|
+
* Scenarios for bounded actions, functions:
|
|
1371
|
+
* 1. Actions with same name and binding parameter type can't have collision.
|
|
1372
|
+
* 2. Functions can have collision and we will capture the max number of parameters.
|
|
1373
|
+
* Special scenario is when same function name is bounded to different entity as well.
|
|
1374
|
+
*/
|
|
1375
|
+
function _collectBoundedActionFunction(isAction, currNamespace, elementName, elementDataList, parserContext) {
|
|
1376
|
+
let functionObjList; // list to collect all the collision obj for function
|
|
1240
1377
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
else if (annotation[0].includes("Container")) {
|
|
1250
|
-
let path = annotation[0].substring(0, annotation[0].lastIndexOf('.'));
|
|
1251
|
-
let actualPath = checkEntitySetMap(path, parserContext);
|
|
1252
|
-
_checkAnnotatationTerm(annotation, csnDefs[actualPath], no_of_terms, str, vocab);
|
|
1378
|
+
for (let i = 0; i < elementDataList.length; i++) {
|
|
1379
|
+
let dataObj = elementDataList[i];
|
|
1380
|
+
let boundedEntityName = dataObj.$Parameter[0].$Type;
|
|
1381
|
+
let [schemaName, entityName] = _extractSchemaNameAndElementName(boundedEntityName);
|
|
1382
|
+
|
|
1383
|
+
// binding parameter type should be within the current schema only
|
|
1384
|
+
if (currNamespace !== schemaName && currNamespace !== parserContext.schemaAliasToNamespace[schemaName]) {
|
|
1385
|
+
throw new Error(messages.INVALID_BOUNDED_PARAM_TYPE + ` Check the Element: ${elementName} of Schema: ${currNamespace} `);
|
|
1253
1386
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
let pathFromEntityTypeMap = parserContext.entitySetToEntityTypeMap.get(path[0]);
|
|
1257
|
-
if (pathFromEntityTypeMap != null) {
|
|
1258
|
-
if (pathFromEntityTypeMap.length > 1) {
|
|
1259
|
-
for (let index = 0; index < pathFromEntityTypeMap.length; index++) {
|
|
1260
|
-
let path1 = checkEntitySetMap(path[0], parserContext, index);
|
|
1261
|
-
_checkAnnotatationTerm(annotation, csnDefs[path1], no_of_terms, str, vocab);
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1387
|
+
if (!parserContext.entityTypes.includes(currNamespace + '.' + entityName)) {
|
|
1388
|
+
throw new Error(messages.UNRESOLVED_TYPE + boundedEntityName);
|
|
1265
1389
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
for (let index = 0; index < pathFromEntityTypeMap.length; index++) {
|
|
1273
|
-
let path1 = checkEntitySetMap(path[0], parserContext, index);
|
|
1274
|
-
_checkAnnotatationTerm(annotation, csnDefs[path1]["elements"][path[1]], no_of_terms, str, vocab);
|
|
1390
|
+
boundedEntityName = currNamespace + '.' + entityName;
|
|
1391
|
+
|
|
1392
|
+
if (isAction) {
|
|
1393
|
+
if (!parserContext.entityToBoundedActions[boundedEntityName]?.[elementName]) {
|
|
1394
|
+
if (!parserContext.entityToBoundedActions[boundedEntityName]) {
|
|
1395
|
+
parserContext.entityToBoundedActions[boundedEntityName] = {};
|
|
1275
1396
|
}
|
|
1397
|
+
parserContext.entityToBoundedActions[boundedEntityName][elementName] = dataObj;
|
|
1276
1398
|
}
|
|
1277
1399
|
else {
|
|
1278
|
-
|
|
1279
|
-
if (csnDefs[path1]) {
|
|
1280
|
-
_checkAnnotatationTerm(annotation, csnDefs[path1]["elements"][path[1]], no_of_terms, str, vocab);
|
|
1281
|
-
}
|
|
1400
|
+
throw new Error(messages.BOUNDED_ACTION_COLLISION + ` Check the Element: ${elementName} of Schema: ${currNamespace} `);
|
|
1282
1401
|
}
|
|
1283
1402
|
}
|
|
1284
1403
|
else {
|
|
1285
|
-
if (
|
|
1286
|
-
|
|
1404
|
+
if (!parserContext.entityToBoundedFunctions[boundedEntityName]?.[elementName]) {
|
|
1405
|
+
if (!parserContext.entityToBoundedFunctions[boundedEntityName]) {
|
|
1406
|
+
parserContext.entityToBoundedFunctions[boundedEntityName] = {};
|
|
1407
|
+
}
|
|
1408
|
+
if (!functionObjList || !parserContext.entityToBoundedFunctions[boundedEntityName][elementName]) {
|
|
1409
|
+
functionObjList = [];
|
|
1410
|
+
}
|
|
1411
|
+
parserContext.entityToBoundedFunctions[boundedEntityName][elementName] = dataObj;
|
|
1412
|
+
functionObjList.push(dataObj);
|
|
1287
1413
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
let temp = path[1].split(')');
|
|
1295
|
-
let path1 = temp[0].split(',');
|
|
1296
|
-
let path2 = path[0].split('.');
|
|
1297
|
-
if (csnDefs[path1[0]]) {
|
|
1298
|
-
_checkAnnotatationTerm(annotation, csnDefs[path1[0]]["actions"][path2[1]], no_of_terms, str, vocab);
|
|
1414
|
+
else {
|
|
1415
|
+
functionObjList.push(dataObj);
|
|
1416
|
+
let functionObject = functionObjList[0];
|
|
1417
|
+
functionObject['$Parameter'] = _collectAllParameters(functionObjList);
|
|
1418
|
+
functionObjList = [functionObject]; // reset the function object list with recently formed object
|
|
1419
|
+
parserContext.entityToBoundedFunctions[boundedEntityName][elementName] = functionObject;
|
|
1299
1420
|
}
|
|
1300
|
-
|
|
1301
|
-
if (
|
|
1302
|
-
|
|
1303
|
-
} else {
|
|
1304
|
-
let path1 = path[1].split('/');
|
|
1305
|
-
if (csnDefs[path[0]] && csnDefs[path[0]]["params"]) {
|
|
1306
|
-
_checkAnnotatationTerm(annotation, csnDefs[path[0]]["params"][path1[1]], no_of_terms, str, vocab);
|
|
1307
|
-
}
|
|
1421
|
+
// throw error if the return type is missing
|
|
1422
|
+
if (!parserContext.entityToBoundedFunctions[boundedEntityName][elementName].$ReturnType) {
|
|
1423
|
+
throw new Error(messages.NO_RETURN_TYPE + elementName);
|
|
1308
1424
|
}
|
|
1309
1425
|
}
|
|
1310
1426
|
}
|
|
1311
1427
|
}
|
|
1312
1428
|
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1429
|
+
/**
|
|
1430
|
+
* @param {Object} edmxAsJson, intermediate JSON
|
|
1431
|
+
* @param {List<Object>} schemaDataList, list of all the schema contents
|
|
1432
|
+
* @param {Object} parserContext, contains parser level context informations
|
|
1433
|
+
*/
|
|
1434
|
+
function _collectSchemaElements(edmxAsJson, schemaDataList, parserContext) {
|
|
1435
|
+
|
|
1436
|
+
_collectUnboundedActionFunction(schemaDataList, parserContext);
|
|
1437
|
+
|
|
1438
|
+
// collect entityType, enumType, complexType, typeDefinition, bounded action/function, annotations
|
|
1439
|
+
for (let schemaData of schemaDataList) {
|
|
1440
|
+
let currNamespace = schemaData.$SchemaNamespace;
|
|
1441
|
+
|
|
1442
|
+
Object.keys(schemaData).forEach(elementName => {
|
|
1443
|
+
let fqName = currNamespace + '.' + elementName;
|
|
1444
|
+
let elementData = schemaData[elementName];
|
|
1445
|
+
|
|
1446
|
+
if (elementData instanceof Object) {
|
|
1447
|
+
if (elementData.$Kind === 'EntityType') parserContext.entityTypes.push(fqName);
|
|
1448
|
+
else if (elementData.$Kind === 'EnumType') parserContext.enumTypes.push(fqName);
|
|
1449
|
+
else if (elementData.$Kind === 'ComplexType') parserContext.complexTypes.push(fqName);
|
|
1450
|
+
else if (elementData.$Kind === 'TypeDefinition') parserContext.typeDefinitions.push(fqName);
|
|
1451
|
+
// bounded actions/functions
|
|
1452
|
+
else if (elementData instanceof Array) {
|
|
1453
|
+
let isAction = elementData[0].$Kind === 'Action' ? true : false;
|
|
1454
|
+
if (elementData[0].$IsBound) {
|
|
1455
|
+
_collectBoundedActionFunction(isAction, currNamespace, elementName, elementData, parserContext);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
// annotations
|
|
1459
|
+
else if (elementName === '$Annotations') {
|
|
1460
|
+
if (edmxAsJson.$Reference) {
|
|
1461
|
+
Object.entries(edmxAsJson.$Reference).forEach(ref => {
|
|
1462
|
+
if (ref[1].$Include[0].$Alias &&
|
|
1463
|
+
known_vocabularies.hasOwnProperty(ref[1].$Include[0].$Namespace)
|
|
1464
|
+
) {
|
|
1465
|
+
parserContext.vocabAliasToNamespace[ref[1].$Include[0].$Alias] = ref[1].$Include[0].$Namespace;
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
schemaData.$Annotations = _resolveAnotationTerm(JSON.stringify(schemaData.$Annotations), parserContext);
|
|
1470
|
+
/**
|
|
1471
|
+
* collect entity container annotations with the assumption that
|
|
1472
|
+
* we only have a single entity container
|
|
1473
|
+
*/
|
|
1474
|
+
if (currNamespace === parserContext.primarySchema) {
|
|
1475
|
+
Object.keys(schemaData.$Annotations).forEach(annotationTarget => {
|
|
1476
|
+
if (annotationTarget.includes(parserContext.entityContainerName)) {
|
|
1477
|
+
parserContext.entityContainerAnnotations[annotationTarget] = schemaData.$Annotations[annotationTarget];
|
|
1478
|
+
/**
|
|
1479
|
+
* since already captured in the parserContext, we will directly make
|
|
1480
|
+
* use of that itself. Hence delete from the schema annotaions here.
|
|
1481
|
+
*/
|
|
1482
|
+
delete schemaData.$Annotations[annotationTarget];
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1321
1489
|
}
|
|
1322
|
-
return JSON.parse(schemaDataAnnotation);
|
|
1323
1490
|
}
|
|
1324
1491
|
|
|
1325
|
-
function
|
|
1326
|
-
let
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
if (
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1492
|
+
function _preProcess(edmxAsJson, mockServerUc, parserContext) {
|
|
1493
|
+
let schemaDataList = [], indexOfSchema = 0;
|
|
1494
|
+
|
|
1495
|
+
Object.keys(edmxAsJson).forEach(schemaName => {
|
|
1496
|
+
let schemaData = edmxAsJson[schemaName];
|
|
1497
|
+
// collect the schema data
|
|
1498
|
+
if (ignoreObjects.indexOf(schemaName) === -1 && schemaData instanceof Object) {
|
|
1499
|
+
schemaData.$SchemaNamespace = schemaName;
|
|
1500
|
+
schemaDataList.push(schemaData);
|
|
1501
|
+
if (schemaData.$Alias) {
|
|
1502
|
+
parserContext.schemaAliasToNamespace[schemaData.$Alias] = schemaName;
|
|
1503
|
+
}
|
|
1504
|
+
parserContext.schemaToSchemaDataIndex[schemaName] = indexOfSchema;
|
|
1505
|
+
indexOfSchema++;
|
|
1339
1506
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
no_of_terms = true;
|
|
1507
|
+
// if mock server use case & traversing primary schema, collect relevant mappings
|
|
1508
|
+
if (mockServerUc && schemaName === parserContext.primarySchema) {
|
|
1509
|
+
let entityContainerData = schemaData[parserContext.entityContainerName];
|
|
1510
|
+
Object.keys(entityContainerData).forEach(elementName => {
|
|
1511
|
+
let elementObj = entityContainerData[elementName];
|
|
1512
|
+
let allowedObject = (ignoreObjects.indexOf(elementName) === -1) && (elementObj instanceof Object);
|
|
1513
|
+
// EntityType - EntitySet, Singleton mapping
|
|
1514
|
+
if (allowedObject && elementObj.$Type) {
|
|
1515
|
+
if (elementObj.$Collection) {
|
|
1516
|
+
parserContext.entityToEntitySetMap.set(entityContainerData[elementName].$Type, elementName);
|
|
1351
1517
|
}
|
|
1352
|
-
if (
|
|
1353
|
-
|
|
1354
|
-
controlFieldFlag = 1;
|
|
1518
|
+
if (elementObj.$Singleton) {
|
|
1519
|
+
parserContext.entityToSingletonMap.set(entityContainerData[elementName].$Type, elementName);
|
|
1355
1520
|
}
|
|
1356
|
-
if (annotation[1].hasOwnProperty('@Common.IsUpperCase')) {
|
|
1357
|
-
annotation[1]["@Common.IsUpperCase"] = true;
|
|
1358
|
-
}
|
|
1359
|
-
_checkAnnotationTarget(annotation, csnDefs, anno[0], no_of_terms, vocab, parserContext);
|
|
1360
1521
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1522
|
+
// Action - ActionImport mapping
|
|
1523
|
+
else if (allowedObject && elementObj.$Action) {
|
|
1524
|
+
parserContext.actionToActionImportMap.set(elementObj.$Action, elementName);
|
|
1525
|
+
}
|
|
1526
|
+
// Function - FunctionImport mapping
|
|
1527
|
+
else if (allowedObject && elementObj.$Function) {
|
|
1528
|
+
parserContext.functionToFunctionImportMap.set(elementObj.$Function, elementName);
|
|
1363
1529
|
}
|
|
1364
1530
|
});
|
|
1365
|
-
controlFieldFlag = 0;
|
|
1366
|
-
});
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
function _flattenAnnotationObject(annotationPrefix, annotationObject) {
|
|
1371
|
-
let flattenedResult = [];
|
|
1372
|
-
|
|
1373
|
-
Object.entries(annotationObject).forEach(annotation => {
|
|
1374
|
-
let currentResult = [];
|
|
1375
|
-
currentResult[0] = annotationPrefix + '.' + annotation[0];
|
|
1376
|
-
currentResult[1] = annotation[1];
|
|
1377
|
-
flattenedResult.push(currentResult);
|
|
1378
|
-
});
|
|
1379
|
-
|
|
1380
|
-
return flattenedResult;
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
function _removeTypeInAnnotation(annotationObject) {
|
|
1384
|
-
Object.keys(annotationObject).forEach(key => {
|
|
1385
|
-
if (key === "$Type") delete annotationObject[key];
|
|
1386
|
-
else if (annotationObject[key] instanceof Object) {
|
|
1387
|
-
_removeTypeInAnnotation(annotationObject[key]);
|
|
1388
1531
|
}
|
|
1389
1532
|
});
|
|
1390
|
-
}
|
|
1391
1533
|
|
|
1392
|
-
|
|
1393
|
-
let annotationResult = [annotationTargetPath, ''], currentResult = [], targetString;
|
|
1534
|
+
_collectSchemaElements(edmxAsJson, schemaDataList, parserContext);
|
|
1394
1535
|
|
|
1395
|
-
|
|
1536
|
+
return schemaDataList;
|
|
1537
|
+
}
|
|
1396
1538
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1539
|
+
function _generateCSN(edmxAsJson, ignorePersistenceSkip, mockServerUc, parserContext) {
|
|
1540
|
+
let csn = {};
|
|
1541
|
+
csn.meta = {};
|
|
1542
|
+
csn.meta.creator = 'cds-dk ' + versionInfo;
|
|
1543
|
+
csn.$version = '2.0';
|
|
1544
|
+
csn.definitions = {};
|
|
1545
|
+
let schemaDataList = _preProcess(edmxAsJson, mockServerUc, parserContext);
|
|
1546
|
+
|
|
1547
|
+
for (let schemaData of schemaDataList) {
|
|
1548
|
+
// Step 1: parse all type definition
|
|
1549
|
+
csn.definitions = Object.assign(_parseCSNForTypeDefinition(schemaData, parserContext), csn.definitions);
|
|
1550
|
+
// Step 2: parse all enum types
|
|
1551
|
+
csn.definitions = Object.assign(_parseCSNForEnumType(schemaData, parserContext), csn.definitions);
|
|
1552
|
+
// Step 3: parse all complex types
|
|
1553
|
+
csn.definitions = Object.assign(_parserCSNForComplexType(schemaData, mockServerUc, parserContext), csn.definitions);
|
|
1554
|
+
// Step 4: parse all entity type
|
|
1555
|
+
csn.definitions = Object.assign(_parseCSNForEntityType(schemaData, ignorePersistenceSkip, mockServerUc, parserContext), csn.definitions);
|
|
1556
|
+
// Step 5: parse all bounded action & function
|
|
1557
|
+
_parseBoundedActionFunction(schemaData, csn.definitions, 'action', mockServerUc, parserContext);
|
|
1558
|
+
_parseBoundedActionFunction(schemaData, csn.definitions, 'function', mockServerUc, parserContext);
|
|
1559
|
+
// Step 6: parse all unbounded action & function
|
|
1560
|
+
csn.definitions = Object.assign(_parseUnBoundedActionFunction(schemaData, 'action', mockServerUc, parserContext), csn.definitions);
|
|
1561
|
+
csn.definitions = Object.assign(_parseUnBoundedActionFunction(schemaData, 'function', mockServerUc, parserContext), csn.definitions);
|
|
1562
|
+
// Step 7: parse all annotations
|
|
1563
|
+
if (schemaData.$Annotations) {
|
|
1564
|
+
_parseAnnotations(schemaData.$Annotations, csn.definitions, parserContext);
|
|
1565
|
+
}
|
|
1409
1566
|
}
|
|
1410
|
-
else {
|
|
1411
|
-
Object.keys(currentAnnotation[1]).forEach(annoKey => {
|
|
1412
|
-
let currentAnnoValue = currentAnnotation[1][annoKey];
|
|
1413
|
-
// if the currentAnnoValue is an Object, we need to flatten it
|
|
1414
|
-
if (currentAnnoValue instanceof Object && !(currentAnnoValue instanceof Array)) {
|
|
1415
|
-
targetString = currentAnnotation[0] + '.' + annoKey;
|
|
1416
|
-
let currentResultList = _flattenAnnotationObject(targetString, currentAnnoValue);
|
|
1417
1567
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
currentResult[1] = currentAnnoValue;
|
|
1429
|
-
annotationResult[1] = currentResult;
|
|
1430
|
-
_checkAnnotationTarget(annotationResult, csnDefs, targetString, undefined, vocab, parserContext);
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1568
|
+
// add Service object and EntityContainer items only if it is mock server use case
|
|
1569
|
+
if (mockServerUc) {
|
|
1570
|
+
csn.definitions[parserContext.primarySchema] = {};
|
|
1571
|
+
csn.definitions[parserContext.primarySchema].kind = 'service';
|
|
1572
|
+
csn.definitions[parserContext.primarySchema]['@cds.external'] = true;
|
|
1573
|
+
|
|
1574
|
+
// Step 8: parse entity container contents and their annotations
|
|
1575
|
+
let schemaData = schemaDataList[parserContext.schemaToSchemaDataIndex[parserContext.primarySchema]];
|
|
1576
|
+
let entityContainerElements = schemaData[parserContext.entityContainerName];
|
|
1577
|
+
csn.definitions = Object.assign(_parseEntityContainer(csn.definitions, entityContainerElements, parserContext), csn.definitions);
|
|
1433
1578
|
}
|
|
1434
|
-
}
|
|
1435
1579
|
|
|
1436
|
-
|
|
1437
|
-
if (path) {
|
|
1438
|
-
path.doc = no_of_terms ? (path.doc += "\n\n") : (path.doc ? (path.doc += "") : (path.doc = ""));
|
|
1439
|
-
path.doc += _replaceSpecialCharacters(annotation[1]["@Core.LongDescription"]);
|
|
1440
|
-
return (path.doc);
|
|
1441
|
-
}
|
|
1580
|
+
return JSON.stringify(csn, null, 4);
|
|
1442
1581
|
}
|
|
1443
1582
|
|
|
1444
|
-
function _generateEDMX2JSON(edmx, name_space, parserContext) {
|
|
1583
|
+
function _generateEDMX2JSON(edmx, name_space, parserContext, context) {
|
|
1445
1584
|
return new Promise(function getJson(resolve, reject) {
|
|
1446
1585
|
try {
|
|
1447
1586
|
const inputBuffer = edmx;
|
|
@@ -1451,7 +1590,7 @@ function _generateEDMX2JSON(edmx, name_space, parserContext) {
|
|
|
1451
1590
|
if (missingReferencedDocuments?.length > 0) {
|
|
1452
1591
|
console.log(missingReferencedDocuments); // eslint-disable-line no-console
|
|
1453
1592
|
}
|
|
1454
|
-
if (_isValidJSON(json, parserContext)) {
|
|
1593
|
+
if (_isValidJSON(json, parserContext, context)) {
|
|
1455
1594
|
resolve(json);
|
|
1456
1595
|
}
|
|
1457
1596
|
});
|
|
@@ -1464,21 +1603,23 @@ function _generateEDMX2JSON(edmx, name_space, parserContext) {
|
|
|
1464
1603
|
async function getEdmxv4CSN(edmx, ignorePersistenceSkip, mockServerUc, context) {
|
|
1465
1604
|
let csn;
|
|
1466
1605
|
let edmjConverted;
|
|
1467
|
-
let name_space = []
|
|
1606
|
+
let name_space = [];
|
|
1468
1607
|
|
|
1469
1608
|
const parserContext = {};
|
|
1470
1609
|
_initialize(parserContext);
|
|
1471
1610
|
|
|
1472
|
-
if (context.include_all_namespaces
|
|
1611
|
+
if (context.include_all_namespaces) {
|
|
1473
1612
|
name_space.push(true);
|
|
1474
|
-
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1475
1615
|
name_space = context.namespaces;
|
|
1616
|
+
}
|
|
1476
1617
|
|
|
1477
1618
|
if (_isJson(edmx)) {
|
|
1478
1619
|
return new Promise(function getCsn(resolve, reject) {
|
|
1479
1620
|
try {
|
|
1480
1621
|
edmjConverted = JSON.parse(edmx);
|
|
1481
|
-
if (_isValidJSON(edmjConverted, parserContext)) {
|
|
1622
|
+
if (_isValidJSON(edmjConverted, parserContext, context)) {
|
|
1482
1623
|
csn = _generateCSN(edmjConverted, ignorePersistenceSkip, mockServerUc, parserContext);
|
|
1483
1624
|
if (csn) resolve(csn);
|
|
1484
1625
|
}
|
|
@@ -1487,7 +1628,8 @@ async function getEdmxv4CSN(edmx, ignorePersistenceSkip, mockServerUc, context)
|
|
|
1487
1628
|
}
|
|
1488
1629
|
});
|
|
1489
1630
|
}
|
|
1490
|
-
const edmj = await _generateEDMX2JSON(edmx, name_space, parserContext);
|
|
1631
|
+
const edmj = await _generateEDMX2JSON(edmx, name_space, parserContext, context);
|
|
1632
|
+
|
|
1491
1633
|
return _generateCSN(edmj, ignorePersistenceSkip, mockServerUc, parserContext);
|
|
1492
1634
|
}
|
|
1493
1635
|
|