@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
@@ -0,0 +1,351 @@
1
+ const { typeRegex } = require('./utils');
2
+ const messages = require("../message").getMessages();
3
+
4
+ module.exports = class ParserContext {
5
+ /**
6
+ * @constructor
7
+ */
8
+ constructor(content) {
9
+ this.services = this.getServicesFromContent(content);
10
+ }
11
+
12
+ /**
13
+ * Returns services from the asyncapi json object along with the events and annotations.
14
+ * @returns an object with all the services.
15
+ */
16
+ getServicesFromContent(content) {
17
+ const services = {};
18
+ const serviceAnnotations = this.getServiceAnnotations(content);
19
+
20
+ Object.entries(content.components.messages).forEach(([key, value]) => {
21
+ // uses regex to break event name in different components
22
+ const regexGroups = typeRegex.exec(key);
23
+ if (regexGroups == null) {
24
+ throw new Error(messages.INVALID_MESSAGE_NAME);
25
+ }
26
+
27
+ const service = regexGroups[1] + "." + regexGroups[2];
28
+ const event = regexGroups[0];
29
+
30
+ // add service to services entry if not present
31
+ if (!(service in services)) {
32
+ services[service] = { 'kind': 'service', ...serviceAnnotations, 'events': {}, 'types': {} };
33
+ }
34
+
35
+ // add event entry to service
36
+ const eventAnnotations = this.getEventAnnotations(value);
37
+
38
+ let properties = {};
39
+ let types = {};
40
+
41
+ if (value.payload) {
42
+ // gets the schema being referred to in the event
43
+ const eventSchema = this.resolveRef(content, value.payload);
44
+ [properties, types] = this.getPropertiesAndTypes(event, eventSchema);
45
+ }
46
+ services[service].types = types;
47
+ services[service].events[event] = { 'kind': 'event', ...eventAnnotations, elements: properties };
48
+ });
49
+
50
+ return services;
51
+ }
52
+
53
+ /**
54
+ * Returns annotations of a particular service.
55
+ * @returns an object with all the annotations as key value pairs.
56
+ */
57
+ getServiceAnnotations(content) {
58
+ const serviceAnnotationsMapping = {
59
+ 'x-sap-stateInfo': 'StateInfo', //expected: object
60
+ 'x-sap-shortText': 'ShortText' //expected: string
61
+ };
62
+
63
+ const infoAnnotationsMapping = {
64
+ 'title': 'Title', //expected: string
65
+ 'version': 'SchemaVersion', //expected: string
66
+ 'description': 'Description' //expected: string
67
+ };
68
+
69
+ const annotations = this.getAnnotations(content, serviceAnnotationsMapping);
70
+
71
+ if (content.info) {
72
+ for (const [key, value] of Object.entries(infoAnnotationsMapping)) {
73
+ if (content.info[key] && !(content.info[key] instanceof Object)) {
74
+ // info values will always be in string format
75
+ annotations['@AsyncAPI.' + value] = content.info[key];
76
+ }
77
+ }
78
+ }
79
+
80
+ return annotations;
81
+ }
82
+
83
+ /**
84
+ * Returns annotations of a particular event. Here the parameter content is event content.
85
+ * @returns an object with all the annotations as key value pairs.
86
+ */
87
+ getEventAnnotations(content) {
88
+ const eventLevelAnnotationsMapping = {
89
+ 'x-sap-event-spec-version': 'EventSpecVersion', //expected: string
90
+ 'x-sap-event-source': 'EventSource', //expected: string
91
+ 'x-sap-event-source-parameters': 'EventSourceParams', //expected: object
92
+ 'x-sap-event-characteristics': 'EventCharacteristics', //expected: object
93
+ 'x-sap-stateInfo': 'EventStateInfo', //expected: object
94
+ 'x-sap-event-version': 'EventSchemaVersion' //expected: string
95
+ };
96
+
97
+ const annotations = this.getAnnotations(content, eventLevelAnnotationsMapping);
98
+
99
+ return annotations;
100
+ }
101
+
102
+ /**
103
+ * Returns validity of the annotation value. Here the parameter
104
+ * content is the annotation name and value of the annotation.
105
+ * @returns true/false depending on the annotation value.
106
+ */
107
+ isValidAnnotationValue(annotation, value) {
108
+ const objectTypeAnnotations = ['StateInfo', 'EventSourceParams', 'EventCharacteristics', 'EventStateInfo'];
109
+ // if the value is an array or null
110
+ if (Array.isArray(value) || value === null) return false;
111
+
112
+ if (objectTypeAnnotations.includes(annotation) && (value instanceof Object)) {
113
+ return true;
114
+ } else if ((!objectTypeAnnotations.includes(annotation)) && (typeof value === 'string')) {
115
+ return true;
116
+ } else {
117
+ console.warn(`WARNING: Annotations which are not following the expected type are omitted.`);
118
+ return false;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Returns annotations for an event/service. Here the parameter content
124
+ * is event/service content along with the corresponding mapping.
125
+ * @returns an object with the annotations as key value pairs.
126
+ */
127
+ getAnnotations(content, annotationsMapping) {
128
+ const annotations = {};
129
+
130
+ for (const [key, value] of Object.entries(content)) {
131
+ // case: known annotation
132
+ if (annotationsMapping[key] && this.isValidAnnotationValue(annotationsMapping[key], value)) {
133
+ annotations['@AsyncAPI.' + annotationsMapping[key]] = value;
134
+ }
135
+ // case: unkown annotation
136
+ else if (key.startsWith('x-sap-')) {
137
+ if (!annotations['@AsyncAPI.Extensions']) {
138
+ annotations['@AsyncAPI.Extensions'] = {};
139
+ }
140
+ annotations['@AsyncAPI.Extensions'][key] = value;
141
+ }
142
+ }
143
+
144
+ return annotations;
145
+ }
146
+
147
+ /**
148
+ * Returns the referred schema. Here the content is the asyncapi
149
+ * document and payloadObject is the reference path
150
+ * @returns an object with the referred schema.
151
+ */
152
+ resolveRef(content, payloadObject) {
153
+ let schema = {};
154
+
155
+ if (payloadObject['$ref']) {
156
+ const pathArray = payloadObject['$ref'].split('/');
157
+ pathArray.forEach((value) => {
158
+ if (value === '#') schema = content;
159
+ else if (schema[value]) schema = schema[value];
160
+ // in case path doesn't exist in the file
161
+ else schema = {};
162
+ });
163
+
164
+ if (!Object.keys(schema).length) {
165
+ console.warn(`WARNING: Unable to resolve ref path: ` + payloadObject['$ref']);
166
+ }
167
+ }
168
+ else if (payloadObject instanceof Object) {
169
+ // in case ref is not there and schema object is there
170
+ schema = payloadObject;
171
+ }
172
+
173
+ return schema;
174
+ }
175
+
176
+ /**
177
+ * Returns the properties and types generated, related to an event.
178
+ * @returns a properties object and a types object.
179
+ */
180
+ getPropertiesAndTypes(eventName, content) {
181
+ const properties = {};
182
+ let types = {};
183
+
184
+ if (content.properties) {
185
+ for (const [key, value] of Object.entries(content.properties)) {
186
+ // will contain type objects generated for a property
187
+ let referredTypes = {};
188
+ let isRequired = false;
189
+ if (content['required']?.includes(key)) isRequired = true;
190
+ [properties[key], referredTypes] = this.parseProperty(eventName, key, value, isRequired);
191
+ // collecting all the types
192
+ types = { ...types, ...referredTypes };
193
+ }
194
+ }
195
+ return [properties, types];
196
+ }
197
+
198
+ /**
199
+ * Returns the processed objects for property and type.
200
+ * @returns property object and type object.
201
+ */
202
+ parseProperty(eventName, propertyName, propertyContent, isRequired) {
203
+ let property = {};
204
+ let propertyAttributes = {};
205
+ let types = {};
206
+
207
+ if (isRequired === true) {
208
+ property['@mandatory'] = true;
209
+ }
210
+
211
+ let type = this.getTypeFromArray(propertyContent.type);
212
+
213
+ // case where type is defined
214
+ if (type && (type === 'object' || type === 'array')) {
215
+ // for generation of type: event name + property name
216
+ propertyName = propertyName[0].toUpperCase() + propertyName.substring(1);
217
+ let typeName = eventName + "." + propertyName;
218
+ // generate object for the introduced type
219
+ const elements = this.getTypeElements(propertyContent);
220
+ types[typeName] = { 'kind': 'type', ...elements };
221
+ // referring to the generated type from the property
222
+ propertyAttributes = { 'type': typeName };
223
+ } else {
224
+ propertyAttributes = this.getPropertyObject(propertyContent);
225
+ }
226
+ property = { ...property, ...propertyAttributes };
227
+
228
+ return [property, types];
229
+ }
230
+
231
+ /**
232
+ * Returns generated type for the property.
233
+ * @returns an object for a type.
234
+ */
235
+ getTypeElements(propertyContent) {
236
+ let properties = {};
237
+ let type = this.getTypeFromArray(propertyContent.type);
238
+
239
+ // for nested object
240
+ if (type === 'object') {
241
+ for (const [key, value] of Object.entries(propertyContent.properties)) {
242
+ properties[key] = this.getTypeElements(value);
243
+ }
244
+ }
245
+ // for nested array
246
+ else if (type === 'array') {
247
+ properties.items = this.getTypeElements(propertyContent.items);
248
+ return properties;
249
+ }
250
+ // base data type mapping
251
+ else {
252
+ return this.getPropertyObject(propertyContent);
253
+ }
254
+
255
+ return { elements: properties };
256
+ }
257
+
258
+ /**
259
+ * Returns type for the property from the AsyncAPI Document.
260
+ * @returns the value of type.
261
+ */
262
+ getTypeFromArray(typeValue) {
263
+ let type;
264
+ // if type is an array
265
+ if (Array.isArray(typeValue)) {
266
+ // one of the value will be null
267
+ type = typeValue[0] === 'null' ? typeValue[1] : typeValue[0];
268
+ } else {
269
+ type = typeValue;
270
+ }
271
+
272
+ return type;
273
+ }
274
+
275
+ /**
276
+ * Returns generated object for a single property.
277
+ * @returns an object for a property.
278
+ */
279
+ getPropertyObject(propertyContent) {
280
+ const property = {};
281
+ const inputType = this.getTypeFromArray(propertyContent.type);
282
+ const cdsType = this.getDataType(inputType, propertyContent.format);
283
+
284
+ property['type'] = cdsType;
285
+
286
+ // case: cds type is decimal
287
+ if (cdsType === 'cds.Decimal') {
288
+ if (propertyContent['x-sap-precision']) property.precision = propertyContent['x-sap-precision'];
289
+ if (propertyContent['x-sap-scale']) property.scale = propertyContent['x-sap-scale'];
290
+ }
291
+
292
+ if (propertyContent['default']) {
293
+ property['default'] = { 'val': propertyContent['default'] };
294
+ }
295
+
296
+ if (propertyContent['enum']) {
297
+ property['enum'] = this.getEnumObject(propertyContent['enum']);
298
+ }
299
+
300
+ return property;
301
+ }
302
+
303
+ /**
304
+ * Takes the enums from the AsyncAPI Document.
305
+ * @returns the property object of enums.
306
+ */
307
+ getEnumObject(enumArray) {
308
+ const enumProperty = {};
309
+
310
+ enumArray.forEach(val => {
311
+ enumProperty[val] = {};
312
+ });
313
+
314
+ return enumProperty;
315
+ }
316
+
317
+ /**
318
+ * Takes the type and format from the document and returns the corresponding cds type.
319
+ * @returns the corresponding cds type.
320
+ */
321
+ getDataType(type, format = null) {
322
+ const asyncapiToCdsMapping = {
323
+ 'number': 'cds.Double',
324
+ 'boolean': 'cds.Boolean',
325
+ 'integer': 'cds.Integer'
326
+ };
327
+
328
+ if (type === 'string') {
329
+ switch (format) {
330
+ case 'int64':
331
+ return 'cds.Integer64';
332
+ case 'decimal':
333
+ return 'cds.Decimal';
334
+ case 'uuid':
335
+ return 'cds.UUID';
336
+ case 'date':
337
+ return 'cds.Date';
338
+ case 'partial-time':
339
+ return 'cds.Time';
340
+ case 'date-time':
341
+ return 'cds.Timestamp';
342
+ default:
343
+ return 'cds.LargeString';
344
+ }
345
+ } else if (asyncapiToCdsMapping[type]) {
346
+ return asyncapiToCdsMapping[type];
347
+ } else {
348
+ throw new Error(messages.UNRESOLVED_TYPE + `'${type}'`);
349
+ }
350
+ }
351
+ }
@@ -0,0 +1,18 @@
1
+ const messages = require("../message").getMessages();
2
+
3
+ function validateAsyncAPI(content) {
4
+ if ('channels' in content && 'components' in content && 'messages' in content.components) {
5
+ return;
6
+ }
7
+
8
+ throw new Error(messages.INVALID_ASYNCAPI_FILE);
9
+ }
10
+
11
+ const typeRegex = new RegExp(
12
+ '^(?<namespace>(?:[a-z][a-z0-9]*)(?:[.][a-z][a-z0-9]*)+)[.](?<businessObject>[a-zA-Z0-9]+)[.](?<operation>[a-zA-Z0-9]+)([.](?<version>v(?:[0-9]|[1-9][0-9]*)))?$'
13
+ );
14
+
15
+ module.exports = {
16
+ validateAsyncAPI,
17
+ typeRegex
18
+ }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-prototype-builtins */
1
2
  'use strict';
2
3
 
3
4
  let convert = require('xml-js');
@@ -18,17 +19,6 @@ let optionsMapping = {
18
19
  let apiOptions = ['includeNamespaces', 'keepNamespace'];
19
20
  let commonOptions = ['no_copy', 'no_save', 'out', 'dry', 'as', 'from', 'force'];
20
21
 
21
- function _isJson(edmx) {
22
- let isJson = false;
23
- try {
24
- JSON.parse(edmx);
25
- isJson = true;
26
- } catch (err) {
27
- isJson = false;
28
- }
29
- return isJson;
30
- }
31
-
32
22
  async function generateEDMX2JSON(edmx) {
33
23
  let cjson = convert.xml2json(edmx, {
34
24
  compact: true,
@@ -49,71 +39,34 @@ async function _isValidXML(edmx) {
49
39
  }
50
40
 
51
41
  async function _getODataVersion(edmx) {
52
- let oDataVersion;
53
- let jsonObj;
54
- let dataServices;
55
- let dataServicesAttributes;
56
- // Check OData version
57
- if (_isJson(edmx)) {
58
- return '';
59
- }
60
- jsonObj = await generateEDMX2JSON(edmx);
61
- try {
62
- dataServices = jsonObj['edmx:Edmx']['edmx:DataServices'];
63
- dataServicesAttributes = dataServices._attributes;
64
- } catch (err) {
65
- oDataVersion = '';
66
- }
67
- if (dataServicesAttributes) {
68
- oDataVersion = dataServicesAttributes['m:DataServiceVersion'];
69
- } else {
70
- // V4 edmx
71
- try {
72
- oDataVersion = jsonObj['edmx:Edmx']._attributes.Version;
73
- } catch (err) {
74
- oDataVersion = '';
75
- }
76
- }
77
- return oDataVersion;
78
- }
42
+ let oDataVersion = "";
79
43
 
80
- async function _isOdataV2(fileInputStream) {
81
- let isV2 = false;
82
- // existing logic
83
- const version = await _getODataVersion(fileInputStream);
84
- if (version === '1.0' || version === '2.0') {
85
- isV2 = true;
44
+ let checkForV2 = edmx.match(/m:DataServiceVersion="[^"]+"/);
45
+ if (checkForV2) {
46
+ oDataVersion = checkForV2[0].split('"')[1];
47
+ return oDataVersion;
86
48
  }
87
- return isV2;
88
- }
89
49
 
90
- async function _isOdataV4(fileInputStream) {
91
- let isV4 = false;
92
- let version;
93
- let edmj;
94
- if (_isJson(fileInputStream)) {
95
- edmj = JSON.parse(fileInputStream);
96
- version = edmj.$Version;
97
- } else {
98
- version = await _getODataVersion(fileInputStream);
50
+ let edmxMatching = edmx.match(/<edmx:Edmx[^>]+"/);
51
+ if (edmxMatching) {
52
+ let checkForV4 = edmxMatching[0].match(/Version="[^"]+"/);
53
+ oDataVersion = checkForV4[0].split('"')[1];
99
54
  }
100
- if (version === '4.0' || version === "4.01") {
101
- isV4 = true;
102
- }
103
- return isV4;
55
+
56
+ return oDataVersion;
104
57
  }
105
58
 
106
- async function getVersion(fileInputStream) {
107
- // method to find the version
108
- // input can be JSON or XML
109
- // if xml, find the version
110
- // if json, find the version
59
+ // method to find the version, input is in XML format
60
+ async function getVersion(fileInputStream){
111
61
  let oDataVersion;
112
- if (await _isOdataV2(fileInputStream)) {
62
+ const version = await _getODataVersion(fileInputStream);
63
+
64
+ if (version === '1.0' || version === '2.0') {
113
65
  oDataVersion = 'V2';
114
- } else if (await _isOdataV4(fileInputStream)) {
66
+ } else if (version === '4.0' || version === "4.01") {
115
67
  oDataVersion = 'V4';
116
68
  }
69
+
117
70
  return oDataVersion;
118
71
  }
119
72
 
@@ -402,9 +355,10 @@ async function identifyFile(filepath) {
402
355
  const src = await getContentFromFile(filepath);
403
356
  //for openapi
404
357
  let jsonSrc = JSON.parse(src);
405
- // eslint-disable-next-line no-prototype-builtins
406
358
  if (jsonSrc.hasOwnProperty('openapi') || jsonSrc.hasOwnProperty('swagger')) {
407
359
  return "openapi";
360
+ } else if (jsonSrc.hasOwnProperty('asyncapi')){
361
+ return "asyncapi";
408
362
  }
409
363
  } else
410
364
  return extension;
@@ -3,12 +3,14 @@ let { path } = cds.utils;
3
3
  let common = require('./common');
4
4
  let odata = require('./odata');
5
5
  let openapi = require('./openapi');
6
+ let asyncapi = require('./asyncapi');
6
7
  let messages = require('./message').getMessages();
7
8
 
8
9
  module.exports = Object.assign(cds_import, {
9
10
  from: cds.lazified({
10
11
  edmx: cds_import_from_edmx,
11
- openapi: cds_import_from_openapi
12
+ openapi: cds_import_from_openapi,
13
+ asyncapi: cds_import_from_asyncapi
12
14
  })
13
15
  });
14
16
 
@@ -35,6 +37,9 @@ async function cds_import(filepath, options = { keepNamespace: false }) {
35
37
  case "openapi":
36
38
  csn = await cds_import_from_openapi(filepath, options);
37
39
  break;
40
+ case "asyncapi":
41
+ csn = await cds_import_from_asyncapi(filepath, options);
42
+ break;
38
43
  default:
39
44
  throw new Error(messages.INVALID_INPUT_FILE);
40
45
  }
@@ -63,12 +68,13 @@ async function cds_import_from_edmx(filepath, options = { keepNamespace: false }
63
68
  common.readEnvVariables(odataOptions, false);
64
69
  }
65
70
  // generates csn
71
+ odataOptions.odataVersion = await common.getVersion(src);
66
72
  odataOptions.include_all_namespaces = false;
67
73
  odataOptions.namespaces = [];
68
74
  if (odataOptions.include_namespaces) odata.getNamespaces(odataOptions);
69
75
  let csn = await odata.edmx2csn(src, !odataOptions.keep_namespace && filepath, odataOptions);
70
76
  //reading the file version
71
- options.inputFileVersion = odataOptions.inputFileVersion;
77
+ options.odataVersion = odataOptions.odataVersion;
72
78
  // return the csn
73
79
  return csn;
74
80
  }
@@ -86,4 +92,19 @@ async function cds_import_from_openapi(filepath, options = {}) {
86
92
  let csn = openapi.openAPI2csn(src);
87
93
  options.inputFileVersion = 'rest';
88
94
  return csn;
95
+ }
96
+
97
+ /**
98
+ * This is the import API function to generate CSN from asyncapi files.
99
+ * @param {string} filepath
100
+ * - a single valid filepath
101
+ * @returns {object} CSN
102
+ */
103
+ async function cds_import_from_asyncapi(filepath, options = {}) {
104
+ // validates the input file
105
+ filepath = await common.isValidInputFile(path.resolve(process.cwd(), filepath));
106
+ const src = await common.getContentFromFile(filepath);
107
+ let csn = asyncapi.asyncapi2csn(src);
108
+ options.inputFileVersion = 'odata';
109
+ return csn;
89
110
  }
@@ -16,9 +16,19 @@ const messages = {
16
16
  MISSING_ASSOCIATION: 'Association is missing in the input file provided. Please check the edmx file.',
17
17
  COLLECTION_IN_KEY: 'Key Property of an Entity cannot be of the type Collection.',
18
18
  INVALID_OPENAPI_FILE: 'The OpenAPI file is not valid. Specify the correct OpenAPI file.',
19
+ INVALID_ASYNCAPI_FILE: 'The AsyncAPI file is not valid. Specify the correct AsyncAPI file.',
20
+ INVALID_MESSAGE_NAME: 'The message name is not valid.',
19
21
  UNRESOLVED_TYPE: 'Could not resolve the type: ',
20
22
  NO_RETURN_TYPE: 'No ReturnType found in Function: ',
21
- INVALID_ENUM_TYPE: 'The Underlying type provided is not supported for the EnumType: '
23
+ INVALID_ENUM_TYPE: 'The Underlying type provided is not supported for the EnumType: ',
24
+ UNBOUNDED_ACTION_COLLISION: 'Unbounded Action does not support collision. Check the Action ',
25
+ INVALID_BOUNDED_PARAM_TYPE: 'Parameter Type of Bounded Action/Function should not refer to an Entity in another Schema.',
26
+ BOUNDED_ACTION_COLLISION: 'For Bounded Action, combination of action name and binding parameter type should be unique.',
27
+ STREAM_NO_COLLECTION: `'Edm.Stream' cannot be of Collection type.`,
28
+ UNSUPPORTED_PROPERTY_TYPE: 'Property cannot be of EntityType. Check the type: ',
29
+ UNRESOLVED_SCHEMA_NAMESPACE: `Could not resolve the schema `,
30
+ INVALID_DATATYPE: `Invalid Data Type `,
31
+ ELEMENT_NAME_COLLISION: 'Collision between EntityContainer Element Name and Schema Element Name is currenlty not supported. Please check the element name: '
22
32
  };
23
33
 
24
34
  function getMessages() {
@@ -53,6 +53,7 @@ function _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace) {
53
53
 
54
54
  async function edmx2csn(edmx, filename, context) {
55
55
  let namespace;
56
+ // we might get aliased namespace value here
56
57
  const result = edmx.match(/EntityType="([^"]+)/);
57
58
  const multipleSchemas = /(\s*<\/Schema>\s*<Schema [^>]*>)\s*/m;
58
59
  if (result) {
@@ -67,13 +68,23 @@ async function edmx2csn(edmx, filename, context) {
67
68
  schema = schema[0].match(/Namespace="[^"]+"/);
68
69
  namespace = schema[0].slice(schema[0].lastIndexOf('Namespace') + 11, schema[0].lastIndexOf('"'));
69
70
  }
70
- if (multipleSchemas.test(edmx)) {
71
+ // this hack is only used for OData V2, for V4 we support multiple schema scenario
72
+ if (context.odataVersion === 'V2' && multipleSchemas.test(edmx)) {
71
73
  edmx = _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace);
72
74
  }
73
75
  let csn = await _generateCSN(edmx, false, true, context);
74
76
  const regex = /^[\p{Letter}\p{Nl}_]{1}[_\p{Letter}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]{0,127}$/gu
75
77
  // Regex checks for this condition “A-B” must start with a letter or underscore, followed by at most 127 letters, underscores or digits in the fileName
76
78
  if (filename) {
79
+ /**
80
+ * V4: <Schema Namespace="Full.Schema.Name" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="aliasedValue">
81
+ * V2: <Schema Namespace="Full.Schema.Name" xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Alias="aliasedValue">
82
+ *
83
+ * context.schemaNamespace is set to correct namespace value by the respective parsers
84
+ */
85
+ if (context.schemaNamespace) {
86
+ namespace = context.schemaNamespace;
87
+ }
77
88
  // Replace namespace by given filename in the csn file instead of edmx file
78
89
  const re = RegExp(`([("/])${namespace}([/").])`, 'g');
79
90
  let name = path.parse(filename).name;
@@ -89,16 +100,11 @@ async function edmx2csn(edmx, filename, context) {
89
100
 
90
101
  async function _generateCSN(edmx, ignorePersistenceSkip, mockServerUc, context) {
91
102
  let csn;
92
- let odataVersion;
93
103
  let edmx2jsonModel;
94
104
  let defaultNamespaces;
95
105
  let namespaces = [];
96
106
  if (context.namespaces) namespaces.push(...(context.namespaces));
97
- odataVersion = await common.getVersion(edmx);
98
- if (odataVersion === "V2") {
99
- if (context) {
100
- context.inputFileVersion = "V2";
101
- }
107
+ if (context.odataVersion === "V2") {
102
108
  // default namespaces to be present
103
109
  if (!context.include_all_namespaces) {
104
110
  defaultNamespaces = ['edmx', 'sap', 'm'];
@@ -113,10 +119,7 @@ async function _generateCSN(edmx, ignorePersistenceSkip, mockServerUc, context)
113
119
  else reject("Error Occurred");
114
120
  });
115
121
  }
116
- else if (odataVersion === 'V4') {
117
- if (context) {
118
- context.inputFileVersion = "V4";
119
- }
122
+ else if (context.odataVersion === 'V4') {
120
123
  // default namespaces to be present
121
124
  if (!context.include_all_namespaces) {
122
125
  defaultNamespaces = ['edmx'];