@sap/cds-dk 6.5.2 → 6.6.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 +24 -0
  2. package/bin/activate.js +7 -0
  3. package/bin/add.js +1 -2
  4. package/bin/build/all.js +14 -9
  5. package/bin/cds.js +11 -8
  6. package/bin/compile/index.js +13 -3
  7. package/bin/env.js +16 -25
  8. package/bin/extend.js +8 -0
  9. package/bin/init.js +0 -3
  10. package/bin/login.js +3 -7
  11. package/bin/logout.js +2 -7
  12. package/bin/watch.js +4 -5
  13. package/lib/client/activate.js +1 -1
  14. package/lib/client/helper/errors.js +2 -1
  15. package/lib/client/params.js +4 -3
  16. package/lib/client/pull.js +31 -5
  17. package/lib/client/push.js +3 -3
  18. package/lib/client/settings_manager.js +6 -6
  19. package/lib/client/subscription.js +1 -1
  20. package/lib/compile/asyncapi/channels.js +22 -0
  21. package/lib/compile/asyncapi/components/messageTraits/index.js +89 -0
  22. package/lib/compile/asyncapi/components/messages/index.js +150 -0
  23. package/lib/compile/asyncapi/components/schemas/annotations.js +13 -0
  24. package/lib/compile/asyncapi/components/schemas/array.js +8 -0
  25. package/lib/compile/asyncapi/components/schemas/cache.js +14 -0
  26. package/lib/compile/asyncapi/components/schemas/definition.js +187 -0
  27. package/lib/compile/asyncapi/components/schemas/enum.js +16 -0
  28. package/lib/compile/asyncapi/components/schemas/examples.js +10 -0
  29. package/lib/compile/asyncapi/components/schemas/index.js +14 -0
  30. package/lib/compile/asyncapi/components/schemas/localized.js +29 -0
  31. package/lib/compile/asyncapi/components/schemas/primitiveElement.js +41 -0
  32. package/lib/compile/asyncapi/components/schemas/ref.js +45 -0
  33. package/lib/compile/asyncapi/components/schemas/utils.js +108 -0
  34. package/lib/compile/asyncapi/index.js +226 -0
  35. package/lib/compile/asyncapi/message.js +13 -0
  36. package/lib/compile/asyncapi/utils.js +27 -0
  37. package/lib/compile/index.js +1 -0
  38. package/lib/compile/openapi/csdl2openapi.js +40 -18
  39. package/lib/import/message.js +3 -1
  40. package/lib/import/odata/metadataconverter/MetadataConverter.js +1 -1
  41. package/lib/import/odata/v2parser.js +131 -50
  42. package/lib/import/odata/v4parser.js +160 -68
  43. package/lib/import/openapi/importOpenAPI.js +14 -14
  44. package/lib/init/constants.js +1 -1
  45. package/lib/init/index.js +11 -21
  46. package/lib/init/template/_merging/registry-mta.js +15 -26
  47. package/lib/init/template/approuter/files/package.json +2 -2
  48. package/lib/init/template/approuter/index.js +6 -12
  49. package/lib/init/template/auditlog/files/auditlog.yaml +4 -1
  50. package/lib/init/template/auditlog/index.js +23 -8
  51. package/lib/init/template/cf-manifest/index.js +4 -14
  52. package/lib/init/template/connectivity/files/connectivity-proxy-info.yaml +2 -6
  53. package/lib/init/template/connectivity/files/values.yaml.hbs +5 -8
  54. package/lib/init/template/connectivity/index.js +2 -9
  55. package/lib/init/template/data/index.js +0 -4
  56. package/lib/init/template/destinations/files/destination.yaml +4 -1
  57. package/lib/init/template/destinations/index.js +25 -10
  58. package/lib/init/template/enterprise-messaging/files/event-mesh.yaml +4 -1
  59. package/lib/init/template/enterprise-messaging/files/values.yaml +1 -2
  60. package/lib/init/template/enterprise-messaging/index.js +28 -20
  61. package/lib/init/template/extensibility/index.js +23 -31
  62. package/lib/init/template/hana/files/hana.yaml +4 -1
  63. package/lib/init/template/hana/index.js +44 -24
  64. package/lib/init/template/helm/chart/templates/_helpers.tpl +12 -0
  65. package/lib/init/template/helm/chart/values.root.schema.json +40 -0
  66. package/lib/init/template/helm/chart/values.schema.json +32 -69
  67. package/lib/init/template/helm/index.js +26 -16
  68. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +13 -26
  69. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +3 -3
  70. package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +2 -0
  71. package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +3 -3
  72. package/lib/init/template/helm/subcharts/content-deployment/templating.md +37 -0
  73. package/lib/init/template/helm/subcharts/content-deployment/values.md +182 -185
  74. package/lib/init/template/helm/subcharts/content-deployment/values.schema.json +18 -22
  75. package/lib/init/template/helm/subcharts/content-deployment/values.yaml +2 -0
  76. package/lib/init/template/helm/subcharts/service-instance/Chart.yaml +5 -0
  77. package/lib/init/template/helm/subcharts/service-instance/templates/_helpers.tpl +65 -0
  78. package/lib/init/template/helm/subcharts/service-instance/templates/serviceinstance.yaml +23 -0
  79. package/lib/init/template/helm/subcharts/service-instance/templating.md +18 -0
  80. package/lib/init/template/helm/subcharts/service-instance/values-lint.yaml +2 -0
  81. package/lib/init/template/helm/subcharts/service-instance/values.md +186 -0
  82. package/lib/init/template/helm/subcharts/service-instance/values.schema.json +102 -0
  83. package/lib/init/template/helm/subcharts/service-instance/values.yaml +15 -0
  84. package/lib/init/template/html5-repo/files/html5-apps-repo-host.yaml +4 -1
  85. package/lib/init/template/html5-repo/index.js +30 -21
  86. package/lib/init/template/java/index.js +2 -5
  87. package/lib/init/template/kibana-logging/index.js +7 -10
  88. package/lib/init/template/lint/index.js +2 -3
  89. package/lib/init/template/mta/files/mta.yaml.hbs +4 -2
  90. package/lib/init/template/mta/index.js +7 -11
  91. package/lib/init/template/mtx/index.js +0 -5
  92. package/lib/init/template/multitenancy/files/java/package.json +16 -7
  93. package/lib/init/template/multitenancy/files/mta.yaml.hbs +11 -3
  94. package/lib/init/template/multitenancy/files/saas-registry-secret.yaml +9 -0
  95. package/lib/init/template/multitenancy/files/saas-registry.yaml +4 -17
  96. package/lib/init/template/multitenancy/files/service-manager.yaml +4 -1
  97. package/lib/init/template/multitenancy/files/values.yaml.hbs +15 -3
  98. package/lib/init/template/multitenancy/index.js +56 -31
  99. package/lib/init/template/nodejs/index.js +4 -7
  100. package/lib/init/template/notebook/index.js +1 -3
  101. package/lib/init/template/pipeline/index.js +2 -5
  102. package/lib/init/template/samples/index.js +6 -11
  103. package/lib/init/template/templateBase.js +10 -17
  104. package/lib/init/template/toggles/index.js +2 -6
  105. package/lib/init/template/xsuaa/files/values.yaml.hbs +0 -1
  106. package/lib/init/template/xsuaa/files/xsuaa.yaml +4 -1
  107. package/lib/init/template/xsuaa/index.js +27 -23
  108. package/lib/init/types.d.ts +0 -81
  109. package/lib/init/util/merge.js +23 -25
  110. package/lib/init/util/projectReader.js +27 -24
  111. package/lib/lint/Linter.js +31 -13
  112. package/lib/watch/livereload-server.js +21 -14
  113. package/lib/watch/node-watch.js +51 -49
  114. package/npm-shrinkwrap.json +551 -458
  115. package/package.json +2 -2
  116. package/readme.md +5 -0
  117. package/lib/init/template/connectivity/files/connectivity-binding.yaml +0 -9
  118. package/lib/init/template/connectivity/files/connectivity.yaml +0 -1
  119. package/lib/init/template/multitenancy/files/xsuaa.yaml +0 -1
  120. /package/lib/init/template/hana/files/{chart.yaml → hana-deployer.yaml} +0 -0
  121. /package/lib/init/template/html5-repo/files/{chart.yaml → html5-apps-deployer.yaml} +0 -0
  122. /package/lib/init/template/multitenancy/files/{chart.yaml → sidecar.yaml} +0 -0
