@sap/cds-dk 6.7.2 → 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.
Files changed (122) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/bin/deploy/index.js +1 -1
  3. package/bin/import.js +5 -3
  4. package/bin/login.js +29 -9
  5. package/bin/watch.js +16 -0
  6. package/lib/bind/bindingUtil.js +2 -2
  7. package/lib/bind/userCdsrcJsonStorageProvider.js +1 -1
  8. package/lib/client/auth_manager.js +3 -1
  9. package/lib/client/helper/errors.js +6 -1
  10. package/lib/client/params.js +22 -20
  11. package/lib/client/settings_manager.js +17 -3
  12. package/lib/compile/openapi/csdl2openapi.js +2 -1
  13. package/lib/import/asyncapi/importAsyncAPI.js +32 -0
  14. package/lib/import/asyncapi/index.js +15 -0
  15. package/lib/import/asyncapi/parserContext.js +351 -0
  16. package/lib/import/asyncapi/utils.js +18 -0
  17. package/lib/import/common.js +21 -67
  18. package/lib/import/importapi.js +23 -2
  19. package/lib/import/message.js +11 -1
  20. package/lib/import/odata/index.js +14 -11
  21. package/lib/import/odata/metadataconverter/MetadataConverter.js +35 -35
  22. package/lib/import/odata/util/MultiMap.js +5 -5
  23. package/lib/import/odata/v2parser.js +1 -0
  24. package/lib/import/odata/v4parser.js +1281 -1139
  25. package/lib/init/constants.js +10 -3
  26. package/lib/init/template/_merging/registry-mta.js +33 -36
  27. package/lib/init/template/approuter/files/mtxs-configmap-java.yaml +20 -0
  28. package/lib/init/template/approuter/files/mtxs-configmap-nodejs.yaml +12 -0
  29. package/lib/init/template/approuter/index.js +24 -33
  30. package/lib/init/template/auditlog/files/package.json.hbs +27 -0
  31. package/lib/init/template/auditlog/index.js +12 -6
  32. package/lib/init/template/cf-manifest/index.js +1 -1
  33. package/lib/init/template/connectivity/index.js +1 -1
  34. package/lib/init/template/destinations/index.js +4 -2
  35. package/lib/init/template/enterprise-messaging/files/package.json.hbs +19 -0
  36. package/lib/init/template/enterprise-messaging/index.js +15 -13
  37. package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +31 -0
  38. package/lib/init/template/enterprise-messaging-shared/files/mta.yaml.hbs +15 -0
  39. package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +22 -0
  40. package/lib/init/template/enterprise-messaging-shared/files/values.yaml +7 -0
  41. package/lib/init/template/enterprise-messaging-shared/index.js +51 -0
  42. package/lib/init/template/extensibility/files/package.json.hbs +20 -0
  43. package/lib/init/template/extensibility/index.js +4 -16
  44. package/lib/init/template/file-based-messaging/files/package.json.hbs +19 -0
  45. package/lib/init/template/file-based-messaging/index.js +25 -0
  46. package/lib/init/template/hana/files/package.json.hbs +33 -0
  47. package/lib/init/template/hana/index.js +38 -65
  48. package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +0 -12
  49. package/lib/init/template/helm/chart/templates/_helpers.tpl +1 -121
  50. package/lib/init/template/helm/index.js +1 -1
  51. package/lib/init/template/html5-repo/index.js +1 -1
  52. package/lib/init/template/java/index.js +2 -2
  53. package/lib/init/template/kibana-logging/files/package.json.hbs +17 -0
  54. package/lib/init/template/kibana-logging/index.js +6 -19
  55. package/lib/init/template/lint/index.js +1 -1
  56. package/lib/init/template/mta/index.js +2 -2
  57. package/lib/init/template/multitenancy/files/mtxs-configmap-java.yaml +3 -5
  58. package/lib/init/template/multitenancy/files/mtxs-configmap-nodejs.yaml +3 -10
  59. package/lib/init/template/multitenancy/files/package.json.hbs +5 -4
  60. package/lib/init/template/multitenancy/index.js +22 -19
  61. package/lib/init/template/nodejs/files/.vscode/launch.json +11 -3
  62. package/lib/init/template/nodejs/index.js +1 -1
  63. package/lib/init/template/pipeline/index.js +1 -1
  64. package/lib/init/template/redis-messaging/files/mta.yaml.hbs +14 -0
  65. package/lib/init/template/redis-messaging/files/package.json.hbs +22 -0
  66. package/lib/init/template/redis-messaging/index.js +50 -0
  67. package/lib/init/template/samples/index.js +3 -3
  68. package/lib/init/template/sqlite/files/package.json.hbs +12 -4
  69. package/lib/init/template/sqlite/index.js +5 -1
  70. package/lib/init/template/toggles/files/package.json.hbs +20 -0
  71. package/lib/init/template/toggles/index.js +3 -9
  72. package/lib/init/template/xsuaa/files/package.json.hbs +28 -0
  73. package/lib/init/template/xsuaa/index.js +8 -23
  74. package/lib/init/util/{md5Tracking.js → md5.js} +1 -1
  75. package/lib/init/util/merge.js +4 -9
  76. package/lib/init/util/{mvnArchetypeUtil.js → mvn.js} +3 -0
  77. package/lib/init/util/projectReader.js +25 -107
  78. package/lib/init/util/xml.js +56 -0
  79. package/lib/lint/checks.js +1 -1
  80. package/lib/lint/io.js +1 -1
  81. package/lib/{init/util/commandUtil.js → util/command.js} +4 -2
  82. package/lib/{init/util/fsUtil.js → util/fs.js} +2 -2
  83. package/npm-shrinkwrap.json +17 -16
  84. package/package.json +5 -4
  85. package/lib/init/template/approuter/files/cds.cdsrc.json +0 -7
  86. package/lib/init/template/approuter/files/cds.cdsrc.json.hbs +0 -9
  87. package/lib/init/template/approuter/files/cds.package.json +0 -9
  88. package/lib/init/template/approuter/files/cds.package.json.hbs +0 -11
  89. package/lib/init/template/auditlog/files/dependencies.package.json +0 -5
  90. package/lib/init/template/enterprise-messaging/files/cds.package.json +0 -9
  91. package/lib/init/template/enterprise-messaging/files/cds.profile.package.json.hbs +0 -11
  92. package/lib/init/template/enterprise-messaging/files/cdsrc.json +0 -7
  93. package/lib/init/template/enterprise-messaging/files/cdsrc.json.hbs +0 -9
  94. package/lib/init/template/extensibility/files/cds.package.json +0 -7
  95. package/lib/init/template/extensibility/files/cds.package.json.hbs +0 -9
  96. package/lib/init/template/extensibility/files/cdsrc.json +0 -5
  97. package/lib/init/template/extensibility/files/cdsrc.json.hbs +0 -7
  98. package/lib/init/template/hana/files/java/cds.json.hbs +0 -7
  99. package/lib/init/template/hana/files/java/cds.profile.json.hbs +0 -12
  100. package/lib/init/template/hana/files/java/cds.shortcut.json.hbs +0 -5
  101. package/lib/init/template/hana/files/java/cds.shortcut.profile.json.hbs +0 -8
  102. package/lib/init/template/hana/files/nodejs/cds.json.hbs +0 -9
  103. package/lib/init/template/hana/files/nodejs/cds.profile.json.hbs +0 -14
  104. package/lib/init/template/hana/files/nodejs/cds.shortcut.json.hbs +0 -7
  105. package/lib/init/template/hana/files/nodejs/cds.shortcut.profile.json.hbs +0 -10
  106. package/lib/init/template/hana/files/nodejs/dependencies.package.json +0 -5
  107. package/lib/init/template/helm/chart/templates/_sapcp_helpers.tpl +0 -103
  108. package/lib/init/template/kibana-logging/files/cds.profile.package.json.hbs +0 -9
  109. package/lib/init/template/toggles/files/cds.package.json +0 -7
  110. package/lib/init/template/toggles/files/cds.package.json.hbs +0 -9
  111. package/lib/init/template/toggles/files/cdsrc.json +0 -5
  112. package/lib/init/template/toggles/files/cdsrc.json.hbs +0 -7
  113. package/lib/init/template/xsuaa/files/cds.cdsrc.json +0 -7
  114. package/lib/init/template/xsuaa/files/cds.cdsrc.json.hbs +0 -9
  115. package/lib/init/template/xsuaa/files/cds.package.json +0 -9
  116. package/lib/init/template/xsuaa/files/cds.package.json.hbs +0 -11
  117. package/lib/init/template/xsuaa/files/dependencies.package.json +0 -6
  118. package/lib/init/util/versionCompare.js +0 -73
  119. /package/lib/init/template/hana/files/{java/db → db}/package.json +0 -0
  120. /package/lib/init/template/hana/files/{common/src → src}/.hdiconfig +0 -0
  121. /package/lib/init/template/hana/files/{common/undeploy.json → undeploy.json} +0 -0
  122. /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 edmxncdsdatatype = {
17
- "Edm.String": "cds.LargeString",
18
- "Edm.Boolean": "cds.Boolean",
19
- "Edm.Int16": "cds.Integer",
20
- "Edm.Int32": "cds.Integer",
21
- "Edm.Int64": "cds.Integer64",
22
- "Edm.Decimal": "cds.Decimal",
23
- "Edm.DateTimeOffset": "cds.DateTime",
24
- "Edm.TimeOfDay": "cds.Time",
25
- "Edm.Date": "cds.Date",
26
- "Edm.Binary": "cds.LargeBinary",
27
- "Edm.Guid": "cds.UUID",
28
- "Edm.Double": "cds.Double",
29
- // Special handling of data types
30
- "Edm.String_m": "cds.String", // Max length
31
- "Edm.Decimal_p": "cds.Decimal", // Precision, scale
32
- //'Edm.DateTime_f': 'cds.Date', // sap:display-format="Date"
33
- //'Edm.DateTimeOffset_f': 'cds.Date', // sap:display-format="Date"
34
- "Edm.Binary_m": "cds.Binary", // Max Length
35
- "Edm.Byte_m": "cds.Integer", // Max Length
36
- "Edm.Double_p": "cds.Double", // Precision, scale
37
- // Special handling with annotations
38
- "Edm.Byte_a": "cds.Integer",
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 extendedPrimitiveTypes = [
46
- "Edm.Duration",
47
- "Edm.Geography",
48
- "Edm.GeographyPoint",
49
- "Edm.GeographyLineString",
50
- "Edm.GeographyPolygon",
51
- "Edm.GeographyMultiPoint",
52
- "Edm.GeographyMultiLineString",
53
- "Edm.GeographyMultiPolygon",
54
- "Edm.GeographyCollection",
55
- "Edm.Geometry",
56
- "Edm.GeometryPoint",
57
- "Edm.GeometryLineString",
58
- "Edm.GeometryPolygon",
59
- "Edm.GeometryMultiPoint",
60
- "Edm.GeometryMultiLineString",
61
- "Edm.GeometryMultiPolygon",
62
- "Edm.GeometryCollection"
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.namespace = "";
95
- parserContext.complexTypes = {};
96
- parserContext.enumTypes = {};
97
- parserContext.typeDefinitions = {};
98
- parserContext.jsonEntityContainerName = "";
99
- parserContext.entitySetToEntityTypeMap = new Multimap();
100
- parserContext.actionImportToActionMap = new Multimap();
101
- parserContext.functionImportToFunctionMap = new Multimap();
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 _isValidDataType(propertyType) {
116
- if (validEdmDatatypes.includes(propertyType)) {
117
- return true;
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 _isValidJSON(jsonObj, parserContext) {
123
- if (jsonObj.$Version && _hasEntityContainer(jsonObj, parserContext) && jsonObj[parserContext.namespace]) {
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
- let lastIndex = jsonObjEntityContainer.lastIndexOf('.');
137
- parserContext.namespace = jsonObjEntityContainer.substring(0, lastIndex);
138
- parserContext.jsonEntityContainerName = jsonObjEntityContainer.substring(lastIndex + 1);
172
+ [parserContext.primarySchema, parserContext.entityContainerName] = _extractSchemaNameAndElementName(jsonObjEntityContainer);
173
+ // required for `--keep-namespace` scenario
174
+ context.schemaNamespace = parserContext.primarySchema;
139
175
  }
140
- if (jsonObj[parserContext.namespace][parserContext.jsonEntityContainerName] &&
141
- jsonObj[parserContext.namespace][parserContext.jsonEntityContainerName] instanceof Object
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 _includeExternalNamespaceAnnotation(source, target) {
151
- Object.entries(source).forEach(element => {
152
- if (element[0].startsWith('@')) {
153
- target[element[0]] = element[1];
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 _addNotNull(property, isCollection) {
159
- if (isCollection) property.items["notNull"] = true;
160
- else property["notNull"] = true;
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 _typeWithCollections(property, type, isCollection) {
164
- if (isCollection) {
165
- let typeKeys = Object.keys(type);
166
- for (let i = 0; i < typeKeys.length; i++) {
167
- if (typeKeys[i].startsWith("@")) {
168
- property[typeKeys[i]] = type[typeKeys[i]];
169
- delete type[typeKeys[i]];
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 generatePropertyCsn(propertyType, isPrimaryKey) {
179
- let property = {};
180
- let typeProperties = {};
181
- if (isPrimaryKey) {
182
- property.key = true;
183
- // primary key cannot be of type Collection
184
- if (propertyType.$Collection)
185
- throw new Error(messages.COLLECTION_IN_KEY);
186
- if (propertyType.$Nullable) {
187
- console.log(warn("Expected key element to be not nullable"));
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
- else {
192
- if ((!propertyType.$Nullable) && (propertyType.$Kind !== 'TypeDefinition')) {
193
- typeProperties["notNull"] = true;
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
- _includeExternalNamespaceAnnotation(propertyType, property);
197
- // conditions have to be made
198
- if (propertyType.$MaxLength) {
199
- if (propertyType.$Type) {
200
- if (propertyType.$Type == "Edm.Binary" && propertyType.$MaxLength >= 5000) {
201
- console.log(warn("MaxLength for type Edm.Binary should not exceed 5000"));
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, "&quot;").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
- typeProperties.length = propertyType.$MaxLength;
208
- } else if (_isValidDataType(propertyType.$Type)) {
209
- if (propertyType.$Precision && propertyType.$Precision > 0) {
210
- typeProperties.type = edmxncdsdatatype[propertyType.$Type + '_a'];
211
- typeProperties["@odata.Precision"] = propertyType.$Precision;
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
- typeProperties.type = edmxncdsdatatype[propertyType.$Type];
214
- // typeProperties["@odata.Precision"] = 0;
344
+ currentResult[0] = annotationPrefix + '.' + annotation[0];
345
+ currentResult[1] = annotation[1];
215
346
  }
216
- } else if (propertyType.$Precision && !_isValidDataType(propertyType.$Type)) {
217
- typeProperties.type = edmxncdsdatatype[propertyType.$Type + '_p'];
218
- if (propertyType.$Scale && !(propertyType.$Scale == 'variable' || propertyType.$Scale == 'floating')) {
219
- typeProperties.scale = propertyType.$Scale;
220
- typeProperties.precision = propertyType.$Precision;
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
- } else if (edmxncdsdatatype[propertyType.$Type + "_a"]) {
223
- typeProperties.type = edmxncdsdatatype[propertyType.$Type + "_a"];
224
- } else if (propertyType.$Type) {
225
- if (edmxncdsdatatype[propertyType.$Type]) {
226
- typeProperties.type = edmxncdsdatatype[propertyType.$Type];
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
- else {
229
- typeProperties.type = propertyType.$Type;
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
- if (edmxncdsdatatype[propertyType.$Type + '_a']) {
236
- if (edmxncdsdatatype[propertyType.$Type + '_a'] === 'cds.LargeBinary') {
237
- typeProperties["@Core.MediaType"] = 'application/octet-stream';
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
- else {
240
- typeProperties["@odata.Type"] = propertyType.$Type;
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
- _typeWithCollections(property, typeProperties, propertyType.$Collection);
490
+ _captureAnnotationWithinElement(propertyObject, dataTypeObj, parserContext);
244
491
 
245
- if (propertyType.$DefaultValue) {
246
- if (propertyType.$Collection) console.log(warn("Type with Collection cannot have DefaultValue"));
247
- _addDefaultValueObject(property, propertyType.$DefaultValue);
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
- return property;
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
- function _generateComplexTypePropertyCsn(propertyType) {
254
- let property = {};
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
- if (propertyType.$DefaultValue && !propertyType.$Collection) {
260
- _addDefaultValueObject(property, propertyType.$DefaultValue);
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 _generateTypeDefinitionPropertyCsn(propertyType) {
267
- let property = {};
268
- let type = { type: propertyType.$Type };
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
- _includeExternalNamespaceAnnotation(propertyType, property);
271
- _typeWithCollections(property, type, propertyType.$Collection);
272
- if (!propertyType.$Nullable) _addNotNull(property, propertyType.$Collection);
273
- // Verify: Can DefaultValue be present here?
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 property;
594
+ return dataTypeObj;
276
595
  }
277
596
 
278
597
  function _parseAssociationProperty(foreignReference, associationNode, mockServerUc, parserContext) {
279
- let associationProperty = {};
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
- /*if(associationProperty.$Nullable != true){
293
- associationProperty["notNull"] = true;
294
- }*/
295
- if (mockServerUc && parserContext.entitySetToEntityTypeMap && parserContext.entitySetToEntityTypeMap.get(associationNode.$Type)) {
296
- if (parserContext.entitySetToEntityTypeMap.get(associationNode.$Type).length > 1) {
297
- for (let j = 0; j < parserContext.entitySetToEntityTypeMap.get(associationNode.$Type).length; j++) {
298
- if (parserContext.entitySetToEntityTypeMap.get(associationNode.$Type)[j] === foreignReference) {
299
- associationProperty.target = parserContext.namespace + '.' +
300
- parserContext.entitySetToEntityTypeMap.get(associationNode.$Type)[j];
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.namespace + '.' + parserContext.entitySetToEntityTypeMap.get(associationNode.$Type);
637
+ associationProperty.target = parserContext.primarySchema + '.' + entityTypeMapping;
305
638
  }
306
639
  } else {
307
- associationProperty.target = associationNode.$Type;
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
- for (i = 0; i < foreignKeys.length; i++) {
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
- _includeExternalNamespaceAnnotation(associationNode, associationProperty);
677
+ _captureAnnotationWithinElement(associationNode, associationProperty, parserContext);
678
+
344
679
  return associationProperty;
345
680
  }
346
681
 
347
- function _getBaseTypeEntityName(baseType, parserContext) {
348
- const mappedResult = parserContext.entitySetToEntityTypeMap.get(baseType);
349
- // if one entity type has mapping to multiple entity sets, use the first entity set
350
- if (mappedResult.length > 1) {
351
- return parserContext.namespace + '.' + mappedResult[0];
352
- } else {
353
- return parserContext.namespace + '.' + mappedResult;
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
- function _generateCSNForEntityType(ignorePersistenceSkip, mockServerUc, parserContext) {
367
- let entityCsn = {};
368
- let keySet;
369
- let curKey;
370
- let j;
371
- let propertyType;
372
- let enumType = {};
373
- const entityTypeNames = parserContext.entityTypes.map(elementObject => {
374
- return Object.keys(elementObject)[0];
375
- });
376
-
377
- parserContext.entityTypes.forEach(element => {
378
- let key = Object.keys(element)[0];
379
- let baseType = element[key].$BaseType;
380
- let isAbstract = element[key].$Abstract;
381
- let isOpenType = element[key].$OpenType;
382
- if (key) {
383
- entityCsn[key] = {};
384
- entityCsn[key].kind = 'entity';
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 (!parserContext.entityTypeOpenEntries.includes(baseType)) parserContext.entityTypeOpenEntries.push(baseType);
410
- }
411
- if ((isAbstract || isOpenType) && !parserContext.entityTypeOpenEntries.includes(key)) {
412
- parserContext.entityTypeOpenEntries.push(key);
413
- }
414
- entityCsn[key].elements = {};
415
- keySet = [];
416
- Object.keys(element[key]).forEach(property => {
417
- // Annotations names can have either @ or .; Don't add in generated CSN as property.
418
- // CDS compiler will throw error when property name has special characters
419
- if ((property.indexOf('@') == -1) && (property.indexOf('.') == -1)) {
420
- if (element[key][property] instanceof Object) {
421
- if (property === '$Key') {
422
- curKey = element[key].$Key;
423
- for (j = 0; j < curKey.length; j++) {
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
- if (element[key].$HasStream) {
460
- entityCsn[key].elements = { ...entityCsn[key].elements, ..._addBlobElement() }
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
- // add @open annotations
466
- parserContext.entityTypeOpenEntries.forEach(key => {
467
- if (entityCsn[key]) entityCsn[key]['@open'] = true;
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
- return entityCsn;
471
- }
846
+ csnEntity[entityName] = {};
847
+ csnEntity[entityName].kind = 'entity';
848
+ csnEntity[entityName]['@cds.external'] = true;
472
849
 
473
- function _generateEntityType(schemaData, mockServerUc, parserContext) {
474
- let entity = {};
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
- function _parseEntityType(schemaDataList, ignorePersistenceSkip, mockServerUc, parserContext) {
523
- // collect entity types
524
- let schemaData;
525
- for (let i = 0; i < schemaDataList.length; i++) {
526
- schemaData = schemaDataList[i];
527
- _generateEntityType(schemaData, mockServerUc, parserContext);
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
- function _parseEntityContainer(entityCSN, schemaData, parserContext) {
533
- Object.entries(schemaData[0][parserContext.jsonEntityContainerName]).forEach(entry => {
534
- if (entityCSN[parserContext.namespace + '.' + entry[0]])
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
- return entityCSN;
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
- return enumTypeDict;
870
+
871
+ return csnEntity;
557
872
  }
558
873
 
559
- function _collectEnumType(schemaDataList, parserContext) {
560
- let schemaData;
561
- for (let i = 0; i < schemaDataList.length; i++) {
562
- schemaData = schemaDataList[i];
563
- parserContext.enumTypes = _generateEnumType(schemaData, parserContext);
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
- return parserContext.enumTypes;
886
+
887
+ return _generateCSNForEntityType(ignorePersistenceSkip, mockServerUc, entitiesList, parserContext);
566
888
  }
567
889
 
568
- function _generateCSNForEnumType(schemaData, parserContext) {
569
- const nonMemberList = ["$Kind", "$Type", "$UnderlyingType", "$IsFlags"];
570
- let enumCsn = {};
571
- let eTypes;
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
- if (!enumCsn[element].type || enumCsn[element].type !== "cds.Integer" ) {
585
- throw new Error(messages.INVALID_ENUM_TYPE + `'${underlyingType}'`);
895
+ // primitive type
896
+ if (isPrimitiveType) {
897
+ if (edmPrimitiveTypes.includes(dataType)) {
898
+ return _generatePrimitiveDataTypeCsn(elementObject, !isPrimaryKey, parserContext);
586
899
  }
587
- Object.keys(eTypes[element]).forEach(member => {
588
- if (!nonMemberList.includes(member)) {
589
- enumCsn[element].enum[member] = {};
590
- enumCsn[element].enum[member].val = eTypes[element][member];
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
- return enumCsn;
940
+ }
941
+ }
942
+ if (returnType) {
943
+ csnObj.returns = _parseParameterAndReturnTypeContent(returnType, mockServerUc, parserContext);
944
+ }
945
+
946
+ return csnObj;
595
947
  }
596
948
 
597
- function _generateTypeDefinition(schemaData, parserContext) {
598
- let typeDefDict = {};
599
- let temp;
600
- Object.keys(schemaData).forEach(curTypeDef => {
601
- if (schemaData[curTypeDef] instanceof Object) {
602
- if (schemaData[curTypeDef].$Kind && schemaData[curTypeDef].$Kind === 'TypeDefinition') {
603
- temp = parserContext.namespace + '.' + curTypeDef;
604
- typeDefDict[temp] = schemaData[curTypeDef];
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 _collectTypeDefinition(schemaDataList, parserContext) {
612
- let schemaData;
613
- for (let i = 0; i < schemaDataList.length; i++) {
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
- function _generateBounded(schemaData, flag, parserContext) {
620
- let bFunction = {};
621
- let boundedFunction = [];
622
- let temp;
623
- let kindString;
624
- if (flag == 1) {
625
- kindString = 'Function';
626
- }
627
- else {
628
- kindString = 'Action';
629
- }
630
- Object.keys(schemaData).forEach(curFunction => {
631
- if (schemaData[curFunction] instanceof Array &&
632
- schemaData[curFunction][0].$Kind === kindString &&
633
- schemaData[curFunction][0].$IsBound === true
634
- ) {
635
- boundedFunction = [];
636
- temp = parserContext.namespace + '.' + curFunction;
637
- for (let i = 0; i < schemaData[curFunction].length; i++) {
638
- boundedFunction.push(schemaData[curFunction][i]);
639
- bFunction[temp] = boundedFunction;
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
- return bFunction;
999
+
1000
+ return csnObj;
644
1001
  }
645
1002
 
646
- function _captureOptionalParameter(annotation, path) {
647
- let param = annotation[0].split('/');
648
- let defaultvalue = annotation[1]["@Core.OptionalParameter"]["DefaultValue"];
649
- if (path.params === undefined) {
650
- _addDefaultValueObject(path, defaultvalue);
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
- else {
653
- _addDefaultValueObject(path.params[param[1]], defaultvalue);
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
- return path;
1069
+
1070
+ return enumCsn;
656
1071
  }
657
1072
 
658
- function _addDefaultValueObject(path, defaultvalue) {
659
- // for collection, default value isin't expected
660
- if (path.items === undefined) {
661
- path.default = {};
662
- path.default.val = (defaultvalue) ? defaultvalue : null;
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 _collectBounded(schemaDataList, flag, parserContext) {
667
- let schemaData;
668
- for (let i = 0; i < schemaDataList.length; i++) {
669
- schemaData = schemaDataList[i];
670
- if (flag == 0) {
671
- parserContext.boundedActions = _generateBounded(schemaData, 0, parserContext);
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
- parserContext.boundedFunctions = _generateBounded(schemaData, 1, parserContext);
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 _getDatatype(datatype, parserContext) {
680
- let type = undefined;
681
- if (datatype === undefined) {
682
- type = "cds.LargeString"; // intentionally used LargeString
683
- } else if (parserContext.entitySetToEntityTypeMap.get(datatype) && parserContext.entitySetToEntityTypeMap.get(datatype)[0]) {
684
- // mapping to entity set if type is entity type
685
- type = parserContext.namespace + "." + parserContext.entitySetToEntityTypeMap.get(datatype)[0];
686
- } else if (parserContext.complexTypes[datatype] || parserContext.typeDefinitions[datatype] || parserContext.enumTypes[datatype]) {
687
- // if type is complex type, type definition or an enum type
688
- type = datatype;
689
- } else {
690
- // checking if type is an entity set
691
- for (const element of parserContext.entitySetToEntityTypeMap.keys()) {
692
- if (parserContext.namespace + "." + parserContext.entitySetToEntityTypeMap.get(element) === datatype)
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
- // will return undefined if type is not supported
702
- return type;
703
- }
1147
+ if (bindingParameter.includes(',')) {
1148
+ bindingParameter = bindingParameter.split(',')[0];
1149
+ }
1150
+ const entityType = _resolveAliasedElementName(bindingParameter, parserContext);
704
1151
 
705
- function _parseBounded(csnDefs, flag, parserContext) {
706
- let boundedEdmxKind, boundedObj, boundedCsnKind, boundedEntityName, obj, paramName, paramObj, name;
707
- if (flag == 0) {
708
- boundedEdmxKind = "Action";
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 _generateComplexType(schemaData, parserContext) {
856
- let complexTypeDict = {};
857
- let temp;
858
- Object.keys(schemaData).forEach(curComplexType => {
859
- if (schemaData[curComplexType] instanceof Object) {
860
- if (schemaData[curComplexType].$Kind && schemaData[curComplexType].$Kind === 'ComplexType') {
861
- temp = parserContext.namespace + '.' + curComplexType;
862
- complexTypeDict[temp] = schemaData[curComplexType];
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
- return complexTypeDict;
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 _collectComplexType(schemaDataList, parserContext) {
870
- // collect complex types
871
- let schemaData;
872
- for (let i = 0; i < schemaDataList.length; i++) {
873
- schemaData = schemaDataList[i];
874
- parserContext.complexTypes = _generateComplexType(schemaData, parserContext);
875
- }
876
- return parserContext.complexTypes;
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 _generateCSNForComplexType(schemaData, parserContext) {
880
- let complexCsn = {};
881
- let cTypes;
882
- let propertyType;
883
- let baseType, isAbstract, isOpenType;
884
- let enumType;
885
- cTypes = _collectComplexType(schemaData, parserContext);
886
- Object.keys(cTypes).forEach(element => {
887
- complexCsn[element] = {};
888
- complexCsn[element].kind = 'type';
889
- complexCsn[element]['@cds.external'] = true;
890
- complexCsn[element].elements = {};
891
- // inheritance handling
892
- baseType = cTypes[element].$BaseType;
893
- isAbstract = cTypes[element].$Abstract;
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
- throw new Error(messages.UNRESOLVED_TYPE + `'${baseType}' in element: '${element}'.`);
1237
+ definitionRefName = entityContainerElements[ecElement].$Type;
1238
+ kind = 'entity';
903
1239
  }
904
- }
905
- if ((isAbstract || isOpenType) && !parserContext.complexTypeOpenEntries.includes(element)) {
906
- parserContext.complexTypeOpenEntries.push(element);
907
- }
908
- Object.keys(cTypes[element]).forEach(property => { // cTypes[element][property].$Type.startsWith('Edm.')
909
- if (cTypes[element][property] instanceof Object) {
910
- if (!cTypes[element][property].$Type ||
911
- edmxncdsdatatype[cTypes[element][property].$Type] ||
912
- edmxncdsdatatype[cTypes[element][property].$Type + "_a"]
913
- ) {
914
- propertyType = cTypes[element][property];
915
- complexCsn[element].elements[property] = generatePropertyCsn(propertyType, false);
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
- // add @open annotations
937
- parserContext.complexTypeOpenEntries.forEach(key => {
938
- if (complexCsn[key]) complexCsn[key]['@open'] = true;
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
- return complexCsn;
942
- }
943
-
944
- function _parseTypeDefinition(parserContext) {
945
- let typeDef = {};
946
- let propertyType;
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
- function _parseUnbounded(schemaData, flag, parserContext) {
967
- let unboundedObj = {}, unbounded, unboundedCsnKind;
968
- let importToMap = new Multimap();
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
- return unboundedObj;
1286
+
1287
+ return csnDefs;
1100
1288
  }
1101
1289
 
1102
- function _generateCSN(edmxAsJson, ignorePersistenceSkip, mockServerUc, parserContext) {
1103
- let csn = {};
1104
- let schemaData = [];
1105
- csn.meta = {};
1106
- csn.meta.creator = 'cds-dk ' + versionInfo;
1107
- csn.$version = '2.0';
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
- Object.keys(edmxAsJson).forEach(data => {
1111
- if (ignoreObjects.indexOf(data) === -1 && edmxAsJson[data] instanceof Object) {
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
- if (mockServerUc) {
1120
- let entitySetData = edmxAsJson[parserContext.namespace];
1121
- let entityContainer;
1122
- Object.keys(entitySetData).forEach(data => {
1123
- if (ignoreObjects.indexOf(data) === -1 && entitySetData[data] instanceof Object) {
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
- csn.definitions[parserContext.namespace] = {};
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, "&quot;").trim();
1310
+ return uniqueParams;
1178
1311
  }
1179
1312
 
1180
- function _checkAnnotatationTerm(annotation, path, no_of_terms, targetString, vocab) {
1181
- let annoTerm = targetString.substring(targetString.indexOf('@') + 1).split('.')[0];
1182
- if (path) {
1183
- if (targetString === "@Core.Description" && annotation[1]["@Core.Description"]) {
1184
- if (path.doc === undefined)
1185
- path.doc = _replaceSpecialCharacters(annotation[1]["@Core.Description"]);
1186
- else
1187
- path.doc += _replaceSpecialCharacters(annotation[1]["@Core.Description"]);
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
- else if (targetString === "@Core.LongDescription" && annotation[1]["@Core.LongDescription"]) {
1190
- _generateLongDescription(annotation, path, no_of_terms);
1338
+ }
1339
+ else {
1340
+ if (dataList.length > 1) {
1341
+ actionFunctionObject['$Parameter'] = _collectAllParameters(dataList);
1342
+ parserContext.unboundedFunctions[fqName] = actionFunctionObject;
1191
1343
  }
1192
- else if (targetString === "@Core.OptionalParameter" && annotation[1]["@Core.OptionalParameter"]) {
1193
- _captureOptionalParameter(annotation, path);
1344
+ else {
1345
+ parserContext.unboundedFunctions[fqName] = actionFunctionObject;
1194
1346
  }
1195
- else if (vocab) {
1196
- if (vocab.includes(annoTerm)) {
1197
- if (annoTerm === "Capabilities") {
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 checkEntitySetMap(pathName, parserContext, index = 0) {
1215
- if (parserContext.entitySetToEntityTypeMap.get(pathName) != null) {
1216
- pathName = parserContext.namespace + '.' + parserContext.entitySetToEntityTypeMap.get(pathName)[index];
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
- function _checkAnnotationTarget(annotation, csnDefs, str, no_of_terms, vocab, parserContext) {
1222
- if ((annotation[0].indexOf('/') === -1 && annotation[0].indexOf('(') === -1) || annotation[0].includes("Container")) {
1223
- if (csnDefs[annotation[0]]) {
1224
- let path = annotation[0].split('/');
1225
- let pathFromEntityTypeMap = parserContext.entitySetToEntityTypeMap.get(path[0]);
1226
- if (pathFromEntityTypeMap != null) {
1227
- if (pathFromEntityTypeMap.length > 1) {
1228
- for (let index = 0; index < pathFromEntityTypeMap.length; index++) {
1229
- let path1 = checkEntitySetMap(path[0], parserContext, index);
1230
- _checkAnnotatationTerm(annotation, csnDefs[path1], no_of_terms, str, vocab);
1231
- }
1232
- } else {
1233
- _checkAnnotatationTerm(annotation, csnDefs[annotation[0]], no_of_terms, str, vocab);
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
- if (annotation[0].indexOf('/') > 0) {
1242
- let path = annotation[0].split('/');
1243
- let temp = parserContext.namespace + '.' + path[1];
1244
- let path1 = checkEntitySetMap(temp, parserContext);
1245
- if (csnDefs[path1]) {
1246
- _checkAnnotatationTerm(annotation, csnDefs[path1], no_of_terms, str, vocab);
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
- else if (annotation[0].indexOf('(') === -1 && (annotation[0].indexOf('/') === -1)) {
1255
- let path = annotation[0].split('/');
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
- else if (annotation[0].indexOf('(') === -1 && !(annotation[0].indexOf('/') === -1)) {
1268
- let path = annotation[0].split('/');
1269
- let pathFromEntityTypeMap = parserContext.entitySetToEntityTypeMap.get(path[0]);
1270
- if (pathFromEntityTypeMap != null) {
1271
- if (pathFromEntityTypeMap.length > 1) {
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
- let path1 = checkEntitySetMap(path[0], parserContext);
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 (csnDefs[path[0]]) {
1286
- _checkAnnotatationTerm(annotation, csnDefs[path[0]]["elements"][path[1]], no_of_terms, str, vocab);
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
- else {
1291
- let path = annotation[0].split('(');
1292
- let actualPath = checkEntitySetMap(path[0], parserContext);
1293
- if (csnDefs[actualPath] === undefined) {
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
- } else {
1301
- if (path[1].indexOf('/') === -1) {
1302
- _checkAnnotatationTerm(annotation, csnDefs[path[0]], no_of_terms, str, vocab);
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
- function _replaceSchemaDataAnnotation(vocabAlias, vocabNamespce, schemaDataAnnotation) {
1314
- for (let v in vocabAlias) {
1315
- if (schemaDataAnnotation.includes(vocabAlias[v])) {
1316
- schemaDataAnnotation = schemaDataAnnotation.split(vocabAlias[v]).join(known_vocabularies[vocabNamespce[v]]);
1317
- }
1318
- if (schemaDataAnnotation.includes(vocabNamespce[v])) {
1319
- schemaDataAnnotation = schemaDataAnnotation.split(vocabNamespce[v]).join(known_vocabularies[vocabNamespce[v]]);
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 _generateDocumentation(edmxAsJson, schemaData, csnDefs, parserContext) {
1326
- let vocabAlias = [];
1327
- let vocab = [];
1328
- let vocabNamespce = [];
1329
- let controlFieldFlag = 0;
1330
- if (schemaData[0].$Annotations) {
1331
- if (edmxAsJson.$Reference) {
1332
- Object.entries(edmxAsJson.$Reference).forEach(a => {
1333
- if (known_vocabularies.hasOwnProperty(a[1].$Include[0].$Namespace)) {
1334
- vocabAlias.push(a[1].$Include[0].$Alias);
1335
- vocabNamespce.push(a[1].$Include[0].$Namespace);
1336
- vocab.push(known_vocabularies[a[1].$Include[0].$Namespace])
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
- schemaData[0].$Annotations = _replaceSchemaDataAnnotation(vocabAlias, vocabNamespce, JSON.stringify(schemaData[0].$Annotations));
1342
-
1343
- Object.entries(schemaData[0].$Annotations).map(annotation => {
1344
- let no_of_terms;
1345
- Object.entries(annotation[1]).forEach(anno => {
1346
- let annoTerm = anno[0].substring(anno[0].indexOf('@') + 1).split('.')[0];
1347
-
1348
- if (vocab.includes(annoTerm) && annoTerm !== 'Capabilities') {
1349
- if (annotation[1].hasOwnProperty('@Core.Description')) {
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 (annotation[1].hasOwnProperty('@Common.FieldControl') && controlFieldFlag === 0) {
1353
- annotation[1]["@Common.FieldControl"] = { "#": annotation[1]["@Common.FieldControl"] };
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
- if (vocab.includes(annoTerm) && annoTerm === 'Capabilities') {
1362
- _parseCapabilitiesAnnotation(annotation[0], anno, csnDefs, vocab, parserContext);
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
- function _parseCapabilitiesAnnotation(annotationTargetPath, currentAnnotation, csnDefs, vocab, parserContext) {
1393
- let annotationResult = [annotationTargetPath, ''], currentResult = [], targetString;
1534
+ _collectSchemaElements(edmxAsJson, schemaDataList, parserContext);
1394
1535
 
1395
- _removeTypeInAnnotation(currentAnnotation);
1536
+ return schemaDataList;
1537
+ }
1396
1538
 
1397
- /**
1398
- * For entries like:
1399
- * <Annotation Term="Capabilities.FilterFunctions"> <Collection> <String>eq</String> <String>ne</String> </Collection> </Annotation>
1400
- * <Annotation Term="Capabilities.DeleteRestrictions.Deletable" Bool="true"/>
1401
- *
1402
- * simply preserve the value as it is
1403
- */
1404
- if (currentAnnotation[1] instanceof Array || !(currentAnnotation[1] instanceof Object)) {
1405
- targetString = currentAnnotation[0];
1406
- currentResult = [targetString, currentAnnotation[1]];
1407
- annotationResult[1] = currentResult;
1408
- _checkAnnotationTarget(annotationResult, csnDefs, targetString, undefined, vocab, parserContext);
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
- for (let idx = 0; idx < currentResultList.length; idx++) {
1419
- targetString = currentResultList[idx][0];
1420
- annotationResult[1] = currentResultList[idx];
1421
- _checkAnnotationTarget(annotationResult, csnDefs, targetString, undefined, vocab, parserContext);
1422
- }
1423
- }
1424
- // if the currentAnnoValue is a list or direct value, we preserve as it is
1425
- else {
1426
- targetString = currentAnnotation[0] + '.' + annoKey;
1427
- currentResult[0] = targetString;
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
- function _generateLongDescription(annotation, path, no_of_terms) {
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 === true)
1611
+ if (context.include_all_namespaces) {
1473
1612
  name_space.push(true);
1474
- else
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