@@ -0,0 +1,108 @@
1
+
2
+ function toString(element) {
3
+ let schema = getBaseTypeStructure("string");
4
+
5
+ if ('length' in element && typeof element.length === 'number') {
6
+ schema.maxLength = element.length;
7
+ }
8
+
9
+ return schema;
10
+ }
11
+
12
+ function getBaseTypeStructure(type) {
13
+ return { type: type };
14
+ }
15
+
16
+ function getFormatStructure(format, example) {
17
+ let schema = getBaseTypeStructure("string");
18
+
19
+ schema.format = format;
20
+ if (example) {
21
+ schema.example = [example];
22
+ }
23
+
24
+ return schema;
25
+ }
26
+
27
+ function isAssociation(entry) {
28
+ return !!entry && entry.type === 'cds.Association';
29
+ }
30
+
31
+ function isComposition(entry) {
32
+ return !!entry && entry.type === 'cds.Composition';
33
+ }
34
+
35
+ function isStructured(entry) {
36
+ return !!entry && typeof entry === 'object' && 'elements' in entry && isElements(entry.elements);
37
+ }
38
+
39
+ function isToMany(entry) {
40
+ return entry.cardinality?.max === '*' || (typeof entry.cardinality?.max === 'number' && entry.cardinality?.max > 1);
41
+ }
42
+
43
+ function isDefault(entry) {
44
+ return "default" in entry;
45
+ }
46
+
47
+ function isEnum(entry) {
48
+ return "enum" in entry;
49
+ }
50
+
51
+ function addDefaultValue(entry, schema) {
52
+ schema.default = entry.default.val;
53
+ }
54
+
55
+ function isVal(value) {
56
+ return typeof value === 'object' && !!value && 'val' in value && Object.keys(value).length === 1;
57
+ }
58
+
59
+ function isElements(value) {
60
+ return typeof value === 'object' && !!value && !Array.isArray(value) && !('=' in value) && !isVal(value);
61
+ }
62
+
63
+ function isManyEntry(entry) {
64
+ return (
65
+ 'items' in entry &&
66
+ typeof entry.items === 'object' &&
67
+ !!entry.items &&
68
+ (('type' in entry.items && typeof entry.items.type === 'string') ||
69
+ ('elements' in entry.items && isElements(entry.items.elements)))
70
+ );
71
+ }
72
+
73
+ function isLocalizedElement(name, element) {
74
+ return (((name === 'texts' && isComposition(element)) || (name === 'localized' && isAssociation(element))) &&
75
+ element.target.endsWith('.texts'));
76
+ }
77
+
78
+ function isToManyAssociation(entry) {
79
+ return isAssociation(entry) && isToMany(entry);
80
+ }
81
+
82
+ function elementIsRelevant(name, element) {
83
+ return !isLocalizedElement(name, element);
84
+ }
85
+
86
+ function isRefEntry(entry) {
87
+ let type = entry.type;
88
+ return typeof type === 'object' && !!type && 'ref' in type && Array.isArray(type.ref);
89
+ }
90
+
91
+ module.exports = {
92
+ toString,
93
+ getBaseTypeStructure,
94
+ getFormatStructure,
95
+ isAssociation,
96
+ isComposition,
97
+ isStructured,
98
+ isToMany,
99
+ isDefault,
100
+ addDefaultValue,
101
+ isEnum,
102
+ isVal,
103
+ elementIsRelevant,
104
+ isManyEntry,
105
+ isElements,
106
+ isRefEntry,
107
+ isToManyAssociation
108
+ }
@@ -0,0 +1,226 @@
1
+ const csnToJSONSchema = require('./components/schemas');
2
+ const getMessageTraits = require('./components/messageTraits');
3
+ const definitionsToMessages = require('./components/messages');
4
+ const getChannels = require('./channels');
5
+ const cds = require('../../cds');
6
+ const messages = require("./message");
7
+ const presetMapping = {
8
+ 'event_spec_version': 'x-sap-event-spec-version',
9
+ 'event_source': 'x-sap-event-source',
10
+ 'event_source_params': 'x-sap-event-source-parameters',
11
+ 'event_characteristics': 'x-sap-event-characteristics'
12
+ };
13
+
14
+ module.exports = function processor(csn, options = {}) {
15
+
16
+ const envConf = cds.env.export?.asyncapi;
17
+ _serviceErrorHandling(csn, options);
18
+
19
+ const applicationNamespace = envConf?.application_namespace;
20
+
21
+ if (!applicationNamespace) {
22
+ throw new Error(messages.APPLICATION_NAMESPACE);
23
+ }
24
+
25
+ const presets = getPresets(envConf);
26
+
27
+ if (options.service === 'all') {
28
+ if (options["asyncapi:merged"]) {
29
+ if (!envConf?.merged?.title || !envConf.merged?.version ) {
30
+ throw new Error(messages.TITLE_VERSION_MERGED);
31
+ }
32
+
33
+ const infoObject = getInfoObject({}, envConf.merged);
34
+ // verify version follows the required pattern
35
+ isVersionValid(infoObject.version);
36
+ const shortTextObj = getShortText({}, envConf.merged.short_text);
37
+
38
+ const events = getEvents(csn.definitions);
39
+
40
+ return {
41
+ asyncapi: '2.0.0',
42
+ 'x-sap-catalog-spec-version': '1.2',
43
+ 'x-sap-application-namespace': applicationNamespace,
44
+ ...Object.keys(shortTextObj).length && { 'x-sap-shortText': shortTextObj },
45
+ info: infoObject,
46
+ defaultContentType: 'application/json',
47
+ channels: getChannels(csn.definitions, events),
48
+ components: getComponents(csn.definitions, events, presets)
49
+ }
50
+ } else {
51
+ const services = getServicesWithEvents(csn.definitions);
52
+ return _iterate(services, csn, applicationNamespace, presets);
53
+ }
54
+ } else {
55
+ const events = getEvents(csn.definitions, options.service);
56
+
57
+ if (events.length === 0) {
58
+ throw new Error(messages.NO_EVENTS);
59
+ }
60
+
61
+ return _getAsyncApi(csn, events, options.service, applicationNamespace, presets);
62
+ }
63
+ }
64
+
65
+ function getPresets(envConf) {
66
+ let projectPresets = {};
67
+ for (const [key, value] of Object.entries(envConf)) {
68
+ if (presetMapping[key]) projectPresets[presetMapping[key]] = value;
69
+ }
70
+ return projectPresets;
71
+ }
72
+
73
+ function _getAsyncApi(csn, events, serviceName, applicationNamespace, presets) {
74
+
75
+ const infoObject = getInfoObject(csn.definitions[serviceName]);
76
+ const stateObj = getStateInfo(csn.definitions[serviceName]);
77
+ const shortTextObj = getShortText(csn.definitions[serviceName]);
78
+
79
+ return {
80
+ asyncapi: '2.0.0',
81
+ 'x-sap-catalog-spec-version': '1.2',
82
+ 'x-sap-application-namespace': applicationNamespace,
83
+ ...Object.keys(stateObj).length && { 'x-sap-stateInfo': stateObj },
84
+ ...Object.keys(shortTextObj).length && { 'x-sap-shortText': shortTextObj },
85
+ info: infoObject,
86
+ defaultContentType: 'application/json',
87
+ channels: getChannels(csn.definitions, events),
88
+ components: getComponents(csn.definitions, events, presets)
89
+ };
90
+ }
91
+
92
+ function getShortText(serviceCsn, presetValue = undefined) {
93
+ let shortTextValue = presetValue || serviceCsn["@AsyncAPI.ShortText"];
94
+ if (shortTextValue) {
95
+ return shortTextValue;
96
+ }
97
+ return {};
98
+ }
99
+
100
+ function getStateInfo(serviceCsn) {
101
+ const stateProperties = ['state', 'deprecationDate', 'decomissionedDate', 'link'];
102
+ const stateObj = {};
103
+
104
+ stateProperties.forEach(function (item) {
105
+ if (serviceCsn['@AsyncAPI.StateInfo.' + item]) {
106
+ stateObj[item] = serviceCsn['@AsyncAPI.StateInfo.' + item];
107
+ }
108
+ });
109
+
110
+ return stateObj;
111
+ }
112
+
113
+ function getInfoObject(serviceCsn, mergedPresetObject = undefined) {
114
+ const info = {};
115
+
116
+ if (mergedPresetObject) {
117
+ Object.keys(mergedPresetObject).forEach(key => {
118
+ if (key !== "short_text") info[key] = mergedPresetObject[key];
119
+ });
120
+ return info;
121
+ }
122
+
123
+ if (!serviceCsn['@AsyncAPI.SchemaVersion'] || !serviceCsn['@AsyncAPI.Title']) {
124
+ throw new Error(messages.TITLE_VERSION_ANNOTATIONS);
125
+ }
126
+
127
+ isVersionValid(serviceCsn['@AsyncAPI.SchemaVersion']);
128
+
129
+ info.version = serviceCsn['@AsyncAPI.SchemaVersion'];
130
+ info.title = serviceCsn['@AsyncAPI.Title'];
131
+
132
+ if (serviceCsn['@AsyncAPI.Description']) {
133
+ info.description = serviceCsn['@AsyncAPI.Description'];
134
+ }
135
+
136
+ return info;
137
+ }
138
+
139
+ function isVersionValid(version) {
140
+ version.split('.').forEach(element => {
141
+ if (isNaN(element)) {
142
+ throw new Error(messages.UNSUPPORTED_VERSION);
143
+ }
144
+ });
145
+ return true;
146
+ }
147
+
148
+ function getEvents(definitions, service = '') {
149
+ let events = [];
150
+ for (const [key, value] of Object.entries(definitions)) {
151
+ if (value.kind === 'event' && value._service !== undefined) {
152
+ if (service === '' || value._service.name === service) {
153
+ events.push(key);
154
+ }
155
+ }
156
+ }
157
+ return events;
158
+ }
159
+
160
+ function getServicesWithEvents(definitions) {
161
+ const services = {};
162
+
163
+ // traverse the definitions list and extract services
164
+ for (const [key, value] of Object.entries(definitions)) {
165
+ if (value.kind === 'service') {
166
+ services[key] = {
167
+ events: []
168
+ };
169
+ }
170
+ }
171
+
172
+ // traverse the definitions list and map events to services
173
+ for (const [key, value] of Object.entries(definitions)) {
174
+ if (value.kind === 'event' && value._service) {
175
+ services[value._service.name].events.push(key);
176
+ }
177
+ }
178
+
179
+ // traverse the services list and remove service with no events
180
+ for (const [key, value] of Object.entries(services)) {
181
+ if (value.events.length === 0) {
182
+ delete services[key];
183
+ }
184
+ }
185
+
186
+ return services;
187
+ }
188
+
189
+ function getComponents(definitions, events, presets) {
190
+
191
+ return {
192
+ messageTraits: getMessageTraits(),
193
+ messages: definitionsToMessages(definitions, events, presets),
194
+ schemas: csnToJSONSchema(definitions, events)
195
+ }
196
+ }
197
+
198
+ function _serviceErrorHandling(csn, options) {
199
+ const services = cds.linked(csn).services;
200
+
201
+ if (services.length < 1) throw new Error(messages.NO_SERVICES);
202
+
203
+ if (!options.service && services.length > 1) throw new Error(`\n
204
+ Found multiple service definitions in given model(s).
205
+ Please choose by adding one of... \n
206
+ -s all ${services.map(s => `\n -s ${s.name}`).join('')}
207
+ `);
208
+
209
+ if (!options.service) {
210
+ options.service = services[0].name;
211
+ } else if (options.service !== 'all') {
212
+ const srv = services.find(s => s.name === options.service);
213
+ if (!srv) throw new Error(`Service definition with given ${options.service} not found in the model(s).`);
214
+ }
215
+
216
+ if (options["asyncapi:merged"] && options.service !== 'all') {
217
+ throw new Error(messages.MERGED_FLAG);
218
+ }
219
+ }
220
+
221
+ function* _iterate(services, csn, applicationNamespace, presets) {
222
+ for (const [key, value] of Object.entries(services)) {
223
+ const generatedAsyncAPI = _getAsyncApi(csn, value.events, key, applicationNamespace, presets);
224
+ yield [generatedAsyncAPI, { file: key }];
225
+ }
226
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ const errorMessages = {
4
+ APPLICATION_NAMESPACE: 'Preset for application namespace needs to be added.',
5
+ TITLE_VERSION_MERGED: 'Preset for Title and Version info needs to be added when merged flag is used.',
6
+ NO_EVENTS: 'No events found in the service.',
7
+ TITLE_VERSION_ANNOTATIONS: 'Title and Version info annotations needs to be added to the service(s).',
8
+ UNSUPPORTED_VERSION: 'The version value provided is unsupported.',
9
+ NO_SERVICES: 'There are no service definitions found in the given model(s).',
10
+ MERGED_FLAG: 'Merged flag cannot be used with single service definition.',
11
+ };
12
+
13
+ module.exports = errorMessages;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Returns value of EventType Annotation if it is present. Otherwise it returns the definition name only.
3
+ */
4
+ function getEventName(eventName, eventDefinition) {
5
+
6
+ if (eventDefinition["@AsyncAPI.EventType"]) {
7
+ eventName = eventDefinition["@AsyncAPI.EventType"];
8
+ }
9
+
10
+ return eventName;
11
+ }
12
+
13
+ /**
14
+ * Gets the property value from the entry if it is defined otherwise returns undefined.
15
+ */
16
+ function getOwnValue(entry, key) {
17
+ if (Object.prototype.hasOwnProperty.call(entry, key)) {
18
+ return entry[key];
19
+ }
20
+
21
+ return;
22
+ }
23
+
24
+ module.exports = {
25
+ getEventName,
26
+ getOwnValue
27
+ }
@@ -4,6 +4,7 @@ const { defineProperty } = Object
4
4
 
5
5
  'xsuaa' in comp_to || defineProperty (comp_to, 'xsuaa', {enumerable:true, get:()=> require('./to-xsuaa')})
6
6
  'openapi' in comp_to || defineProperty (comp_to, 'openapi', {enumerable:true, get:()=> require('./openapi')})
7
+ 'asyncapi' in comp_to || defineProperty (comp_to, 'asyncapi', {enumerable:true, get:()=> require('./asyncapi')})
7
8
 
8
9
  'edmx-v2' in comp_to || defineProperty (comp_to, 'edmx-v2', {enumerable:true, value: (csn,o) => comp_to.edmx(csn,{...o,flavor:'v2'})})
9
10
  'edmx-v4' in comp_to || defineProperty (comp_to, 'edmx-v4', {enumerable:true, value: (csn,o) => comp_to.edmx(csn,{...o,flavor:'v4'})})
@@ -24,14 +24,23 @@ const SUFFIX = {
24
24
  read: "",
25
25
  create: "-create",
26
26
  update: "-update",
27
- };
28
-
29
- const TITLE_SUFFIX = {
27
+ };
28
+
29
+ const TITLE_SUFFIX = {
30
30
  "": "",
31
31
  "-create": " (for create)",
32
32
  "-update": " (for update)",
33
- };
34
-
33
+ };
34
+
35
+ /**
36
+ * ODM annotations in CDS that should be converted into OpenAPI.
37
+ */
38
+ const ODM_ANNOTATIONS = Object.freeze(
39
+ {
40
+ '@ODM.entityName': 'x-sap-odm-entity-name',
41
+ '@ODM.oid': 'x-sap-odm-oid',
42
+ });
43
+
35
44
  /**
36
45
  * Construct an OpenAPI description from a CSDL document
37
46
  * @param {object} csdl CSDL document
@@ -210,7 +219,7 @@ module.exports.csdl2openapi = function (
210
219
  const terms = {
211
220
  Authorization: ['Authorizations', 'SecuritySchemes'],
212
221
  Capabilities: ['BatchSupport', 'BatchSupported', 'ChangeTracking', 'CountRestrictions', 'DeleteRestrictions', 'DeepUpdateSupport', 'ExpandRestrictions',
213
- 'FilterRestrictions', 'IndexableByKey', 'InsertRestrictions', 'KeyAsSegmentSupported', 'NavigationRestrictions', 'OperationRestrictions',
222
+ 'FilterRestrictions', 'IndexableByKey', 'InsertRestrictions', 'KeyAsSegmentSupported', 'NavigationRestrictions', 'OperationRestrictions',
214
223
  'ReadRestrictions', 'SearchRestrictions', 'SelectSupport', 'SkipSupported', 'SortRestrictions', 'TopSupported', 'UpdateRestrictions'],
215
224
  Core: ['AcceptableMediaTypes', 'Computed', 'DefaultNamespace', 'Description', 'Example', 'Immutable', 'LongDescription',
216
225
  'OptionalParameter', 'Permissions', 'SchemaVersion'],
@@ -248,8 +257,8 @@ module.exports.csdl2openapi = function (
248
257
  description = containerSchema[voc.Core.LongDescription];
249
258
  }
250
259
  else {
251
- description = ('This service is located at [' + serviceRoot + '/]('
252
- + serviceRoot.replace(/\(/g, '%28').replace(/\)/g, '%29') + '/)');
260
+ description = ('This service is located at [' + serviceRoot + '/]('
261
+ + serviceRoot.replace(/\(/g, '%28').replace(/\)/g, '%29') + '/)');
253
262
  }
254
263
  description += (diagram ? getResourceDiagram(csdl, entityContainer) : '');
255
264
  let title;
@@ -266,7 +275,7 @@ module.exports.csdl2openapi = function (
266
275
  title = 'OData CSDL document';
267
276
  }
268
277
  return {
269
- title: title,
278
+ title: title,
270
279
  description: csdl.$EntityContainer ? description : '',
271
280
  version: containerSchema[voc.Core.SchemaVersion] || ''
272
281
  };
@@ -437,7 +446,7 @@ module.exports.csdl2openapi = function (
437
446
  throw new Error(`The input server object is invalid.`);
438
447
  }
439
448
 
440
- if(!servers.length){
449
+ if (!servers.length) {
441
450
  throw new Error(`The input server object should be an array.`);
442
451
  }
443
452
  } else {
@@ -712,7 +721,7 @@ module.exports.csdl2openapi = function (
712
721
  tags: [sourceName],
713
722
  parameters: [],
714
723
  responses: response(200, 'Retrieved ' + (byKey ? pluralize.singular(lname) : lname), { $Type: element.$Type, $Collection: collection },
715
- byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses)
724
+ byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses)
716
725
  };
717
726
  const deltaSupported = element[voc.Capabilities.ChangeTracking] && element[voc.Capabilities.ChangeTracking].Supported;
718
727
  if (!byKey && deltaSupported) {
@@ -1412,7 +1421,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
1412
1421
  summary: actionImport[voc.Core.Description] || overload[voc.Core.Description] || 'Invoke action ' + name,
1413
1422
  tags: [overload[voc.Common.Label] || sourceName || 'Service Operations'],
1414
1423
  responses: overload.$ReturnType ? response(200, "Success", overload.$ReturnType, overload[voc.Capabilities.OperationRestrictions]?.ErrorResponses)
1415
- : response(204, "Success", undefined, overload[voc.Capabilities.OperationRestrictions]?.ErrorResponses),
1424
+ : response(204, "Success", undefined, overload[voc.Capabilities.OperationRestrictions]?.ErrorResponses),
1416
1425
  }
1417
1426
  };
1418
1427
  const description = actionImport[voc.Core.LongDescription] || overload[voc.Core.LongDescription];
@@ -1599,7 +1608,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
1599
1608
  * @param {object} type Response type object
1600
1609
  * @param {array} errors Array of operation-specific status codes with descriptions
1601
1610
  */
1602
- function response(code, description, type, errors) {
1611
+ function response(code, description, type, errors) {
1603
1612
  const r = {};
1604
1613
  r[code] = {
1605
1614
  description: description
@@ -1829,6 +1838,8 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
1829
1838
  if (Object.keys(schemaProperties).length > 0)
1830
1839
  schemas[schemaName].properties = schemaProperties;
1831
1840
 
1841
+ odmExtensions(type, schemas[schemaName]);
1842
+
1832
1843
  if (suffix === SUFFIX.create && required.length > 0)
1833
1844
  schemas[schemaName].required = [...new Set(required)];
1834
1845
 
@@ -1846,6 +1857,17 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
1846
1857
  }
1847
1858
  }
1848
1859
 
1860
+ /**
1861
+ * Add ODM extensions to OpenAPI schema for a structured type
1862
+ * @param {object} type Structured type
1863
+ * @param {object} schema OpenAPI schema to augment
1864
+ */
1865
+ function odmExtensions(type, schema) {
1866
+ for (const [annotation, openApiExtension] of Object.entries(ODM_ANNOTATIONS)) {
1867
+ if (type[annotation]) schema[openApiExtension] = type[annotation];
1868
+ }
1869
+ }
1870
+
1849
1871
  /**
1850
1872
  * Collect all properties of a structured type along the inheritance hierarchy
1851
1873
  * @param {object} type Structured type
@@ -2022,7 +2044,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
2022
2044
  break;
2023
2045
  case 'Edm.Decimal':
2024
2046
  s = {
2025
- anyOf: [{ type: 'number', format: 'decimal'}, { type: 'string' }],
2047
+ anyOf: [{ type: 'number', format: 'decimal' }, { type: 'string' }],
2026
2048
  example: 0
2027
2049
  };
2028
2050
  // eslint-disable-next-line no-case-declarations
@@ -2164,13 +2186,13 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
2164
2186
  if (forFunction) {
2165
2187
  if (s.example && typeof s.example === "string") {
2166
2188
  s.example = `${pathValuePrefix(element.$Type)}${s.example
2167
- }${pathValueSuffix(element.$Type)}`;
2189
+ }${pathValueSuffix(element.$Type)} `;
2168
2190
  }
2169
2191
  if (s.pattern) {
2170
2192
  const pre = pathValuePrefix(element.$Type);
2171
2193
  const suf = pathValueSuffix(element.$Type);
2172
- s.pattern = s.pattern.replace(/^\^/, `^${pre}(`);
2173
- s.pattern = s.pattern.replace(/\$$/, `)${suf}$`);
2194
+ s.pattern = s.pattern.replace(/^\^/, `^ ${pre} (`);
2195
+ s.pattern = s.pattern.replace(/\$$/, `)${suf} $`);
2174
2196
  } else if (!element.$Type || element.$Type === "Edm.String") {
2175
2197
  s.pattern = "^'([^']|'')*'$";
2176
2198
  }
@@ -2388,4 +2410,4 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
2388
2410
  return !name.startsWith('$') && !name.includes('@');
2389
2411
  }
2390
2412
 
2391
- };
2413
+ };
@@ -15,7 +15,9 @@ const messages = {
15
15
  INCORRECT_INPUT_NAMESPACES: 'Invalid value for --include-namespaces option. Specify namespaces as comma-separated list or "*"',
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
- INVALID_OPENAPI_FILE: 'The OpenAPI file is not valid. Specify the correct OpenAPI file.'
18
+ INVALID_OPENAPI_FILE: 'The OpenAPI file is not valid. Specify the correct OpenAPI file.',
19
+ UNRESOLVED_TYPE: 'Could not resolve the type: ',
20
+ NO_RETURN_TYPE: 'No ReturnType found in Function: '
19
21
  };
20
22
 
21
23
  function getMessages() {
@@ -2198,7 +2198,7 @@ class ReturnTypeExpression extends Expression {
2198
2198
  const target = {};
2199
2199
  this.setTarget(target);
2200
2200
 
2201
- this.buildTypeCollectionAndFacets('$Type', '$Nullable', '$Precision', '$Scale');
2201
+ this.buildTypeCollectionAndFacets('$Type', '$MaxLength', '$Nullable', '$Precision', '$Scale');
2202
2202
  super.interpret();
2203
2203
  return { $ReturnType: target };
2204
2204
  }