@sap/cds-compiler 4.1.2 → 4.2.2

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 (74) hide show
  1. package/CHANGELOG.md +101 -1
  2. package/bin/cdsc.js +6 -3
  3. package/doc/CHANGELOG_BETA.md +5 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +2 -2
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +24 -24
  8. package/lib/base/message-registry.js +41 -6
  9. package/lib/base/messages.js +7 -0
  10. package/lib/base/model.js +37 -8
  11. package/lib/checks/elements.js +11 -10
  12. package/lib/checks/manyNavigations.js +33 -0
  13. package/lib/checks/onConditions.js +5 -2
  14. package/lib/checks/queryNoDbArtifacts.js +2 -3
  15. package/lib/checks/selectItems.js +4 -55
  16. package/lib/checks/utils.js +3 -2
  17. package/lib/checks/validator.js +3 -1
  18. package/lib/compiler/.eslintrc.json +2 -1
  19. package/lib/compiler/assert-consistency.js +27 -24
  20. package/lib/compiler/base.js +6 -2
  21. package/lib/compiler/builtins.js +34 -34
  22. package/lib/compiler/checks.js +179 -208
  23. package/lib/compiler/classes.js +2 -2
  24. package/lib/compiler/cycle-detector.js +6 -6
  25. package/lib/compiler/define.js +66 -45
  26. package/lib/compiler/extend.js +81 -72
  27. package/lib/compiler/finalize-parse-cdl.js +26 -26
  28. package/lib/compiler/generate.js +61 -45
  29. package/lib/compiler/index.js +47 -49
  30. package/lib/compiler/kick-start.js +8 -7
  31. package/lib/compiler/moduleLayers.js +1 -1
  32. package/lib/compiler/populate.js +42 -35
  33. package/lib/compiler/propagator.js +6 -6
  34. package/lib/compiler/resolve.js +170 -126
  35. package/lib/compiler/shared.js +122 -45
  36. package/lib/compiler/tweak-assocs.js +93 -40
  37. package/lib/compiler/utils.js +15 -12
  38. package/lib/edm/.eslintrc.json +40 -1
  39. package/lib/edm/annotations/genericTranslation.js +721 -707
  40. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  41. package/lib/edm/csn2edm.js +389 -378
  42. package/lib/edm/edm.js +678 -772
  43. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  44. package/lib/edm/edmInboundChecks.js +29 -27
  45. package/lib/edm/edmPreprocessor.js +686 -646
  46. package/lib/edm/edmUtils.js +277 -296
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +1 -1
  49. package/lib/gen/languageParser.js +1253 -1276
  50. package/lib/json/from-csn.js +34 -4
  51. package/lib/json/to-csn.js +4 -4
  52. package/lib/language/language.g4 +2 -5
  53. package/lib/main.d.ts +61 -1
  54. package/lib/model/csnUtils.js +31 -2
  55. package/lib/model/revealInternalProperties.js +1 -1
  56. package/lib/modelCompare/compare.js +37 -2
  57. package/lib/modelCompare/utils/filter.js +1 -1
  58. package/lib/optionProcessor.js +15 -3
  59. package/lib/render/toCdl.js +30 -4
  60. package/lib/render/toSql.js +5 -9
  61. package/lib/render/utils/common.js +8 -6
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/cdsPersistence.js +1 -1
  64. package/lib/transform/db/constraints.js +47 -17
  65. package/lib/transform/db/expansion.js +121 -47
  66. package/lib/transform/db/flattening.js +75 -7
  67. package/lib/transform/forOdata.js +4 -1
  68. package/lib/transform/forRelationalDB.js +80 -62
  69. package/lib/transform/localized.js +91 -54
  70. package/lib/transform/transformUtils.js +9 -10
  71. package/lib/utils/file.js +7 -7
  72. package/lib/utils/moduleResolve.js +210 -121
  73. package/lib/utils/objectUtils.js +1 -1
  74. package/package.json +5 -5
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+
2
3
  const { isEdmPropertyRendered, isBuiltinType } = require('../../model/csnUtils');
3
4
  const edmUtils = require('../edmUtils.js');
4
5
  const oDataDictionary = require('../../gen/Dictionary.json');
@@ -37,115 +38,115 @@ const { isBetaEnabled, setProp } = require('../../base/model.js');
37
38
  */
38
39
 
39
40
  const vocabularyDefinitions = {
40
- 'Aggregation': {
41
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.xml' },
42
- 'inc': { Alias: 'Aggregation', Namespace: 'Org.OData.Aggregation.V1' },
43
- 'int': { filename: 'Aggregation.xml' }
41
+ Aggregation: {
42
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.xml' },
43
+ inc: { Alias: 'Aggregation', Namespace: 'Org.OData.Aggregation.V1' },
44
+ int: { filename: 'Aggregation.xml' },
44
45
  },
45
- 'Analytics': {
46
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Analytics.xml' },
47
- 'inc': { Alias: 'Analytics', Namespace: 'com.sap.vocabularies.Analytics.v1' },
48
- 'int': { filename: 'Analytics.xml' }
46
+ Analytics: {
47
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Analytics.xml' },
48
+ inc: { Alias: 'Analytics', Namespace: 'com.sap.vocabularies.Analytics.v1' },
49
+ int: { filename: 'Analytics.xml' },
49
50
  },
50
- 'Authorization': {
51
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Authorization.V1.xml' },
52
- 'inc': { Alias: 'Authorization', Namespace: 'Org.OData.Authorization.V1' },
53
- 'int': { filename: 'Authorization.xml' }
51
+ Authorization: {
52
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Authorization.V1.xml' },
53
+ inc: { Alias: 'Authorization', Namespace: 'Org.OData.Authorization.V1' },
54
+ int: { filename: 'Authorization.xml' },
54
55
  },
55
- 'Capabilities': {
56
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml' },
57
- 'inc': { Alias: 'Capabilities', Namespace: 'Org.OData.Capabilities.V1' },
58
- 'int': { filename: 'Capabilities.xml' }
56
+ Capabilities: {
57
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml' },
58
+ inc: { Alias: 'Capabilities', Namespace: 'Org.OData.Capabilities.V1' },
59
+ int: { filename: 'Capabilities.xml' },
59
60
  },
60
- 'CodeList': {
61
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/CodeList.xml' },
62
- 'inc': { Alias: 'CodeList', Namespace: 'com.sap.vocabularies.CodeList.v1' },
63
- 'int': { filename: 'CodeList.xml' }
61
+ CodeList: {
62
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/CodeList.xml' },
63
+ inc: { Alias: 'CodeList', Namespace: 'com.sap.vocabularies.CodeList.v1' },
64
+ int: { filename: 'CodeList.xml' },
64
65
  },
65
- 'Common': {
66
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Common.xml' },
67
- 'inc': { Alias: 'Common', Namespace: 'com.sap.vocabularies.Common.v1' },
68
- 'int': { filename: 'Common.xml' }
66
+ Common: {
67
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Common.xml' },
68
+ inc: { Alias: 'Common', Namespace: 'com.sap.vocabularies.Common.v1' },
69
+ int: { filename: 'Common.xml' },
69
70
  },
70
- 'Communication': {
71
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Communication.xml' },
72
- 'inc': { Alias: 'Communication', Namespace: 'com.sap.vocabularies.Communication.v1' },
73
- 'int': { filename: 'Communication.xml' }
71
+ Communication: {
72
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Communication.xml' },
73
+ inc: { Alias: 'Communication', Namespace: 'com.sap.vocabularies.Communication.v1' },
74
+ int: { filename: 'Communication.xml' },
74
75
  },
75
- 'Core': {
76
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml' },
77
- 'inc': { Alias: 'Core', Namespace: 'Org.OData.Core.V1' },
78
- 'int': { filename: 'Core.xml' }
76
+ Core: {
77
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml' },
78
+ inc: { Alias: 'Core', Namespace: 'Org.OData.Core.V1' },
79
+ int: { filename: 'Core.xml' },
79
80
  },
80
- 'DataIntegration': {
81
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/DataIntegration.xml' },
82
- 'inc': { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
83
- 'int': { filename: 'DataIntegration.xml' }
81
+ DataIntegration: {
82
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/DataIntegration.xml' },
83
+ inc: { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
84
+ int: { filename: 'DataIntegration.xml' },
84
85
  },
85
- 'Graph': {
86
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
87
- 'inc': { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
88
- 'int': { filename: 'Graph.xml' }
86
+ Graph: {
87
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
88
+ inc: { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
89
+ int: { filename: 'Graph.xml' },
89
90
  },
90
- 'Hierarchy': {
91
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Hierarchy.xml' },
92
- 'inc': { Alias: 'Hierarchy', Namespace: 'com.sap.vocabularies.Hierarchy.v1' },
93
- 'int': { filename: 'Hierarchy.xml' }
91
+ Hierarchy: {
92
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Hierarchy.xml' },
93
+ inc: { Alias: 'Hierarchy', Namespace: 'com.sap.vocabularies.Hierarchy.v1' },
94
+ int: { filename: 'Hierarchy.xml' },
94
95
  },
95
- 'HTML5': {
96
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml' },
97
- 'inc': { Alias: 'HTML5', Namespace: 'com.sap.vocabularies.HTML5.v1' },
98
- 'int': { filename: 'HTML5.xml' }
96
+ HTML5: {
97
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml' },
98
+ inc: { Alias: 'HTML5', Namespace: 'com.sap.vocabularies.HTML5.v1' },
99
+ int: { filename: 'HTML5.xml' },
99
100
  },
100
- 'JSON': {
101
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.JSON.V1.xml' },
102
- 'inc': { Alias: 'JSON', Namespace: 'Org.OData.JSON.V1' },
103
- 'int': { filename: 'JSON.xml' }
101
+ JSON: {
102
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.JSON.V1.xml' },
103
+ inc: { Alias: 'JSON', Namespace: 'Org.OData.JSON.V1' },
104
+ int: { filename: 'JSON.xml' },
104
105
  },
105
- 'Measures': {
106
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml' },
107
- 'inc': { Alias: 'Measures', Namespace: 'Org.OData.Measures.V1' },
108
- 'int': { filename: 'Measures.xml' }
106
+ Measures: {
107
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml' },
108
+ inc: { Alias: 'Measures', Namespace: 'Org.OData.Measures.V1' },
109
+ int: { filename: 'Measures.xml' },
109
110
  },
110
- 'ODM': {
111
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/ODM.xml' },
112
- 'inc': { Alias: 'ODM', Namespace: 'com.sap.vocabularies.ODM.v1' },
113
- 'int': { filename: 'ODM.xml' }
111
+ ODM: {
112
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/ODM.xml' },
113
+ inc: { Alias: 'ODM', Namespace: 'com.sap.vocabularies.ODM.v1' },
114
+ int: { filename: 'ODM.xml' },
114
115
  },
115
- 'Offline': {
116
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Offline.xml' },
117
- 'inc': { Alias: 'Offline', Namespace: 'com.sap.vocabularies.Offline.v1' },
118
- 'int': { filename: 'Offline.xml' }
116
+ Offline: {
117
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Offline.xml' },
118
+ inc: { Alias: 'Offline', Namespace: 'com.sap.vocabularies.Offline.v1' },
119
+ int: { filename: 'Offline.xml' },
119
120
  },
120
- 'PDF': {
121
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/PDF.xml' },
122
- 'inc': { Alias: 'PDF', Namespace: 'com.sap.vocabularies.PDF.v1' },
123
- 'int': { filename: 'PDF.xml' }
121
+ PDF: {
122
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/PDF.xml' },
123
+ inc: { Alias: 'PDF', Namespace: 'com.sap.vocabularies.PDF.v1' },
124
+ int: { filename: 'PDF.xml' },
124
125
  },
125
- 'PersonalData': {
126
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/PersonalData.xml' },
127
- 'inc': { Alias: 'PersonalData', Namespace: 'com.sap.vocabularies.PersonalData.v1' },
128
- 'int': { filename: 'PersonalData.xml' }
126
+ PersonalData: {
127
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/PersonalData.xml' },
128
+ inc: { Alias: 'PersonalData', Namespace: 'com.sap.vocabularies.PersonalData.v1' },
129
+ int: { filename: 'PersonalData.xml' },
129
130
  },
130
- 'Repeatability': {
131
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Repeatability.V1.xml' },
132
- 'inc': { Alias: 'Repeatability', Namespace: 'Org.OData.Repeatability.V1' },
133
- 'int': { filename: 'Repeatability.xml' }
131
+ Repeatability: {
132
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Repeatability.V1.xml' },
133
+ inc: { Alias: 'Repeatability', Namespace: 'Org.OData.Repeatability.V1' },
134
+ int: { filename: 'Repeatability.xml' },
134
135
  },
135
- 'Session': {
136
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Session.xml' },
137
- 'inc': { Alias: 'Session', Namespace: 'com.sap.vocabularies.Session.v1' },
138
- 'int': { filename: 'Session.xml' }
136
+ Session: {
137
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Session.xml' },
138
+ inc: { Alias: 'Session', Namespace: 'com.sap.vocabularies.Session.v1' },
139
+ int: { filename: 'Session.xml' },
139
140
  },
140
- 'UI': {
141
- 'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/UI.xml' },
142
- 'inc': { Alias: 'UI', Namespace: 'com.sap.vocabularies.UI.v1' },
143
- 'int': { filename: 'UI.xml' }
141
+ UI: {
142
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/UI.xml' },
143
+ inc: { Alias: 'UI', Namespace: 'com.sap.vocabularies.UI.v1' },
144
+ int: { filename: 'UI.xml' },
144
145
  },
145
- 'Validation': {
146
- 'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.xml' },
147
- 'inc': { Alias: 'Validation', Namespace: 'Org.OData.Validation.V1' },
148
- 'int': { filename: 'Validation.xml' }
146
+ Validation: {
147
+ ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.xml' },
148
+ inc: { Alias: 'Validation', Namespace: 'Org.OData.Validation.V1' },
149
+ int: { filename: 'Validation.xml' },
149
150
  },
150
151
  /* unvalidated vocabularies below here:
151
152
  A vocabulary is unvalidated if it doesn't have an int.filename property as this indicates that
@@ -164,29 +165,27 @@ Object.entries(vocabularyDefinitions).forEach(([n, v]) => {
164
165
  vocabularyDefinitions[v.inc.Namespace] = vocabularyDefinitions[n];
165
166
  });
166
167
  */
167
- /**************************************************************************************************
168
+ /** ************************************************************************************************
168
169
  * csn2annotationEdm
169
170
  *
170
171
  * options:
171
172
  * v - array with two boolean entries, first is for v2, second is for v4
172
173
  * dictReplacement: for test purposes, replaces the standard oDataDictionary
173
174
  */
174
- function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
175
- Edm = undefined, options=undefined, messageFunctions=undefined, mergedVocDefs=vocabularyDefinitions) {
175
+ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
176
+ Edm = undefined, options = undefined, messageFunctions = undefined, mergedVocDefs = vocabularyDefinitions ) {
176
177
  // global variable where we store all the generated annotations
177
- const g_annosArray = [];
178
+ const gAnnosArray = [];
178
179
 
179
180
  const { message } = messageFunctions;
180
181
 
181
182
  const [ userDefinedTermDict, allKnownVocabularies ] = createUserDefinedTermDictionary();
182
183
 
183
184
  allKnownVocabularies.push(...Object.keys(mergedVocDefs));
184
- allKnownVocabularies.sort((a,b) => b.length-a.length);
185
- const whatsMyTermNamespace = function(anno) {
186
- return allKnownVocabularies.reduce((rc, ns) => !rc && anno && anno.startsWith('@' + ns + '.') ? ns : rc, undefined);
187
- }
185
+ allKnownVocabularies.sort((a, b) => b.length - a.length);
186
+ const whatsMyTermNamespace = anno => allKnownVocabularies.reduce((rc, ns) => (!rc && anno && anno.startsWith(`@${ns}.`) ? ns : rc), undefined);
188
187
 
189
- // Static dynamic expression dictionary, loaded with Edm creators
188
+ // Static dynamic expression dictionary, loaded with Edm creators
190
189
  const [ dynamicExpressions, dynamicExpressionNames ] = initEdmJson();
191
190
 
192
191
  // annotation preprocessing
@@ -194,25 +193,25 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
194
193
 
195
194
  // we take note of which vocabularies are actually used in a service in order to avoid
196
195
  // producing useless references; reset everything to "unused"
197
- for(const n in mergedVocDefs) {
196
+ for (const n in mergedVocDefs) {
198
197
  mergedVocDefs[n].used = false;
199
198
  delete mergedVocDefs[n].$ignore;
200
199
  }
201
200
 
202
201
  // These vocabularies are always added for the runtimes
203
- mergedVocDefs['Common'].used = true;
204
- mergedVocDefs['Core'].used = true;
202
+ mergedVocDefs.Common.used = true;
203
+ mergedVocDefs.Core.used = true;
205
204
 
206
205
  const vocDef = mergedVocDefs[serviceName];
207
- if(vocDef && vocDef.$optVocRef) {
206
+ if (vocDef && vocDef.$optVocRef) {
208
207
  setProp(vocDef, '$ignore', true);
209
208
  message('odata-anno-vocref', null,
210
- { name: serviceName, '#': 'service' } );
209
+ { name: serviceName, '#': 'service' } );
211
210
  }
212
211
 
213
212
  // provide functions for dictionary lookup
214
213
  // use closure to avoid making "dict" and "experimental" global variables
215
- const { getDictTerm, getDictType } = function(){
214
+ const { getDictTerm, getDictType } = (function createDictGetters() {
216
215
  const dict = options.dictReplacement || oDataDictionary; // tests can set different dictionary via options
217
216
  const experimental = {}; // take note of all experimental annos that have been used
218
217
  const deprecated = {}; // take note of all deprecated annos that have been used
@@ -221,27 +220,27 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
221
220
  // called to look-up a term in the dictionary
222
221
  // in addition: - note usage of the respective vocabulary
223
222
  // - issue a warning if the term is flagged as "experimental"
224
- getDictTerm: function(termName, msg) {
223
+ getDictTerm(termName, msg) {
225
224
  const dictTerm = (dict.terms[termName] ||
226
- userDefinedTermDict.terms[serviceName + '.' + termName] ||
225
+ userDefinedTermDict.terms[`${serviceName}.${termName}`] ||
227
226
  userDefinedTermDict.terms[termName]);
228
227
  // register vocabulary usage if possible
229
228
  const vocName = termName.slice(0, termName.indexOf('.'));
230
- const vocDef = mergedVocDefs[vocName];
231
- if(vocDef && !vocDef.$ignore)
232
- vocDef.used = true;
233
- else if(dictTerm?.$myServiceRoot &&
229
+ const myVocDef = mergedVocDefs[vocName];
230
+ if (myVocDef && !myVocDef.$ignore)
231
+ myVocDef.used = true;
232
+ else if (dictTerm?.$myServiceRoot &&
234
233
  userDefinedTermDict.xrefs[dictTerm?.$myServiceRoot])
235
234
  userDefinedTermDict.xrefs[dictTerm.$myServiceRoot].used = true;
236
235
  if (dictTerm) {
237
236
  // issue message for usage of experimental Terms, but only once per Term
238
- if (dictTerm['$experimental'] && !experimental[termName]) {
237
+ if (dictTerm.$experimental && !experimental[termName]) {
239
238
  message('odata-anno-dict', msg.location, { anno: msg.anno(), '#': 'experimental' });
240
239
  experimental[termName] = true;
241
240
  }
242
- if (dictTerm['$deprecated'] && !deprecated[termName]) {
241
+ if (dictTerm.$deprecated && !deprecated[termName]) {
243
242
  message('odata-anno-def', msg.location,
244
- { anno: msg.anno(), depr: dictTerm['$deprecationText'], '#': 'deprecated' });
243
+ { anno: msg.anno(), depr: dictTerm.$deprecationText, '#': 'deprecated' });
245
244
  deprecated[termName] = true;
246
245
  }
247
246
  }
@@ -249,44 +248,44 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
249
248
  },
250
249
  // called to look-up a type in the dictionary
251
250
  // in addition, note usage of the respective vocabulary
252
- getDictType: function (typeName) {
253
- let dictType = (dict.types[typeName] ||
254
- userDefinedTermDict.types[serviceName + '.' + typeName] ||
251
+ getDictType(typeName) {
252
+ const dictType = (dict.types[typeName] ||
253
+ userDefinedTermDict.types[`${serviceName}.${typeName}`] ||
255
254
  userDefinedTermDict.types[typeName]);
256
255
  if (dictType) {
257
256
  // register usage of vocabulary
258
257
  const vocName = typeName.slice(0, typeName.indexOf('.'));
259
- const vocDef = mergedVocDefs[vocName];
260
- if(vocDef && !vocDef.$ignore)
261
- vocDef.used = true;
258
+ const myVocDef = mergedVocDefs[vocName];
259
+ if (myVocDef && !myVocDef.$ignore)
260
+ myVocDef.used = true;
262
261
  }
263
262
  return dictType;
264
- }
265
- }
266
- }();
263
+ },
264
+ };
265
+ }());
267
266
 
268
- const v = options.v;
267
+ const { v } = options;
269
268
 
270
269
  // Copy annotations from origin to parameter entity if it's
271
270
  // qualified with #$parameters or if its applicable to an EntitySet or Singleton
272
271
  forEachDefinition(reqDefs, (object) => {
273
- if(object.$isParamEntity && object._origin) {
274
- for(const attr in object._origin) {
272
+ if (object.$isParamEntity && object._origin) {
273
+ for (const attr in object._origin) {
275
274
  if (attr[0] === '@') {
276
275
  const [ prefix, innerAnnotation ] = attr.split('.@');
277
276
  const ns = whatsMyTermNamespace(prefix);
278
- if(ns) {
279
- const steps = prefix.replace('@' + ns + '.', '').split('.');
277
+ if (ns) {
278
+ const steps = prefix.replace(`@${ns}.`, '').split('.');
280
279
  const paramAnnoParts = steps[0].split('#$parameters');
281
- const dictTerm = getDictTerm(ns + '.' + paramAnnoParts[0], options);
282
- if(paramAnnoParts.length > 1 || ['Singleton', 'EntitySet'].some(y => dictTerm?.AppliesTo?.includes(y))) {
283
- steps[0] = '@' + ns + '.' + paramAnnoParts.join('');
280
+ const dictTerm = getDictTerm(`${ns}.${paramAnnoParts[0]}`, options);
281
+ if (paramAnnoParts.length > 1 || [ 'Singleton', 'EntitySet' ].some(y => dictTerm?.AppliesTo?.includes(y))) {
282
+ steps[0] = `@${ns}.${paramAnnoParts.join('')}`;
284
283
  let newAnno = steps.join('.');
285
- if(innerAnnotation)
286
- newAnno += '.@' + innerAnnotation;
284
+ if (innerAnnotation)
285
+ newAnno += `.@${innerAnnotation}`;
287
286
  edmUtils.assignAnnotation(object, newAnno, object._origin[attr]);
288
287
  // delete original annotation only if it was qualified with $parameters
289
- if(paramAnnoParts.length > 1)
288
+ if (paramAnnoParts.length > 1)
290
289
  delete object._origin[attr];
291
290
  }
292
291
  }
@@ -301,8 +300,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
301
300
  // Note: we assume that all objects ly flat in the service, i.e. objName always
302
301
  // looks like <service name, can contain dots>.<id>
303
302
  forEachDefinition(reqDefs, (object, objName) => {
304
- if (objName === serviceName || objName.startsWith(serviceName + '.')) {
305
-
303
+ if (objName === serviceName || objName.startsWith(`${serviceName}.`)) {
306
304
  const location = [ 'definitions', objName ];
307
305
  if (object.kind === 'action' || object.kind === 'function') {
308
306
  handleAction(objName, object, null, location);
@@ -322,14 +320,14 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
322
320
  // filter out empty <Annotations...> elements
323
321
  // add references for the used vocabularies
324
322
  return {
325
- annos: g_annosArray,
326
- usedVocabularies: Object.values(mergedVocDefs).filter(v => v.used),
327
- xrefs: Object.values(userDefinedTermDict.xrefs).filter(v => v.used).map(v => v.$myServiceRoot)
323
+ annos: gAnnosArray,
324
+ usedVocabularies: Object.values(mergedVocDefs).filter(voc => voc.used),
325
+ xrefs: Object.values(userDefinedTermDict.xrefs).filter(voc => voc.used).map(voc => voc.$myServiceRoot),
328
326
  };
329
327
 
330
- //-------------------------------------------------------------------------------------------------
331
- //-------------------------------------------------------------------------------------------------
332
- //-------------------------------------------------------------------------------------------------
328
+ //-------------------------------------------------------------------------------------------------
329
+ //-------------------------------------------------------------------------------------------------
330
+ //-------------------------------------------------------------------------------------------------
333
331
 
334
332
  // helper to determine the OData version
335
333
  // TODO: improve option handling
@@ -337,7 +335,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
337
335
  return v && v[0];
338
336
  }
339
337
 
340
- /*
338
+ /*
341
339
  Mapping annotated thing in cds/csn => annotated thing in edmx:
342
340
 
343
341
  carrier: the annotated thing in cds, can be: service, entity, structured type, element of entity or structured type,
@@ -375,17 +373,16 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
375
373
  */
376
374
 
377
375
 
378
-
379
-
380
376
  // handle the annotations of the elements of an object
381
377
  // in: objname : name of the object
382
378
  // object : the object itself
383
- function handleElements(objname, object, location) {
384
- if (!object.elements) return;
385
- Object.entries(object.elements).forEach(([elemName, element]) => {
379
+ function handleElements( objname, object, location ) {
380
+ if (!object.elements)
381
+ return;
382
+ Object.entries(object.elements).forEach(([ elemName, element ]) => {
386
383
  // determine the name of the target in the resulting edm
387
384
  // for non-assoc element, this simply is "<objectName>/<elementName>"
388
- const edmTargetName = objname + '/' + elemName;
385
+ const edmTargetName = `${objname}/${elemName}`;
389
386
  const eLocation = [ ...location, 'elements', elemName ];
390
387
  handleAnnotations(edmTargetName, element, eLocation);
391
388
  });
@@ -402,16 +399,16 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
402
399
  // handle the annotations of cObject's (an entity) bound actions/functions and their parameters
403
400
  // in: cObjectname : qualified name of the object that holds the actions
404
401
  // cObject : the object itself
405
- function handleBoundActions(cObjectname, cObject, location) {
406
- if(!cObject.actions) return;
402
+ function handleBoundActions( cObjectname, cObject, location ) {
403
+ if (!cObject.actions)
404
+ return;
407
405
  // get service name: remove last part of the object name
408
406
  // only works if all objects ly flat in the service
409
- const nameParts = cObjectname.split('.')
407
+ const nameParts = cObjectname.split('.');
410
408
  const entityName = nameParts.pop();
411
- const serviceName = nameParts.join('.');
412
409
 
413
- Object.entries(cObject.actions).forEach(([n, action]) => {
414
- const actionName = serviceName + '.' + (isV2() ? entityName + '_' : '') + n;
410
+ Object.entries(cObject.actions).forEach(([ n, action ]) => {
411
+ const actionName = `${serviceName}.${isV2() ? `${entityName}_` : ''}${n}`;
415
412
  handleAction(actionName, action, cObjectname, [ ...location, 'actions', n ]);
416
413
  });
417
414
  }
@@ -421,12 +418,12 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
421
418
  // in: cActionName : qualified name of the action
422
419
  // cAction : the action object
423
420
  // entityNameIfBound : qualified name of entity if bound action/function
424
- function handleAction(cActionName, cAction, entityNameIfBound, location) {
421
+ function handleAction( cActionName, cAction, entityNameIfBound, location ) {
425
422
  let actionName = cActionName;
426
423
  if (isV2()) { // Replace up to last dot with <serviceName>.EntityContainer
427
424
  const lastDotIndex = actionName.lastIndexOf('.');
428
425
  if (lastDotIndex > -1)
429
- actionName = serviceName + '.EntityContainer/' + actionName.substring(lastDotIndex + 1);
426
+ actionName = `${serviceName}.EntityContainer/${actionName.substring(lastDotIndex + 1)}`;
430
427
  }
431
428
  else { // add parameter type list
432
429
  actionName += relParList();
@@ -434,17 +431,17 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
434
431
 
435
432
  handleAnnotations(actionName, cAction, location);
436
433
 
437
- if(cAction.params) {
438
- Object.entries(cAction.params).forEach(([n, p]) => {
439
- const edmTargetName = actionName + '/' + n;
434
+ if (cAction.params) {
435
+ Object.entries(cAction.params).forEach(([ n, p ]) => {
436
+ const edmTargetName = `${actionName}/${n}`;
440
437
  handleAnnotations(edmTargetName, p, [ ...location, 'params', n ]);
441
438
  });
442
439
  }
443
- if(cAction.returns) {
444
- const edmTargetName = actionName + '/$ReturnType';
440
+ if (cAction.returns) {
441
+ const edmTargetName = `${actionName}/$ReturnType`;
445
442
  setProp(cAction.returns, '$appliesToReturnType', true);
446
443
  handleAnnotations(edmTargetName, cAction.returns, [ ...location, 'returns' ]);
447
- delete cAction.returns['$appliesToReturnType'];
444
+ delete cAction.returns.$appliesToReturnType;
448
445
  }
449
446
 
450
447
  function relParList() {
@@ -452,40 +449,37 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
452
449
  const params = [];
453
450
  if (entityNameIfBound) {
454
451
  // If this is an action and has an explicit binding parameter add it here
455
- if(cAction.$bindingParam && cAction.kind === 'action') {
456
- params.push(cAction.$bindingParam.items ? 'Collection(' + entityNameIfBound + ')' : entityNameIfBound);
457
- }
452
+ if (cAction.$bindingParam && cAction.kind === 'action')
453
+ params.push(cAction.$bindingParam.items ? `Collection(${entityNameIfBound})` : entityNameIfBound);
454
+
458
455
  // If action/function has no explicit binding parameter add it here
459
- else if (!cAction.$bindingParam) {
460
- params.push(cAction['@cds.odata.bindingparameter.collection'] ? 'Collection(' + entityNameIfBound + ')' : entityNameIfBound);
461
- }
456
+ else if (!cAction.$bindingParam)
457
+ params.push(cAction['@cds.odata.bindingparameter.collection'] ? `Collection(${entityNameIfBound})` : entityNameIfBound);
462
458
  }
463
459
  // In case this is a function the explicit binding parameter is part of
464
460
  // the functions params dictionary. Only for functions all parameters must
465
461
  // be listed in the annotation target
466
- if (cAction.kind === 'function') {
467
- if(cAction.params) {
468
- cAction.params && Object.values(cAction.params).forEach(p => {
469
- const isArrayType = !p.type && p.items && p.items.type;
470
- params.push(isArrayType ? 'Collection(' + mapType(p.items) + ')' : mapType(p));
471
- });
472
- }
462
+ if (cAction.kind === 'function' && cAction.params) {
463
+ Object.values(cAction.params).forEach((p) => {
464
+ const isArrayType = !p.type && p.items && p.items.type;
465
+ params.push(isArrayType ? `Collection(${mapType(p.items)})` : mapType(p));
466
+ });
473
467
  }
474
- return '(' + params.join(',') + ')';
468
+ return `(${params.join(',')})`;
475
469
 
476
- function mapType(p) {
477
- if(isBuiltinType(p.type))
478
- return edmUtils.mapCdsToEdmType(p, messageFunctions, false /*is only called for v4*/)
479
- else if(options.whatsMySchemaName) {
470
+ function mapType( p ) {
471
+ if (isBuiltinType(p.type)) {
472
+ return edmUtils.mapCdsToEdmType(p, messageFunctions, false /* is only called for v4 */);
473
+ }
474
+ else if (options.whatsMySchemaName) {
480
475
  const schemaName = options.whatsMySchemaName(p.type);
481
- // strip the service namespace of from a parameter type
482
- if(schemaName && schemaName !== options.serviceName)
483
- return p.type.replace(options.serviceName + '.', '');
476
+ // strip the service namespace of from a parameter type
477
+ if (schemaName && schemaName !== options.serviceName)
478
+ return p.type.replace(`${options.serviceName}.`, '');
484
479
  }
485
480
  return p.type;
486
481
  }
487
482
  }
488
-
489
483
  }
490
484
 
491
485
 
@@ -493,7 +487,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
493
487
  // edmTargetName : string, name of the target in edm
494
488
  // carrier: object, the annotated cds thing, contains all the annotations
495
489
  // as properties with names starting with @
496
- function handleAnnotations(edmTargetName, carrier, location) {
490
+ function handleAnnotations( edmTargetName, carrier, location ) {
497
491
  // collect the names of the carrier's annotation properties
498
492
  // keep only those annotations that - start with a known vocabulary name
499
493
  // - have a value other than null
@@ -503,20 +497,22 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
503
497
  // if the carrier is a media stream element in V2
504
498
  // do nothing
505
499
 
506
- if(!isEdmPropertyRendered(carrier, options) ||
507
- (isV2() && (edmUtils.isDerivedType(carrier)))) {
500
+ if (!isEdmPropertyRendered(carrier, options) ||
501
+ (isV2() && (edmUtils.isDerivedType(carrier))))
508
502
  return;
509
- }
503
+
510
504
 
511
505
  // Filter unknown toplevel annotations
512
506
  // Final filtering of all annotations is done in handleTerm
513
507
 
514
508
  let knownAnnos = filterKnownAnnotations();
515
- if (knownAnnos.length === 0) return;
509
+ if (knownAnnos.length === 0)
510
+ return;
516
511
 
517
- if(rewriteInnerAnnotations()) {
512
+ if (rewriteInnerAnnotations()) {
518
513
  knownAnnos = filterKnownAnnotations();
519
- if (knownAnnos.length === 0) return;
514
+ if (knownAnnos.length === 0)
515
+ return;
520
516
  }
521
517
 
522
518
  const prefixTree = createPrefixTree();
@@ -529,12 +525,12 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
529
525
  // later when looking at single annotations
530
526
 
531
527
  const [
532
- stdEdmTargetName, // either the schema path or the EntityContainer itself
533
- hasAlternativeCarrier, // is the alternative annotation target available in the EDM?
534
- alternativeEdmTargetName, // EntitySet path name
535
- testToStandardEdmTarget, // if true, assign to standard Edm Target
536
- testToAlternativeEdmTarget, // if true, assign to alternative Edm Target
537
- ] = initCarrierControlVars();
528
+ stdEdmTargetName, // either the schema path or the EntityContainer itself
529
+ hasAlternativeCarrier, // is the alternative annotation target available in the EDM?
530
+ alternativeEdmTargetName, // EntitySet path name
531
+ testToStandardEdmTarget, // if true, assign to standard Edm Target
532
+ testToAlternativeEdmTarget, // if true, assign to alternative Edm Target
533
+ ] = initCarrierControlVars();
538
534
 
539
535
  // collect produced Edm.Annotation nodes for various carriers
540
536
  const serviceAnnotations = [];
@@ -546,66 +542,66 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
546
542
 
547
543
  // Produce Edm.Annotations and attach collected Edm.Annotation(s) to the
548
544
  // envelope (or directly to the Schema)
549
- if(serviceAnnotations.length) {
550
- g_annosArray.push(...serviceAnnotations.filter(a=>a));
551
- }
552
- if(stdAnnotations.length) {
545
+ if (serviceAnnotations.length)
546
+ gAnnosArray.push(...serviceAnnotations.filter(a => a));
547
+
548
+ if (stdAnnotations.length) {
553
549
  const annotations = new Edm.Annotations(v, stdEdmTargetName); // used in closure
554
550
  annotations.append(...stdAnnotations);
555
- g_annosArray.push(annotations);
551
+ gAnnosArray.push(annotations);
556
552
  }
557
- if(alternativeAnnotations.length) {
553
+ if (alternativeAnnotations.length) {
558
554
  const annotations = new Edm.Annotations(v, alternativeEdmTargetName);
559
555
  annotations.append(...alternativeAnnotations);
560
- g_annosArray.push(annotations);
556
+ gAnnosArray.push(annotations);
561
557
  }
562
558
 
563
559
  function filterKnownAnnotations() {
564
560
  const annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
565
561
  const nullWhitelist = [ '@Core.OperationAvailable' ];
566
- const knownAnnos = annoNames.filter(n => {
562
+ const knownAnnosP = annoNames.filter((n) => {
567
563
  const tns = whatsMyTermNamespace(n);
568
564
  return tns &&
569
565
  (mergedVocDefs[tns] && !mergedVocDefs[tns].$ignore ||
570
566
  !mergedVocDefs[tns]);
571
567
  }).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
572
- if(isBetaEnabled(options, 'odataTerms')) {
568
+ if (isBetaEnabled(options, 'odataTerms')) {
573
569
  // Extend knownAnnos with the in-service term definitions
574
- annoNames.forEach(an => {
570
+ annoNames.forEach((an) => {
575
571
  const paths = an.slice(1).split('.');
576
572
  const hasNSPrefix = paths[0] === serviceName;
577
- if(!hasNSPrefix) {
573
+ if (!hasNSPrefix)
578
574
  paths.splice(0, 0, serviceName);
579
- }
580
- const fqName = '@' + paths.join('.');
575
+
576
+ const fqName = `@${paths.join('.')}`;
581
577
  const i = paths[1].indexOf('#');
582
578
  const termNameWithoutQualifiers = i > 0 ? paths[1].substring(0, i) : paths[1];
583
- const def = reqDefs.definitions[paths[0] + '.' + termNameWithoutQualifiers];
579
+ const def = reqDefs.definitions[`${paths[0]}.${termNameWithoutQualifiers}`];
584
580
  // if there is a term definition inside the service and the
585
581
  // annotation value is != null, then add the annotation to the list
586
582
  // of known annotations
587
- if(def?.kind === 'annotation' && carrier[an] !== null) {
583
+ if (def?.kind === 'annotation' && carrier[an] !== null) {
588
584
  // Subsequent annotation handler code expects that first path segment
589
585
  // is the Vocabulary namespace. The ad-hoc namespace is the service
590
586
  // name itself.
591
587
  // For service S an annotation assignment could be addressed
592
588
  // relative or absolute to the service @S.foo or @foo
593
- if(!hasNSPrefix) {
589
+ if (!hasNSPrefix) {
594
590
  carrier[fqName] = carrier[an];
595
591
  delete carrier[an];
596
592
  }
597
- knownAnnos.push(fqName);
593
+ knownAnnosP.push(fqName);
598
594
  }
599
595
  });
600
596
  }
601
- return knownAnnos;
597
+ return knownAnnosP;
602
598
  }
603
599
 
604
600
  // construct a function that is used to add an <Annotation ...> to the
605
601
  // respective collector array
606
602
  // this function is specific to the actual carrier, following the mapping rules given above
607
- function addAnnotation(annotation, appliesTo) {
608
- let rc=false;
603
+ function addAnnotation( annotation, appliesTo ) {
604
+ let rc = false;
609
605
  if (testToAlternativeEdmTarget && appliesTo && testToAlternativeEdmTarget(appliesTo)) {
610
606
  if (carrier.kind === 'service') {
611
607
  if (isV2()) {
@@ -614,98 +610,98 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
614
610
  }
615
611
  serviceAnnotations.push(annotation); // for target Schema: no <Annotations> element
616
612
  }
617
- else if(hasAlternativeCarrier) {
613
+ else if (hasAlternativeCarrier) {
618
614
  alternativeAnnotations.push(annotation);
619
615
  }
620
- rc=true;
616
+ rc = true;
621
617
  }
622
- if(testToStandardEdmTarget(appliesTo)) {
618
+ if (testToStandardEdmTarget(appliesTo)) {
623
619
  stdAnnotations.push(annotation);
624
- rc=true;
620
+ rc = true;
625
621
  }
626
622
  // Another crazy hack due to this crazy function:
627
623
  // If carrier is a managed association (has keys) and rc is false (annotation was not applicable)
628
624
  // return true to NOT trigger 'unapplicable' message message
629
- if(rc === false && carrier.target && carrier.keys && appliesTo.includes('Property'))
625
+ if (rc === false && carrier.target && carrier.keys && appliesTo.includes('Property'))
630
626
  rc = true;
631
627
  return rc;
632
628
  }
633
629
 
634
630
  function initCarrierControlVars() {
635
631
  // eslint-disable-next-line no-unused-vars
636
- let testToStandardEdmTarget = () => true; // if true, assign to standard Edm Target
637
- let stdEdmTargetName = edmTargetName;
638
- let alternativeEdmTargetName = null;
639
- let hasAlternativeCarrier = false; // is the alternative annotation target available in the EDM?
640
- let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
632
+ let testToStandardEdmTargetP = () => true; // if true, assign to standard Edm Target
633
+ let stdEdmTargetNameP = edmTargetName;
634
+ let alternativeEdmTargetNameP = null;
635
+ let hasAlternativeCarrierP = false; // is the alternative annotation target available in the EDM?
636
+ let testToAlternativeEdmTargetP = null; // if true, assign to alternative Edm Target
641
637
 
642
638
  if (carrier.kind === 'entity') {
643
639
  // If AppliesTo=[EntitySet/Singleton/Collection, EntityType], EntitySet/Singleton/Collection has precedence
644
- testToAlternativeEdmTarget = (x => {
645
- if(options.isV2()) {
646
- return ['Singleton', 'EntitySet', 'Collection'].some(y => x.includes(y));
647
- }
648
- else {
649
- return edmUtils.isSingleton(carrier)
650
- ? x.includes('Singleton')
651
- : ['EntitySet', 'Collection'].some(y => x.includes(y));
652
- }
640
+ testToAlternativeEdmTargetP = ((x) => {
641
+ if (options.isV2())
642
+ return [ 'Singleton', 'EntitySet', 'Collection' ].some(y => x.includes(y));
643
+
644
+
645
+ return edmUtils.isSingleton(carrier)
646
+ ? x.includes('Singleton')
647
+ : [ 'EntitySet', 'Collection' ].some(y => x.includes(y));
653
648
  });
654
- testToStandardEdmTarget = (x => x ? x.includes('EntityType') : true);
655
- // if carrier has an alternate 'entitySetName' use this instead of EdmTargetName
656
- // (see edmPreprocessor.initializeParameterizedEntityOrView(), where parameterized artifacts
657
- // are split into *Parameter and *Type entities and their respective EntitySets are eventually
658
- // renamed.
659
- // (which is the definition key in the CSN and usually the name of the EntityType)
660
- // Replace up to last dot with <serviceName>.EntityContainer/
661
- alternativeEdmTargetName = carrier.$entitySetName || edmTargetName
662
- const lastDotIndex = alternativeEdmTargetName.lastIndexOf('.');
649
+ testToStandardEdmTargetP = (x => (x ? x.includes('EntityType') : true));
650
+ // if carrier has an alternate 'entitySetName' use this instead of EdmTargetName
651
+ // (see edmPreprocessor.initializeParameterizedEntityOrView(), where parameterized artifacts
652
+ // are split into *Parameter and *Type entities and their respective EntitySets are eventually
653
+ // renamed.
654
+ // (which is the definition key in the CSN and usually the name of the EntityType)
655
+ // Replace up to last dot with <serviceName>.EntityContainer/
656
+ alternativeEdmTargetNameP = carrier.$entitySetName || edmTargetName;
657
+ const lastDotIndex = alternativeEdmTargetNameP.lastIndexOf('.');
663
658
  if (lastDotIndex > -1)
664
- alternativeEdmTargetName = serviceName + '.EntityContainer/' + alternativeEdmTargetName.substring(lastDotIndex + 1);
665
- hasAlternativeCarrier = carrier.$hasEntitySet;
659
+ alternativeEdmTargetNameP = `${serviceName}.EntityContainer/${alternativeEdmTargetNameP.substring(lastDotIndex + 1)}`;
660
+ hasAlternativeCarrierP = carrier.$hasEntitySet;
666
661
  }
667
- else if(carrier.kind === 'type') {
668
- testToStandardEdmTarget = (x => x ? x.includes(carrier.elements ? 'ComplexType' : 'TypeDefinition') : true);
662
+ else if (carrier.kind === 'type') {
663
+ testToStandardEdmTargetP = (x => (x ? x.includes(carrier.elements ? 'ComplexType' : 'TypeDefinition') : true));
669
664
  }
670
665
  else if (carrier.kind === 'service') {
671
666
  // if annotated object is a service, annotation goes to EntityContainer,
672
667
  // except if AppliesTo contains Schema but not EntityContainer, then annotation goes to Schema
673
- testToAlternativeEdmTarget = (x => x.includes('Schema') && !x.includes('EntityContainer'));
674
- testToStandardEdmTarget = ( x => x ? (
668
+ testToAlternativeEdmTargetP = (x => x.includes('Schema') && !x.includes('EntityContainer'));
669
+ testToStandardEdmTargetP = ( x => (x ? (
675
670
  // either only AppliesTo=[EntityContainer]
676
- (!x.includes('Schema') && x.includes('EntityContainer')) ||
671
+ (!x.includes('Schema') && x.includes('EntityContainer')) ||
677
672
  // or AppliesTo=[Schema, EntityContainer]
678
673
  (x.includes('Schema') && x.includes('EntityContainer')))
679
- : true );
680
- stdEdmTargetName = edmTargetName + '.EntityContainer';
681
- alternativeEdmTargetName = edmTargetName;
682
- hasAlternativeCarrier = true; // EntityContainer is always available
674
+ : true) );
675
+ stdEdmTargetNameP = `${edmTargetName}.EntityContainer`;
676
+ alternativeEdmTargetNameP = edmTargetName;
677
+ hasAlternativeCarrierP = true; // EntityContainer is always available
683
678
  }
684
- //element => decide if navprop or normal property
685
- else if(!carrier.kind) {
679
+ // element => decide if navprop or normal property
680
+ else if (!carrier.kind) {
686
681
  // if appliesTo is undefined, return true
687
- if(carrier.target) {
688
- testToStandardEdmTarget = (x => x
682
+ if (carrier.target) {
683
+ testToStandardEdmTargetP = (x => (x
689
684
  ? x.includes('NavigationProperty') ||
690
685
  carrier.cardinality && carrier.cardinality.max === '*' && x.includes('Collection')
691
- : true);
686
+ : true));
687
+ }
688
+ else if (carrier.$appliesToReturnType) {
689
+ testToStandardEdmTargetP = (x => (x ? x.includes('ReturnType') : true));
692
690
  }
693
- else if(carrier.$appliesToReturnType)
694
- testToStandardEdmTarget = (x => x ? x.includes('ReturnType') : true);
695
691
  else {
696
692
  // this might be more precise if handleAnnotation would know more about the carrier
697
- testToStandardEdmTarget = (x => x
698
- ? ['Parameter', 'Property'].some(y => x.includes(y) ||
693
+ testToStandardEdmTargetP = (x => (x
694
+ ? [ 'Parameter', 'Property' ].some(y => x.includes(y) ||
699
695
  carrier._isCollection && x.includes('Collection'))
700
- : true);
696
+ : true));
701
697
  }
702
698
  }
703
699
  return [
704
- stdEdmTargetName,
705
- hasAlternativeCarrier,
706
- alternativeEdmTargetName,
707
- testToStandardEdmTarget,
708
- testToAlternativeEdmTarget
700
+ stdEdmTargetNameP,
701
+ hasAlternativeCarrierP,
702
+ alternativeEdmTargetNameP,
703
+ testToStandardEdmTargetP,
704
+ testToAlternativeEdmTargetP,
709
705
  ];
710
706
  /* all AppliesTo entries:
711
707
  "Action",
@@ -759,17 +755,17 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
759
755
 
760
756
  Insert $value into $edmJson with inner annotation as well.
761
757
  */
762
- if(innerAnnotation) {
758
+ if (innerAnnotation) {
763
759
  // != null => also != undefined
764
- if(carrier[prefix] != null) {
765
- const valPrefix = prefix + '.$value';
760
+ if (carrier[prefix] != null) {
761
+ const valPrefix = `${prefix}.$value`;
766
762
  carrier[valPrefix] = carrier[prefix];
767
763
  delete carrier[prefix];
768
764
  rc = true;
769
765
  }
770
- const edmJsonPrefix = prefix + '.$edmJson';
771
- if(carrier[edmJsonPrefix] != null) {
772
- const valPrefix = prefix + '.$value.$edmJson';
766
+ const edmJsonPrefix = `${prefix}.$edmJson`;
767
+ if (carrier[edmJsonPrefix] != null) {
768
+ const valPrefix = `${prefix}.$value.$edmJson`;
773
769
  carrier[valPrefix] = carrier[edmJsonPrefix];
774
770
  delete carrier[edmJsonPrefix];
775
771
  rc = true;
@@ -787,7 +783,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
787
783
  // in OData, there are "structured" annotations -> we first need to regroup the cds annotations
788
784
  // by building a "prefix tree" for the annotations attached to the carrier
789
785
  // see example at definition of function mergePathStepsIntoPrefixTree
790
- const prefixTree = {};
786
+ const prefixTreeP = {};
791
787
 
792
788
  for (const a of knownAnnos) {
793
789
  // remove leading @ and split at "."
@@ -798,27 +794,26 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
798
794
  // takes care of assigning these annotations to the record members
799
795
  const [ prefix, innerAnnotation ] = a.split('.@');
800
796
  const ns = whatsMyTermNamespace(prefix);
801
- const steps = prefix.replace('@' + ns + '.', '').split('.');
797
+ const steps = prefix.replace(`@${ns}.`, '').split('.');
802
798
  steps.splice(0, 0, ns);
803
799
  let i = steps.lastIndexOf('$edmJson');
804
- if(i > -1) {
805
- i = steps.findIndex(s => s.includes('@'), i+1);
806
- if(i > -1) {
807
- steps.splice(i, steps.length-i, steps.slice(i).join('.'));
808
- }
800
+ if (i > -1) {
801
+ i = steps.findIndex(s => s.includes('@'), i + 1);
802
+ if (i > -1)
803
+ steps.splice(i, steps.length - i, steps.slice(i).join('.'));
809
804
  }
810
805
  if (innerAnnotation) {
811
806
  // A voc annotation has two steps (Namespace+Name),
812
807
  // any further steps need to be rendered separately
813
808
  const innerAnnoSteps = innerAnnotation.split('.');
814
- const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length-2);
815
- // prepend annotation prefix (path) to tail steps
816
- tailSteps.splice(0, 0, '@' + innerAnnoSteps.join('.'));
809
+ const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length - 2);
810
+ // prepend annotation prefix (path) to tail steps
811
+ tailSteps.splice(0, 0, `@${innerAnnoSteps.join('.')}`);
817
812
  steps.push(...tailSteps);
818
813
  }
819
- mergePathStepsIntoPrefixTree(prefixTree, steps, 0, carrier);
814
+ mergePathStepsIntoPrefixTree(prefixTreeP, steps, 0);
820
815
  }
821
- return prefixTree;
816
+ return prefixTreeP;
822
817
 
823
818
  // tree: object where to put the next level of names
824
819
  // path: the parts of the annotation name
@@ -843,17 +838,17 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
843
838
  // q2 : ... }
844
839
  // t3#y : { q1 : ...,
845
840
  // q2 : ... } } }
846
- function mergePathStepsIntoPrefixTree(tree, pathSteps, index, carrier) {
841
+ function mergePathStepsIntoPrefixTree( tree, pathSteps, index ) {
847
842
  // TODO check nesting level > 3
848
843
  const name = pathSteps[index];
849
- if (index+1 < pathSteps.length ) {
850
- if (!tree[name]) {
844
+ if (index + 1 < pathSteps.length ) {
845
+ if (!tree[name])
851
846
  tree[name] = {};
852
- }
853
- mergePathStepsIntoPrefixTree(tree[name], pathSteps, index+1, carrier);
847
+
848
+ mergePathStepsIntoPrefixTree(tree[name], pathSteps, index + 1);
854
849
  }
855
- else if(typeof tree === 'object' ){
856
- tree[name] = carrier['@' + pathSteps.join('.')];
850
+ else if (typeof tree === 'object' ) {
851
+ tree[name] = carrier[`@${pathSteps.join('.')}`];
857
852
  }
858
853
  }
859
854
  }
@@ -864,34 +859,33 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
864
859
  // addAnnotationFunc: a function that adds the <Annotation ...> tags created here into the
865
860
  // correct parent tag (see handleAnnotations())
866
861
  // prefixTree: the annotations
867
- function handleAnno2(addAnnotationFunc, prefixTree, location) {
862
+ function handleAnno2( addAnnotationFunc, prefixTree, location ) {
868
863
  // first level names of prefix tree are the vocabulary names
869
864
  // second level names are the term names
870
865
  // create an annotation tag <Annotation ...> for each term
871
866
  for (const voc of Object.keys(prefixTree)) {
872
867
  for (const term of Object.keys(prefixTree[voc])) {
873
- const fullTermName = voc + '.' + term;
868
+ const fullTermName = `${voc}.${term}`;
874
869
 
875
870
  // msg is "semantic" location message used for messages
876
871
  const msg = {
877
872
  fullTermName,
878
873
  stack: [],
879
- location: [ ...location, '@' + fullTermName ],
874
+ location: [ ...location, `@${fullTermName}` ],
880
875
  };
881
- msg.anno = () => {
882
- return msg.fullTermName + msg.stack.join('');
883
- }
876
+ msg.anno = () => msg.fullTermName + msg.stack.join('');
884
877
 
885
878
  // anno is the full <Annotation Term=...>
886
879
  const anno = handleTerm(fullTermName, prefixTree[voc][term], msg);
887
- if(anno !== undefined) {
880
+ if (anno !== undefined) {
888
881
  // addAnnotationFunc needs AppliesTo message from dictionary to decide where to put the anno
889
882
  const termName = fullTermName.replace(/#(\w+)$/g, ''); // remove qualifier
890
883
  const dictTerm = getDictTerm(termName, msg); // message for unknown term was already issued in handleTerm
891
- if(!addAnnotationFunc(anno, dictTerm && dictTerm.AppliesTo)) {
892
- if(dictTerm && dictTerm.AppliesTo) {
884
+ // eslint-disable-next-line sonarjs/no-collapsible-if
885
+ if (!addAnnotationFunc(anno, dictTerm && dictTerm.AppliesTo)) {
886
+ if (dictTerm && dictTerm.AppliesTo) {
893
887
  message('odata-anno-def', location,
894
- { anno: termName, rawvalues: dictTerm.AppliesTo, '#': 'notapplied' });
888
+ { anno: termName, rawvalues: dictTerm.AppliesTo, '#': 'notapplied' });
895
889
  }
896
890
  }
897
891
  }
@@ -905,30 +899,30 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
905
899
  // they are regrouped here
906
900
  // msg : for messages
907
901
  // return : object that represents the annotation in the result edmx
908
- function handleTerm(termName, annoValue, msg) {
902
+ function handleTerm( termName, annoValue, msg ) {
909
903
  /**
910
904
  * create the <Annotation ...> tag
911
905
  * @type {object}
912
906
  * */
913
- let newAnno = undefined;
914
- const omissions = { 'Aggregation.default':1 };
915
- const nullList = { 'Core.OperationAvailable':1 };
916
- if(annoValue !== null && !omissions[termName]|| nullList[termName]) {
907
+ let newAnno;
908
+ const omissions = { 'Aggregation.default': 1 };
909
+ const nullList = { 'Core.OperationAvailable': 1 };
910
+ if (annoValue !== null && !omissions[termName] || nullList[termName]) {
917
911
  // termName may contain a qualifier: @UI.FieldGroup#shippingStatus
918
912
  // -> remove qualifier from termName and set Qualifier attribute in newAnno
919
913
  const i = termName.indexOf('#');
920
914
  const termNameWithoutQualifiers = i > 0 ? termName.substring(0, i) : termName;
921
- const qualifier = i >= 0 ? termName.substring(i+1) : undefined;
915
+ const qualifier = i >= 0 ? termName.substring(i + 1) : undefined;
922
916
 
923
917
  termNameWithoutQualifiers.split('.').forEach((id) => {
924
- if(!edmUtils.isODataSimpleIdentifier(id))
918
+ if (!edmUtils.isODataSimpleIdentifier(id))
925
919
  message('odata-spec-violation-id', msg.location, { id });
926
- })
920
+ });
927
921
  newAnno = new Edm.Annotation(v, termNameWithoutQualifiers);
928
922
  if (qualifier?.length) {
929
923
  if (!edmUtils.isODataSimpleIdentifier(qualifier)) {
930
924
  message('odata-spec-violation-id', msg.location,
931
- { id: qualifier, '#': 'qualifier' });
925
+ { id: qualifier, '#': 'qualifier' });
932
926
  }
933
927
  newAnno.setEdmAttribute('Term', termNameWithoutQualifiers);
934
928
  newAnno.setEdmAttribute('Qualifier', qualifier);
@@ -941,9 +935,9 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
941
935
  }
942
936
  else {
943
937
  // message if term is completely unknown or if vocabulary is unchecked
944
- const vocDef = mergedVocDefs[whatsMyTermNamespace('@'+termNameWithoutQualifiers)];
945
- if((vocDef?.int && vocDef?.int?.filename) || !vocDef)
946
- message('odata-anno-def', msg.location,{ anno: termNameWithoutQualifiers });
938
+ const myVocDef = mergedVocDefs[whatsMyTermNamespace(`@${termNameWithoutQualifiers}`)];
939
+ if ((myVocDef?.int && myVocDef?.int?.filename) || !myVocDef)
940
+ message('odata-anno-def', msg.location, { anno: termNameWithoutQualifiers });
947
941
  }
948
942
 
949
943
  // handle the annotation value and put the result into the <Annotation ...> tag just created above
@@ -957,131 +951,143 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
957
951
  // cAnnoValue: the annotation value (c : csn)
958
952
  // oTarget: the result object (o: odata)
959
953
  // oTermName: current term
960
- // dTypeName: expected type of cAnnoValue according to dictionary, may be null (d: dictionary)
961
- function handleValue(cAnnoValue, oTarget, oTermName, dTypeName, msg) {
954
+ // dTypeNameArg: expected type of cAnnoValue according to dictionary, may be null (d: dictionary)
955
+ function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg ) {
962
956
  // this function basically only figures out what kind of annotation value we have
963
957
  // (can be: array, expression, enum, pseudo-record, record, simple value),
964
958
  // then calls a more specific function to deal with it and puts
965
959
  // the result into the oTarget object
966
960
 
967
- if (Array.isArray(cAnnoValue))
968
- {
969
- if (isEnumType(dTypeName))
970
- {
961
+ const [ dTypeName, dTypeIsACollection ] = stripCollection(dTypeNameArg);
962
+
963
+ if (Array.isArray(cAnnoValue)) {
964
+ if (isEnumType(dTypeName)) {
971
965
  // if we find an array although we expect an enum, this may be a "flag enum"
972
- checkMultiEnumValue(cAnnoValue);
973
- oTarget.setJSON({ 'EnumMember': generateMultiEnumValue(cAnnoValue, false), 'EnumMember@odata.type' : '#'+dTypeName });
974
- oTarget.setXml( { 'EnumMember': generateMultiEnumValue(cAnnoValue, true) });
966
+ checkMultiEnumValue();
967
+ oTarget.setJSON({ EnumMember: generateMultiEnumValue(false), 'EnumMember@odata.type': `#${dTypeName}` });
968
+ oTarget.setXml( { EnumMember: generateMultiEnumValue(true) });
975
969
  }
976
- else
977
- {
978
- oTarget.append(generateCollection(cAnnoValue, oTermName, dTypeName, msg));
970
+ else {
971
+ oTarget.append(generateCollection(cAnnoValue, oTermName, dTypeName, dTypeIsACollection, msg));
979
972
  }
980
973
  }
981
974
  else if (cAnnoValue && typeof cAnnoValue === 'object') {
982
975
  // an empty record is rendered as <Record/>
983
976
  if ('=' in cAnnoValue) {
977
+ if (dTypeIsACollection) {
978
+ message('odata-anno-value', msg.location,
979
+ { anno: msg.anno(), str: 'path', '#': 'incompval' });
980
+ }
984
981
  // expression
985
982
  const res = handleExpression(cAnnoValue['='], dTypeName);
986
- oTarget.setXml( { [res.name] : res.value });
987
- oTarget.setJSON( { [res.name] : res.value });
983
+ oTarget.setXml( { [res.name]: res.value });
984
+ oTarget.setJSON( { [res.name]: res.value });
988
985
  }
989
986
  else if (cAnnoValue['#'] !== undefined) {
990
987
  const enumSymbol = cAnnoValue['#'];
991
988
  // enum
992
989
  if (dTypeName) {
993
- const typeDef = getDictType(stripCollection(dTypeName));
994
- if(typeDef && typeDef.$Allowed && !typeDef.Members) {
990
+ const typeDef = getDictType(dTypeName);
991
+ if (typeDef && typeDef.$Allowed && !typeDef.Members) {
995
992
  const allowedValue = typeDef.$Allowed.Symbols[enumSymbol];
996
- if(!allowedValue) {
993
+ if (!allowedValue) {
997
994
  message('odata-anno-value', msg.location,
998
- { anno: msg.anno(),
999
- type: dTypeName,
1000
- value: `"#${enumSymbol}"`,
1001
- rawvalues: Object.keys(typeDef.$Allowed.Symbols).map(m => `"#${m}"`),
1002
- '#': 'enum'
1003
- });
995
+ {
996
+ anno: msg.anno(),
997
+ type: dTypeName,
998
+ value: `"#${enumSymbol}"`,
999
+ rawvalues: Object.keys(typeDef.$Allowed.Symbols).map(m => `"#${m}"`),
1000
+ '#': 'enum',
1001
+ });
1004
1002
  }
1005
1003
  else {
1006
1004
  oTarget.setXml( { [typeDef.UnderlyingType?.replace('Edm.', '') || 'String']: allowedValue.Value || enumSymbol });
1007
1005
  }
1008
1006
  }
1009
- else if(checkEnumValue(enumSymbol))
1010
- oTarget.setXml( { 'EnumMember': dTypeName + '/' + enumSymbol });
1011
- else
1012
- oTarget.setXml( { 'String': enumSymbol });
1007
+ else if (checkEnumValue(enumSymbol)) {
1008
+ oTarget.setXml( { EnumMember: `${dTypeName}/${enumSymbol}` });
1009
+ }
1010
+ else {
1011
+ oTarget.setXml( { String: enumSymbol });
1012
+ }
1013
1013
  }
1014
1014
  else {
1015
- oTarget.setXml( { 'EnumMember': oTermName + 'Type/' + enumSymbol });
1015
+ oTarget.setXml( { EnumMember: `${oTermName}Type/${enumSymbol}` });
1016
1016
  }
1017
1017
  oTarget.setJSON({ 'Edm.String': enumSymbol });
1018
1018
  }
1019
- else if (cAnnoValue['$value'] !== undefined) {
1019
+ else if (cAnnoValue.$value !== undefined) {
1020
1020
  // "pseudo-structure" used for annotating scalar annotations
1021
- handleValue(cAnnoValue['$value'], oTarget, oTermName, dTypeName, msg);
1021
+ handleValue(cAnnoValue.$value, oTarget, oTermName, dTypeNameArg, msg);
1022
1022
 
1023
1023
  const k = Object.keys(cAnnoValue).filter( x => x[0] === '@');
1024
1024
  if (!k || k.length === 0) {
1025
1025
  message('odata-anno-value', msg.location,
1026
- { anno: msg.anno(), str: 'nested', '#': 'nested' });
1026
+ { anno: msg.anno(), str: 'nested', '#': 'nested' });
1027
1027
  }
1028
1028
  for (const nestedAnnoName of k) {
1029
1029
  const nestedAnno = handleTerm(nestedAnnoName.slice(1), cAnnoValue[nestedAnnoName], msg);
1030
1030
  oTarget.append(nestedAnno);
1031
1031
  }
1032
1032
  }
1033
- else if (cAnnoValue['$edmJson']) {
1033
+ else if (cAnnoValue.$edmJson) {
1034
1034
  // "pseudo-structure" used for embedding a piece of JSON that represents "OData CSDL, JSON Representation"
1035
- oTarget.append(handleEdmJson(cAnnoValue['$edmJson'], msg));
1035
+ oTarget.append(handleEdmJson(cAnnoValue.$edmJson, msg));
1036
1036
  }
1037
1037
  else if ( Object.keys(cAnnoValue).filter( x => x[0] !== '@' ).length === 0) {
1038
1038
  // object consists only of properties starting with "@", no $value
1039
1039
  message('odata-anno-value', msg.location,
1040
- { anno: msg.anno(), str: 'base', '#': 'nested'} );
1040
+ { anno: msg.anno(), str: 'base', '#': 'nested' } );
1041
1041
  }
1042
1042
  else {
1043
1043
  // regular record
1044
- oTarget.append(generateRecord(cAnnoValue, oTermName, dTypeName, msg));
1044
+ if (dTypeIsACollection) {
1045
+ message('odata-anno-value', msg.location,
1046
+ { anno: msg.anno(), str: 'structured', '#': 'incompval' });
1047
+ }
1048
+ oTarget.append(generateRecord(cAnnoValue, oTermName, dTypeName, dTypeIsACollection, msg));
1045
1049
  }
1046
1050
  }
1047
1051
  else {
1048
1052
  const res = handleSimpleValue(cAnnoValue, dTypeName, msg);
1049
- if(((oTermName === 'Core.OperationAvailable' && dTypeName === 'Edm.Boolean') ||
1050
- (oTermName === 'Validation.AllowedValues' && dTypeName === 'Edm.PrimitiveType'))
1051
- && cAnnoValue === null) {
1053
+ if (((oTermName === 'Core.OperationAvailable' && dTypeName === 'Edm.Boolean') ||
1054
+ (oTermName === 'Validation.AllowedValues' && dTypeName === 'Edm.PrimitiveType')) &&
1055
+ cAnnoValue === null) {
1052
1056
  oTarget.append(new Edm.ValueThing(v, 'Null'));
1053
1057
  oTarget._ignoreChildren = true;
1054
1058
  }
1055
1059
  else {
1056
- oTarget.setXml( { [res.name] : res.value });
1060
+ oTarget.setXml( { [res.name]: res.value });
1057
1061
  }
1058
- oTarget.setJSON( { [res.jsonName] : res.value });
1062
+ oTarget.setJSON( { [res.jsonName]: res.value });
1059
1063
  }
1060
-
1061
1064
  // found an enum value ("#"), check whether this fits
1062
1065
  // the expected type "dTypeName"
1063
- function checkEnumValue(value) {
1066
+ function checkEnumValue( value ) {
1064
1067
  let rc = true;
1065
1068
  const expectedType = getDictType(dTypeName);
1066
1069
  if (!expectedType && !isPrimitiveType(dTypeName)) {
1067
1070
  message('odata-anno-dict', msg.location,
1068
- { anno: msg.anno(), type: dTypeName });
1071
+ { anno: msg.anno(), type: dTypeName });
1069
1072
  }
1070
- else if (isComplexType(dTypeName) || isPrimitiveType(dTypeName) || expectedType['$kind'] !== 'EnumType') {
1073
+ else if (isComplexType(dTypeName) || isPrimitiveType(dTypeName) || expectedType.$kind !== 'EnumType') {
1071
1074
  message('odata-anno-value', msg.location,
1072
- { anno: msg.anno(),
1073
- type: dTypeName,
1074
- value: `"#${value}"` });
1075
+ {
1076
+ anno: msg.anno(),
1077
+ type: dTypeName,
1078
+ value: `"#${value}"`,
1079
+ });
1075
1080
  rc = false;
1076
1081
  }
1077
1082
  else if (!expectedType.Members.includes(value)) {
1078
1083
  message('odata-anno-value', msg.location,
1079
- { anno: msg.anno(),
1080
- type: dTypeName,
1081
- value: `"#${value}"`,
1082
- rawvalues: expectedType.Members.map(m => `"#${m}"`),
1083
- '#': 'enum'
1084
- });
1084
+ {
1085
+ anno: msg.anno(),
1086
+ type: dTypeName,
1087
+ value: `"#${value}"`,
1088
+ rawvalues: expectedType.Members.map(m => `"#${m}"`),
1089
+ '#': 'enum',
1090
+ });
1085
1091
  }
1086
1092
  return rc;
1087
1093
  }
@@ -1089,64 +1095,65 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1089
1095
  // cAnnoValue: array
1090
1096
  // dTypeName: expected type, already identified as enum type
1091
1097
  // array is expected to contain enum values
1092
- function checkMultiEnumValue(cAnnoValue) {
1093
- // we know that dTypeName is not null
1098
+ function checkMultiEnumValue( ) {
1099
+ // we know that dTypeName is not null
1094
1100
  const type = getDictType(dTypeName);
1095
- if (!type || type['IsFlags'] !== 'true') {
1101
+ if (!type || type.IsFlags !== 'true') {
1096
1102
  message('odata-anno-value', msg.location,
1097
- { anno: msg.anno(),
1098
- str: 'collection',
1099
- type: dTypeName,
1100
- '#': 'struct' });
1103
+ {
1104
+ anno: msg.anno(),
1105
+ str: 'collection',
1106
+ type: dTypeName,
1107
+ '#': 'incompval',
1108
+ });
1101
1109
  }
1102
1110
 
1103
1111
  let index = 0;
1104
- for (let value of cAnnoValue) {
1105
- msg.stack.push('[' + index + ']');
1112
+ for (const value of cAnnoValue) {
1113
+ msg.stack.push(`[${index}]`);
1106
1114
  index++;
1107
1115
  if (value['#']) {
1108
1116
  checkEnumValue(value['#']);
1109
1117
  }
1110
1118
  else {
1111
1119
  message('odata-anno-value', msg.location,
1112
- { anno: msg.anno(),
1113
- type: dTypeName,
1114
- value: value['='] || value,
1115
- rawvalues: type.Members.map(m => `"#${m}"`),
1116
- '#': 'enum'
1117
- });
1120
+ {
1121
+ anno: msg.anno(),
1122
+ type: dTypeName,
1123
+ value: value['='] || value,
1124
+ rawvalues: type.Members.map(m => `"#${m}"`),
1125
+ '#': 'enum',
1126
+ });
1118
1127
  }
1119
1128
  msg.stack.pop();
1120
1129
  }
1121
1130
  }
1122
1131
 
1123
- function generateMultiEnumValue(cAnnoValue, forXml) {
1132
+ function generateMultiEnumValue( forXml ) {
1124
1133
  // remove all invalid entries (warnining message has already been issued)
1125
1134
  // replace short enum name by the full name
1126
1135
  // concatenate all the enums to a string, separated by spaces
1127
- return cAnnoValue.filter( x => x['#'] != undefined ).map( x => (forXml ? dTypeName + '/' : '') + x['#'] ).join(forXml ? ' ' : ',');
1136
+ return cAnnoValue.filter( x => x['#']).map( x => (forXml ? `${dTypeName}/` : '') + x['#'] ).join(forXml ? ' ' : ',');
1128
1137
  }
1129
1138
  }
1130
1139
 
1131
-
1132
-
1133
1140
  // found an expression value ("=") "expr"
1134
1141
  // expected type is dTypeName
1135
1142
  // note: expr can also be provided if an enum/complex type/collection is expected
1136
- function handleExpression(value, dTypeName) {
1143
+ function handleExpression( value, dTypeName ) {
1137
1144
  let typeName = 'Path';
1138
- if( ['Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(dTypeName) )
1145
+ if ( [ 'Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(dTypeName) )
1139
1146
  typeName = dTypeName.split('.')[1];
1140
1147
 
1141
- if(value) {
1148
+ if (value) {
1142
1149
  // replace all occurrences of '.' by '/' up to first '@'
1143
- value = value.split('@').map((o,i) => (i === 0 ? o.replace(/\./g, '/') : o)).join('@');
1150
+ value = value.split('@').map((o, i) => (i === 0 ? o.replace(/\./g, '/') : o)).join('@');
1144
1151
  }
1145
1152
 
1146
1153
  return {
1147
- name : typeName,
1148
- value
1149
- }
1154
+ name: typeName,
1155
+ value,
1156
+ };
1150
1157
  }
1151
1158
 
1152
1159
 
@@ -1157,7 +1164,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1157
1164
  // floating point type except Edm.Decimal -> Float
1158
1165
  // Edm.Decimal -> Decimal
1159
1166
  // integer type -> Int
1160
- function handleSimpleValue(value, dTypeName, msg) {
1167
+ function handleSimpleValue( value, dTypeName, msg ) {
1161
1168
  // these types must be represented as "String" values in XML:
1162
1169
  const castToXmlString = [ 'Edm.PrimitiveType', 'Edm.Stream', 'Edm.Untyped' ];
1163
1170
  // caller already made sure that val is neither object nor array
@@ -1172,66 +1179,72 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1172
1179
  const type = getDictType(resolvedType);
1173
1180
  const expected = type.Members.map(m => `"#${m}"`);
1174
1181
  message('odata-anno-value', msg.location,
1175
- { anno: msg.anno(),
1176
- value,
1177
- rawvalues: expected,
1178
- type: resolvedType,
1179
- '#': 'enum' });
1182
+ {
1183
+ anno: msg.anno(),
1184
+ value,
1185
+ rawvalues: expected,
1186
+ type: resolvedType,
1187
+ '#': 'enum',
1188
+ });
1180
1189
  }
1181
1190
 
1182
1191
  let typeName = 'String';
1183
- if(Allowed && !Allowed.Values[value])
1192
+ if (Allowed && !Allowed.Values[value]) {
1184
1193
  message('odata-anno-value', msg.location,
1185
- { anno: msg.anno(),
1186
- value,
1187
- rawvalues: Object.keys(Allowed.Values),
1188
- type: resolvedType,
1189
- '#': 'enum' });
1194
+ {
1195
+ anno: msg.anno(),
1196
+ value,
1197
+ rawvalues: Object.keys(Allowed.Values),
1198
+ type: resolvedType,
1199
+ '#': 'enum',
1200
+ });
1201
+ }
1190
1202
 
1191
1203
  if (typeof value === 'string') {
1192
1204
  if (resolvedType === 'Edm.Boolean') {
1193
1205
  typeName = 'Bool';
1194
1206
  if (value !== 'true' && value !== 'false') {
1195
1207
  message('odata-anno-value', msg.location,
1196
- { anno: msg.anno(), value, type: resolvedType });
1208
+ { anno: msg.anno(), value, type: resolvedType });
1197
1209
  }
1198
1210
  }
1199
1211
  else if (resolvedType === 'Edm.Decimal') {
1200
1212
  typeName = 'Decimal';
1213
+ // eslint-disable-next-line no-restricted-globals
1201
1214
  if (isNaN(Number(value)) || isNaN(parseFloat(value))) {
1202
1215
  message('odata-anno-value', msg.location,
1203
- { anno: msg.anno(), value, type: resolvedType });
1216
+ { anno: msg.anno(), value, type: resolvedType });
1204
1217
  }
1205
1218
  }
1206
1219
  else if (resolvedType === 'Edm.Double' || resolvedType === 'Edm.Single') {
1207
1220
  typeName = 'Float';
1221
+ // eslint-disable-next-line no-restricted-globals
1208
1222
  if (isNaN(Number(value)) || isNaN(parseFloat(value))) {
1209
1223
  message('odata-anno-value', msg.location,
1210
- { anno: msg.anno(), value, type: resolvedType });
1224
+ { anno: msg.anno(), value, type: resolvedType });
1211
1225
  }
1212
1226
  }
1213
1227
  else if (isComplexType(resolvedType)) {
1214
1228
  message('odata-anno-value', msg.location,
1215
- { anno: msg.anno(), value, type: resolvedType });
1229
+ { anno: msg.anno(), value, type: resolvedType });
1216
1230
  }
1217
1231
  else if (isEnumType(resolvedType)) {
1218
1232
  message('odata-anno-value', msg.location,
1219
- { anno: msg.anno(), value, type: resolvedType });
1233
+ { anno: msg.anno(), value, type: resolvedType });
1220
1234
  typeName = 'EnumMember';
1221
1235
  }
1222
1236
  else if (resolvedType && resolvedType.startsWith('Edm.') && !castToXmlString.includes(resolvedType)) {
1223
1237
  // this covers also all paths
1224
1238
  typeName = resolvedType.substring(4);
1225
1239
  }
1226
- else {
1227
- if(resolvedType == undefined || castToXmlString.some(t => t === resolvedType))
1228
- resolvedType = 'Edm.String';
1240
+ else if (!resolvedType || castToXmlString.some(t => t === resolvedType)) {
1241
+ resolvedType = 'Edm.String';
1229
1242
  // TODO
1230
- //message(message, msg, "type is not yet handled: found String, expected type: " + dTypeName);
1243
+ // message(message, msg, "type is not yet handled: found String, expected type: " + dTypeName);
1231
1244
  }
1232
1245
  }
1233
1246
  else if (typeof value === 'boolean') {
1234
- if(resolvedType == undefined || resolvedType === 'Edm.Boolean' || resolvedType === 'Edm.PrimitiveType') {
1247
+ if (!resolvedType || resolvedType === 'Edm.Boolean' || resolvedType === 'Edm.PrimitiveType') {
1235
1248
  typeName = 'Bool';
1236
1249
  resolvedType = 'Edm.Boolean';
1237
1250
  }
@@ -1243,7 +1256,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1243
1256
  }
1244
1257
  else {
1245
1258
  message('odata-anno-value', msg.location,
1246
- { anno: msg.anno(), value, type: resolvedType });
1259
+ { anno: msg.anno(), value, type: resolvedType });
1247
1260
  }
1248
1261
  }
1249
1262
  else if (typeof value === 'number') {
@@ -1251,7 +1264,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1251
1264
  resolvedType === 'Edm.PropertyPath' ||
1252
1265
  resolvedType === 'Edm.Boolean') {
1253
1266
  message('odata-anno-value', msg.location,
1254
- { anno: msg.anno(), value, type: resolvedType });
1267
+ { anno: msg.anno(), value, type: resolvedType });
1255
1268
  }
1256
1269
  else if (resolvedType === 'Edm.String') {
1257
1270
  typeName = 'String';
@@ -1262,36 +1275,35 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1262
1275
  else if (resolvedType === 'Edm.Double') {
1263
1276
  typeName = 'Float';
1264
1277
  }
1278
+ else if (Number.isInteger(value)) {
1279
+ // typeName = Number.isInteger(val) ? 'Int' : 'Float';
1280
+ typeName = 'Int';
1281
+ if (resolvedType == null || resolvedType === 'Edm.PrimitiveType' || !resolvedType.startsWith('Edm.'))
1282
+ resolvedType = 'Edm.Int64';
1283
+ }
1265
1284
  else {
1266
- //typeName = Number.isInteger(val) ? 'Int' : 'Float';
1267
- if(Number.isInteger(value)) {
1268
- typeName = 'Int';
1269
- if(resolvedType == null || resolvedType === 'Edm.PrimitiveType' || !resolvedType.startsWith('Edm.'))
1270
- resolvedType = 'Edm.Int64';
1271
- }
1272
- else {
1273
- typeName = 'Float';
1274
- if(resolvedType == null || resolvedType === 'Edm.PrimitiveType'|| !resolvedType.startsWith('Edm.'))
1275
- resolvedType = 'Edm.Double';
1276
- }
1285
+ typeName = 'Float';
1286
+ if (resolvedType == null || resolvedType === 'Edm.PrimitiveType' || !resolvedType.startsWith('Edm.'))
1287
+ resolvedType = 'Edm.Double';
1277
1288
  }
1278
1289
  }
1279
- else if (value === null)
1280
- if((resolvedType == null || resolvedType === 'Edm.PrimitiveType') && typeName === 'String') {
1290
+ else if (value === null) {
1291
+ if ((resolvedType == null || resolvedType === 'Edm.PrimitiveType') && typeName === 'String') {
1281
1292
  resolvedType = 'Edm.String';
1282
1293
  }
1283
1294
  else {
1284
1295
  message('odata-anno-value', msg.location,
1285
- { anno: msg.anno(), value, type: resolvedType });
1296
+ { anno: msg.anno(), value, type: resolvedType });
1286
1297
  }
1298
+ }
1287
1299
 
1288
- if( ['Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(resolvedType) )
1300
+ if ( [ 'Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(resolvedType) )
1289
1301
  resolvedType = resolvedType.split('.')[1];
1290
1302
 
1291
1303
  return {
1292
- name : typeName,
1304
+ name: typeName,
1293
1305
  jsonName: resolvedType,
1294
- value : value
1306
+ value,
1295
1307
  };
1296
1308
  }
1297
1309
 
@@ -1300,59 +1312,67 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1300
1312
  // dTypeName : name of the expected record type according to vocabulary, may be null
1301
1313
  //
1302
1314
  // can be called for a record directly below a term, or at a deeper level
1303
- function generateRecord(obj, termName, dTypeName, msg) {
1315
+ function generateRecord( obj, termName, dTypeName, dTypeIsACollection, msg ) {
1304
1316
  /** @type {object} */
1305
1317
  const newRecord = new Edm.Record(v);
1306
1318
 
1307
1319
  // first determine what is the actual type to be used for the record
1308
1320
  if (dTypeName && !isComplexType(dTypeName)) {
1309
- if (!getDictType(dTypeName) && !isPrimitiveType(dTypeName) && !isCollection(dTypeName))
1321
+ if (!getDictType(dTypeName) && !isPrimitiveType(dTypeName) && !dTypeIsACollection) {
1310
1322
  message('odata-anno-dict', msg.location,
1311
- { anno: msg.anno(), type: dTypeName });
1312
- else
1323
+ { anno: msg.anno(), type: dTypeName });
1324
+ }
1325
+ else {
1313
1326
  message('odata-anno-value', msg.location,
1314
- { anno: msg.anno(), str: 'structured', type: dTypeName, '#': 'struct' });
1327
+ {
1328
+ anno: msg.anno(), str: 'structured', type: dTypeName, '#': 'incompval',
1329
+ });
1330
+ }
1315
1331
  return newRecord;
1316
1332
  }
1317
1333
 
1318
1334
  let actualTypeName = null;
1319
- if (obj['$Type']) { // type is explicitly specified
1320
- actualTypeName = obj['$Type'];
1335
+ if (obj.$Type) { // type is explicitly specified
1336
+ actualTypeName = obj.$Type;
1321
1337
  if (!getDictType(actualTypeName)) {
1322
1338
  // this type doesn't exist
1323
1339
  message('odata-anno-type', msg.location,
1324
- { anno: msg.anno(), type: actualTypeName, '#': 'unknown' });
1340
+ { anno: msg.anno(), type: actualTypeName, '#': 'unknown' });
1325
1341
  // explicitly mentioned type, render in XML and JSON
1326
- newRecord.setXml({ 'Type': actualTypeName});
1342
+ newRecord.setXml({ Type: actualTypeName });
1327
1343
  // unknown dictionary type: can't fully qualify it
1328
- newRecord.setJSON({ 'Type': actualTypeName});
1344
+ newRecord.setJSON({ Type: actualTypeName });
1329
1345
  }
1330
1346
  else {
1331
1347
  if (isAbstractType(actualTypeName)) {
1332
1348
  // this type is abstract
1333
1349
  message('odata-anno-type', msg.location,
1334
- { anno: msg.anno(), type: actualTypeName, code: '$Type', '#': 'abstract' });
1335
- if(dTypeName)
1350
+ {
1351
+ anno: msg.anno(), type: actualTypeName, code: '$Type', '#': 'abstract',
1352
+ });
1353
+ if (dTypeName)
1336
1354
  actualTypeName = dTypeName;
1337
1355
  }
1338
1356
  else if (dTypeName && !isDerivedFrom(actualTypeName, dTypeName)) {
1339
1357
  // this type doesn't fit the expected one
1340
1358
  message('odata-anno-type', msg.location,
1341
- { anno: msg.anno(),
1342
- type: actualTypeName,
1343
- name: dTypeName,
1344
- code: '$Type',
1345
- '#': 'derived' });
1359
+ {
1360
+ anno: msg.anno(),
1361
+ type: actualTypeName,
1362
+ name: dTypeName,
1363
+ code: '$Type',
1364
+ '#': 'derived',
1365
+ });
1346
1366
  actualTypeName = dTypeName;
1347
1367
  }
1348
1368
  // Dictionary Type, render in XML only for backward compatibility
1349
1369
  newRecord.setXml( { Type: actualTypeName });
1350
1370
  const vocName = actualTypeName.slice(0, actualTypeName.indexOf('.'));
1351
- const vocDef = mergedVocDefs[vocName];
1371
+ const myVocDef = mergedVocDefs[vocName];
1352
1372
  // Set full qualified type in JSON
1353
1373
  // TODO: Adhoc type x-ref URIs (only if abstract types are allowed in CDS)
1354
- if(vocDef)
1355
- newRecord.setJSON( { 'Type': `${vocDef.ref.Uri}#${actualTypeName}` });
1374
+ if (myVocDef)
1375
+ newRecord.setJSON( { Type: `${myVocDef.ref.Uri}#${actualTypeName}` });
1356
1376
  // don't add short actualTypeName into JSON as this would be wrong for a resolved! type.
1357
1377
  // A $Type w/o vocDef can only occur for adhoc type defs and these can't be abstract but
1358
1378
  // are fully resolvable due to their term usage via schema x-ref.
@@ -1361,15 +1381,18 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1361
1381
  else if (dTypeName) { // there is an expected type name according to dictionary
1362
1382
  // convenience for common situation:
1363
1383
  // if DataFieldAbstract is expected and no explicit type is provided, automatically choose DataField
1364
- if (dTypeName === 'UI.DataFieldAbstract') {
1384
+ if (dTypeName === 'UI.DataFieldAbstract')
1365
1385
  actualTypeName = 'UI.DataField';
1366
- }
1367
- else {
1386
+
1387
+ else
1368
1388
  actualTypeName = dTypeName;
1369
- }
1370
- if (isAbstractType(actualTypeName))
1389
+
1390
+ if (isAbstractType(actualTypeName)) {
1371
1391
  message('odata-anno-type', msg.location,
1372
- { anno: msg.anno(), type: dTypeName, code: '$Type', '#': 'abstract' });
1392
+ {
1393
+ anno: msg.anno(), type: dTypeName, code: '$Type', '#': 'abstract',
1394
+ });
1395
+ }
1373
1396
 
1374
1397
  // Dictionary Type, render in XML only for backward compatibility
1375
1398
  newRecord.setXml( { Type: actualTypeName });
@@ -1383,7 +1406,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1383
1406
 
1384
1407
  // loop over elements
1385
1408
  for (const name of Object.keys(obj)) {
1386
- msg.stack.push('.' + name);
1409
+ msg.stack.push(`.${name}`);
1387
1410
 
1388
1411
  if (name === '$Type') {
1389
1412
  // ignore, this is an "artificial" property used to indicate the type
@@ -1400,11 +1423,11 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1400
1423
  dictPropertyTypeName = dictProperties[name];
1401
1424
  if (!dictPropertyTypeName && !getDictType(actualTypeName).OpenType) {
1402
1425
  message('odata-anno-type', msg.location,
1403
- { name, anno: termName, type: dTypeName });
1426
+ { name, anno: termName, type: dTypeName });
1404
1427
  }
1405
1428
  }
1406
1429
 
1407
- let newPropertyValue = new Edm.PropertyValue(v, name);
1430
+ const newPropertyValue = new Edm.PropertyValue(v, name);
1408
1431
  // property value can be anything, so delegate handling to handleValue
1409
1432
  handleValue(obj[name], newPropertyValue, termName, dictPropertyTypeName, msg);
1410
1433
  newRecord.append(newPropertyValue);
@@ -1419,55 +1442,50 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1419
1442
 
1420
1443
  // annoValue is an array
1421
1444
  // dTypeName : Collection(...) according to dictionary
1422
- function generateCollection(annoValue, termName, dTypeName, msg) {
1445
+ function generateCollection( annoValue, termName, dTypeName, dTypeIsACollection, msg ) {
1423
1446
  const newCollection = new Edm.Collection(v);
1424
1447
 
1425
- let innerTypeName = null;
1426
- if (dTypeName) {
1427
- const match = dTypeName.match(/^Collection\((.+)\)/);
1428
- if (match) {
1429
- innerTypeName = match[1];
1430
- }
1431
- else {
1432
- message('odata-anno-value', msg.location,
1433
- { anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'struct' });
1434
- }
1448
+ if (dTypeName && !dTypeIsACollection) {
1449
+ message('odata-anno-value', msg.location,
1450
+ {
1451
+ anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'incompval',
1452
+ });
1435
1453
  }
1436
1454
 
1437
1455
  let index = 0;
1438
1456
  for (const value of annoValue) {
1439
- msg.stack.push('[' + index + ']');
1440
- index++
1457
+ msg.stack.push(`[${index}]`);
1458
+ index++;
1441
1459
 
1442
1460
  // for dealing with the single array entries we unfortunately cannot call handleValue(),
1443
1461
  // as the values inside an array are represented differently from the values
1444
1462
  // in a record or term
1445
1463
  if (Array.isArray(value)) {
1446
1464
  message('odata-anno-value', msg.location,
1447
- { anno: msg.anno(), '#': 'nestedcollection' });
1465
+ { anno: msg.anno(), '#': 'nestedcollection' });
1448
1466
  }
1449
1467
  else if (value && typeof value === 'object') {
1450
1468
  if (value['=']) {
1451
- const res = handleExpression(value['='], innerTypeName);
1469
+ const res = handleExpression(value['='], dTypeName);
1452
1470
  const newPropertyPath = new Edm.ValueThing(v, res.name, res.value );
1453
- newPropertyPath.setJSON( { [res.name] : res.value } );
1471
+ newPropertyPath.setJSON( { [res.name]: res.value } );
1454
1472
  newCollection.append(newPropertyPath);
1455
1473
  }
1456
1474
  else if (value['#']) {
1457
1475
  message('odata-anno-value', msg.location,
1458
- { anno: msg.anno(), '#': 'enumincollection' });
1476
+ { anno: msg.anno(), '#': 'enumincollection' });
1459
1477
  }
1460
- else if(value['$edmJson']) {
1461
- newCollection.append(handleEdmJson(value['$edmJson'], msg));
1478
+ else if (value.$edmJson) {
1479
+ newCollection.append(handleEdmJson(value.$edmJson, msg));
1462
1480
  }
1463
1481
  else {
1464
- newCollection.append(generateRecord(value, termName, innerTypeName, msg));
1482
+ newCollection.append(generateRecord(value, termName, dTypeName, dTypeIsACollection, msg));
1465
1483
  }
1466
1484
  }
1467
1485
  else {
1468
- const res = handleSimpleValue(value, innerTypeName, msg);
1469
- const newThing = (value === null) ?new Edm.ValueThing(v, 'Null') : new Edm.ValueThing(v, res.name, value );
1470
- newThing.setJSON( { [res.jsonName] : res.value });
1486
+ const res = handleSimpleValue(value, dTypeName, msg);
1487
+ const newThing = (value === null) ? new Edm.ValueThing(v, 'Null') : new Edm.ValueThing(v, res.name, value );
1488
+ newThing.setJSON( { [res.jsonName]: res.value });
1471
1489
  newCollection.append(newThing);
1472
1490
  }
1473
1491
 
@@ -1485,17 +1503,16 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1485
1503
  // See example in test/odataAnnotations/smallTests/edmJson_noReverse_ok
1486
1504
  // and test3/ODataBackends/DynExpr
1487
1505
 
1488
- function handleEdmJson(obj, msg, exprDef=undefined) {
1489
-
1490
- let edmNode = undefined;
1491
- if(obj == null)
1506
+ function handleEdmJson( obj, msg, exprDef = undefined ) {
1507
+ let edmNode;
1508
+ if (obj == null)
1492
1509
  return edmNode;
1493
1510
 
1494
1511
  const dynExprs = edmUtils.intersect(dynamicExpressionNames, Object.keys(obj));
1495
1512
 
1496
- if(dynExprs.length > 1) {
1513
+ if (dynExprs.length > 1) {
1497
1514
  message('odata-anno-value', msg.location,
1498
- { anno: msg.anno(), rawvalues: dynExprs, '#': 'multexpr' });
1515
+ { anno: msg.anno(), rawvalues: dynExprs, '#': 'multexpr' });
1499
1516
  return edmNode;
1500
1517
  }
1501
1518
 
@@ -1506,61 +1523,60 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1506
1523
  edmNode = new Edm.ValueThing(v, k[0] === '$' ? k.slice(1) : k, val );
1507
1524
  edmNode.setJSON( { [edmNode.kind]: val } );
1508
1525
  }
1509
- else {
1510
- // This thing is either a record or a collection or a literal
1511
- if(Array.isArray(obj)) {
1512
- // EDM JSON doesn't mention annotations on collections
1513
- edmNode = new Edm.Collection(v);
1514
- obj.forEach(o => edmNode.append(handleEdmJson(o, msg)));
1515
- }
1516
- else if(typeof obj === 'object') {
1517
- edmNode = new Edm.Record(v);
1518
- const annos = Object.create(null);
1519
- const props = Object.create(null);
1520
- Object.entries(obj).forEach(([k, val]) => {
1521
- if(k === '@type') {
1522
- edmNode.setJSON({ 'Type': val});
1523
- // try to shorten full qualified type URI to short type name
1524
- const parts = val.split('#');
1525
- const shortTypeName = parts[parts.length-1];
1526
- edmNode.setXml({ Type: shortTypeName });
1526
+ // This thing is either a record or a collection or a literal
1527
+ else if (Array.isArray(obj)) {
1528
+ // EDM JSON doesn't mention annotations on collections
1529
+ edmNode = new Edm.Collection(v);
1530
+ obj.forEach(o => edmNode.append(handleEdmJson(o, msg)));
1531
+ }
1532
+ else if (typeof obj === 'object') {
1533
+ edmNode = new Edm.Record(v);
1534
+ const annos = Object.create(null);
1535
+ const props = Object.create(null);
1536
+ Object.entries(obj).forEach(([ k, val ]) => {
1537
+ if (k === '@type') {
1538
+ edmNode.setJSON({ Type: val });
1539
+ // try to shorten full qualified type URI to short type name
1540
+ const parts = val.split('#');
1541
+ const shortTypeName = parts[parts.length - 1];
1542
+ edmNode.setXml({ Type: shortTypeName });
1543
+ }
1544
+ else {
1545
+ let child;
1546
+ const [ head, tail ] = k.split('@');
1547
+ if (tail) {
1548
+ child = handleTerm(tail, val, msg);
1527
1549
  }
1528
1550
  else {
1529
- let child = undefined;
1530
- const [ head, tail ] = k.split('@');
1531
- if(tail) {
1532
- child = handleTerm(tail, val, msg);
1551
+ child = new Edm.PropertyValue(v, head);
1552
+ child.append(handleEdmJson(val, msg));
1553
+ }
1554
+ if (child) {
1555
+ if (tail && head.length) {
1556
+ if (!annos[head])
1557
+ annos[head] = [ child ];
1558
+ else
1559
+ annos[head].push(child);
1533
1560
  }
1534
1561
  else {
1535
- child = new Edm.PropertyValue(v, head);
1536
- child.append(handleEdmJson(val, msg));
1537
- }
1538
- if(child) {
1539
- if(tail && head.length) {
1540
- if(!annos[head])
1541
- annos[head] = [ child ];
1542
- else
1543
- annos[head].push(child);
1544
- }
1545
- else {
1546
- if(head.length)
1547
- props[head] = child;
1548
- edmNode.append(child);
1549
- }
1562
+ if (head.length)
1563
+ props[head] = child;
1564
+ edmNode.append(child);
1550
1565
  }
1551
1566
  }
1552
- });
1553
- // add collected annotations to record members
1554
- Object.entries(annos).forEach(([n, val]) => {
1555
- props[n] && props[n].prepend(...val);
1556
- });
1557
- }
1558
- else { // literal
1559
- edmNode = new Edm.ValueThing(v,
1560
- exprDef && exprDef.valueThingName || getXmlTypeName(obj), obj);
1561
- // typename for static expression rendering
1562
- edmNode.setJSON( { [getJsonTypeName(obj)]: obj } );
1563
- }
1567
+ }
1568
+ });
1569
+ // add collected annotations to record members
1570
+ Object.entries(annos).forEach(([ n, val ]) => {
1571
+ if (props[n])
1572
+ props[n].prepend(...val);
1573
+ });
1574
+ }
1575
+ else { // literal
1576
+ edmNode = new Edm.ValueThing(v,
1577
+ exprDef && exprDef.valueThingName || getXmlTypeName(obj), obj);
1578
+ // typename for static expression rendering
1579
+ edmNode.setJSON( { [getJsonTypeName(obj)]: obj } );
1564
1580
  }
1565
1581
  }
1566
1582
  else {
@@ -1570,23 +1586,21 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1570
1586
 
1571
1587
  // iterate over each obj.property and translate expression into EDM
1572
1588
  forEach(obj, (name, val) => {
1573
- if(exprDef) {
1574
- if(exprDef.anno && name[0] === '@') {
1589
+ if (exprDef) {
1590
+ if (exprDef.anno && name[0] === '@') {
1575
1591
  edmNode.append(handleTerm(name.slice(1), val, msg));
1576
1592
  }
1577
1593
  else if (exprDef.attr && exprDef.attr.includes(name)) {
1578
- if (name[0] === '$') {
1594
+ if (name[0] === '$')
1579
1595
  edmNode.setEdmAttribute(name.slice(1), val);
1580
- }
1581
1596
  }
1582
1597
  else if (exprDef.jsonAttr && exprDef.jsonAttr.includes(name)) {
1583
- if (name[0] === '$') {
1584
- edmNode.setJSON( { [name.slice(1)]: val }) ;
1585
- }
1598
+ if (name[0] === '$')
1599
+ edmNode.setJSON( { [name.slice(1)]: val });
1586
1600
  }
1587
- else if(exprDef.children) {
1601
+ else if (exprDef.children) {
1588
1602
  if (Array.isArray(val)) {
1589
- val.forEach(a => {
1603
+ val.forEach((a) => {
1590
1604
  edmNode.append(handleEdmJson(a, msg, exprDef));
1591
1605
  });
1592
1606
  }
@@ -1599,23 +1613,22 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1599
1613
  }
1600
1614
  return edmNode;
1601
1615
 
1602
- function getXmlTypeName(val) {
1616
+ function getXmlTypeName( val ) {
1603
1617
  let typeName = 'String';
1604
- if (typeof val === 'boolean') {
1618
+ if (typeof val === 'boolean')
1605
1619
  typeName = 'Bool';
1606
- }
1607
- else if (typeof val === 'number') {
1620
+
1621
+ else if (typeof val === 'number')
1608
1622
  typeName = Number.isInteger(val) ? 'Int' : 'Decimal';
1609
- }
1623
+
1610
1624
  return typeName;
1611
1625
  }
1612
1626
 
1613
- function getJsonTypeName(val) {
1614
- let typeName = getXmlTypeName(val);
1615
- if(typeName === 'Int')
1616
- return 'Edm.Int32'
1617
- else
1618
- return 'Edm.'+typeName;
1627
+ function getJsonTypeName( val ) {
1628
+ const typeName = getXmlTypeName(val);
1629
+ if (typeName === 'Int')
1630
+ return 'Edm.Int32';
1631
+ return `Edm.${typeName}`;
1619
1632
  }
1620
1633
  }
1621
1634
 
@@ -1630,73 +1643,75 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1630
1643
  * @returns [object, Array<object>]
1631
1644
  */
1632
1645
  function createUserDefinedTermDictionary() {
1633
- const allKnownVocabularies = [];
1646
+ const allKnownVocabulariesP = [];
1634
1647
  const dict = { terms: {}, types: {}, xrefs: {} };
1635
1648
 
1636
- if(!isBetaEnabled(options, 'odataTerms'))
1637
- return [ dict, allKnownVocabularies ];
1649
+ if (!isBetaEnabled(options, 'odataTerms'))
1650
+ return [ dict, allKnownVocabulariesP ];
1638
1651
 
1639
- for(let termName in csnVocabularies) {
1652
+ for (const termName in csnVocabularies) {
1640
1653
  let dictDef = oDataDictionary.terms[termName];
1641
- if(dictDef) {
1642
- message('odata-anno-dict', ['vocabularies', termName],
1643
- { anno: termName, string: 'annotation', '#': 'redefinition' } );
1654
+ if (dictDef) {
1655
+ message('odata-anno-dict', [ 'vocabularies', termName ],
1656
+ { anno: termName, string: 'annotation', '#': 'redefinition' } );
1644
1657
  }
1645
- else if(!dictDef) {
1658
+ else if (!dictDef) {
1646
1659
  const annoDef = csnVocabularies[termName];
1647
- if(annoDef?.$mySchemaName) {
1648
- if(!allKnownVocabularies.includes[annoDef.$mySchemaName])
1649
- allKnownVocabularies.push(annoDef.$mySchemaName);
1660
+ if (annoDef?.$mySchemaName) {
1661
+ if (!allKnownVocabulariesP.includes[annoDef.$mySchemaName])
1662
+ allKnownVocabulariesP.push(annoDef.$mySchemaName);
1650
1663
  const myServiceRoot = options.whatsMyServiceRootName(annoDef.$mySchemaName);
1651
- if(!dict.xrefs[myServiceRoot])
1664
+ if (!dict.xrefs[myServiceRoot])
1652
1665
  dict.xrefs[myServiceRoot] = { $myServiceRoot: myServiceRoot, used: false };
1653
1666
  const edmType = new Edm.TypeBase(options.v, {}, annoDef);
1654
1667
  dictDef = edmType._edmAttributes;
1655
- dictDef['$myServiceRoot'] = myServiceRoot;
1668
+ dictDef.$myServiceRoot = myServiceRoot;
1656
1669
  let val = annoDef['@odata.term.AppliesTo'];
1657
- if(val != null)
1658
- dictDef.AppliesTo = Array.isArray(val) ? val.map(v=>v['='] || v) : [val['='] || val];
1659
- val = annoDef['@odata.term.Experimental'];
1660
- if(val != null)
1661
- dictDef['$experimental'] = !!val;
1670
+ if (val != null)
1671
+ dictDef.AppliesTo = Array.isArray(val) ? val.map(av => av['='] || av) : [ val['='] || val ];
1672
+ val = annoDef['@odata.term.Experimental'];
1673
+ if (val != null)
1674
+ dictDef.$experimental = !!val;
1662
1675
  val = annoDef['@odata.term.Deprecated'];
1663
- if(val != null) {
1664
- dictDef['$deprecated'] = !!val;
1665
- if(typeof val === 'string')
1666
- dictDef['$deprecationText'] = val;
1676
+ if (val != null) {
1677
+ dictDef.$deprecated = !!val;
1678
+ if (typeof val === 'string')
1679
+ dictDef.$deprecationText = val;
1667
1680
  }
1668
1681
  dict.terms[termName] = dictDef;
1669
1682
 
1670
- if((annoDef.items?.enum || annoDef.enum) && isBuiltinType(annoDef.items?.type || annoDef.type)) {
1671
- const enumType = createTypeDefWithAllowedValues(annoDef, annoDef, dictDef.Type, ['vocabularies', termName ]);
1672
- const tName = termName + '_$$$EnumType$$$$';
1683
+ if ((annoDef.items?.enum || annoDef.enum) && isBuiltinType(annoDef.items?.type || annoDef.type)) {
1684
+ const enumType = createTypeDefWithAllowedValues(annoDef, annoDef, dictDef.Type, [ 'vocabularies', termName ]);
1685
+ const tName = `${termName}_$$$EnumType$$$$`;
1673
1686
  dict.types[tName] = enumType;
1674
1687
  dictDef.Type = tName;
1675
1688
  }
1676
- else
1677
- addTypesToDictionary(annoDef);
1689
+ else {
1690
+ addTypesToDictionary(annoDef);
1691
+ }
1678
1692
  }
1679
1693
  }
1680
1694
  }
1681
- return [ dict, allKnownVocabularies ];
1695
+ return [ dict, allKnownVocabulariesP ];
1682
1696
 
1683
- function addTypesToDictionary(node) {
1697
+ function addTypesToDictionary( node ) {
1684
1698
  const typeName = node.items?.type || node.type;
1685
1699
  // for type reuse in x-ref mode, the definition has already been
1686
1700
  // replaced by a reference object in edmPreprocessor.
1687
1701
  // Fall back to original type (the one of the other service).
1688
- const typeDef = reqDefs.definitions[typeName] || reqDefs.definitions[typeName.replace(serviceName + '.', '')];
1689
- if(typeDef) {
1702
+ const typeDef = reqDefs.definitions[typeName] || reqDefs.definitions[typeName.replace(`${serviceName}.`, '')];
1703
+ if (typeDef) {
1690
1704
  let dictDef = { };
1691
1705
  const elements = typeDef.items?.elements || typeDef.elements;
1692
- if(elements) {
1693
- // complex type
1694
- dictDef['$kind'] = 'ComplexType';
1706
+ if (elements) {
1707
+ // complex type
1708
+ dictDef.$kind = 'ComplexType';
1709
+ // eslint-disable-next-line no-new-object
1695
1710
  dictDef.Properties = new Object(null);
1696
1711
 
1697
- for(let en in elements) {
1712
+ for (const en in elements) {
1698
1713
  const elt = elements[en];
1699
- if(isEdmPropertyRendered(elt, options)) {
1714
+ if (isEdmPropertyRendered(elt, options)) {
1700
1715
  const edmType = new Edm.TypeBase(options.v, {}, elt);
1701
1716
  dictDef.Properties[en] = edmType._edmAttributes[edmType._typeName];
1702
1717
  addTypesToDictionary(elt);
@@ -1706,39 +1721,42 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1706
1721
  else {
1707
1722
  // type definition
1708
1723
  const edmType = new Edm.TypeBase(options.v, {}, typeDef);
1709
- dictDef = createTypeDefWithAllowedValues(node, typeDef, edmType._edmAttributes[edmType._typeName], ['definitions', typeName ]);
1710
- if(!typeDef.enum)
1724
+ dictDef = createTypeDefWithAllowedValues(node, typeDef, edmType._edmAttributes[edmType._typeName], [ 'definitions', typeName ]);
1725
+ if (!typeDef.enum)
1711
1726
  delete dictDef.$Allowed;
1712
1727
  }
1713
1728
  dict.types[typeName] = dictDef;
1714
1729
  }
1715
1730
  }
1716
1731
 
1717
- function createTypeDefWithAllowedValues(node, typeDef, UnderlyingType, path) {
1732
+ function createTypeDefWithAllowedValues( node, typeDef, UnderlyingType, path ) {
1718
1733
  const dictTypeDef = { $kind: 'TypeDefinition', UnderlyingType, $Allowed: { Values: {}, Symbols: {} } };
1719
1734
  // create an artificial type that holds the $Allowed enum symbols and values
1720
- if(node.items && typeDef.enum || typeDef.items?.enum)
1735
+ if (node.items && typeDef.enum || typeDef.items?.enum) {
1721
1736
  message('odata-anno-dict-enum', [ 'vocabularies', node.name ],
1722
- { name: node.name,
1723
- type: typeDef.name,
1724
- '#': node.name === typeDef.name ? 'std' : 'type'
1725
- });
1737
+ {
1738
+ name: node.name,
1739
+ type: typeDef.name,
1740
+ '#': node.name === typeDef.name ? 'std' : 'type',
1741
+ });
1742
+ }
1726
1743
  const enumDic = (typeDef.items?.enum || typeDef.enum);
1727
1744
  const baseType = (typeDef.items || typeDef).type;
1728
- if(baseType !== 'cds.String' && Object.values(enumDic).some(v => !v.val)) {
1745
+ if (baseType !== 'cds.String' && Object.values(enumDic).some(av => !av.val)) {
1729
1746
  message('odata-anno-dict-enum', path,
1730
- { name: node.name, type: baseType, '#': 'value' });
1747
+ { name: node.name, type: baseType, '#': 'value' });
1731
1748
  }
1732
1749
  else {
1733
- for(const symbol in enumDic) {
1750
+ for (const symbol in enumDic) {
1734
1751
  const valDic = { '#SymbolicName': symbol };
1735
1752
  const enumDef = enumDic[symbol];
1736
1753
  // <Null/> values can't be rendered
1737
- if(enumDef.val === undefined)
1754
+ if (enumDef.val === undefined)
1738
1755
  valDic.Value = symbol;
1739
- else if(valDic.val !== null)
1756
+ else if (valDic.val !== null)
1740
1757
  valDic.Value = enumDef.val;
1741
- dictTypeDef.$Allowed.Symbols[symbol] = dictTypeDef.$Allowed.Values[symbol] = valDic;
1758
+ dictTypeDef.$Allowed.Values[symbol] = valDic;
1759
+ dictTypeDef.$Allowed.Symbols[symbol] = valDic;
1742
1760
  }
1743
1761
  }
1744
1762
  return dictTypeDef;
@@ -1747,127 +1765,126 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1747
1765
 
1748
1766
  function initEdmJson() {
1749
1767
  // Static dynamic expression dictionary, loaded with Edm creators
1750
- const dynamicExpressions = {
1751
- '$And': { create: () => { return new Edm.Expr(v, 'And') }, anno: true },
1752
- '$Or': { create: () => { return new Edm.Expr(v, 'Or') }, anno: true },
1753
- '$Not': { create: () => { return new Edm.Expr(v, 'Not') }, anno: true },
1754
- '$Eq': { create: () => { return new Edm.Expr(v, 'Eq') }, anno: true },
1755
- '$Ne': { create: () => { return new Edm.Expr(v, 'Ne') }, anno: true },
1756
- '$Gt': { create: () => { return new Edm.Expr(v, 'Gt') }, anno: true },
1757
- '$Ge': { create: () => { return new Edm.Expr(v, 'Ge') }, anno: true },
1758
- '$Lt': { create: () => { return new Edm.Expr(v, 'Lt') }, anno: true },
1759
- '$Le': { create: () => { return new Edm.Expr(v, 'Le') }, anno: true },
1760
- //valueThingName: 'EnumMember' Implicit Cast Rule String => Primitive Type is OK
1761
- '$Has': { create: () => { return new Edm.Expr(v, 'Has') }, anno: true },
1762
- '$In': { create: () => { return new Edm.Expr(v, 'In') }, anno: true },
1763
- '$Add': { create: () => { return new Edm.Expr(v, 'Add') }, anno: true },
1764
- '$Sub': { create: () => { return new Edm.Expr(v, 'Sub') }, anno: true },
1765
- '$Neg': { create: () => { return new Edm.Expr(v, 'Neg') }, anno: true },
1766
- '$Mul': { create: () => { return new Edm.Expr(v, 'Mul') }, anno: true },
1767
- '$Div': { create: () => { return new Edm.Expr(v, 'Div') }, anno: true },
1768
- '$DivBy': { create: () => { return new Edm.Expr(v, 'DivBy') }, anno: true },
1769
- '$Mod': { create: () => { return new Edm.Expr(v, 'Mod') }, anno: true },
1770
- '$Apply': {
1771
- create: () => { return new Edm.Apply(v) },
1768
+ const dynamicExpressionsP = {
1769
+ $And: { create: () => new Edm.Expr(v, 'And'), anno: true },
1770
+ $Or: { create: () => new Edm.Expr(v, 'Or'), anno: true },
1771
+ $Not: { create: () => new Edm.Expr(v, 'Not'), anno: true },
1772
+ $Eq: { create: () => new Edm.Expr(v, 'Eq'), anno: true },
1773
+ $Ne: { create: () => new Edm.Expr(v, 'Ne'), anno: true },
1774
+ $Gt: { create: () => new Edm.Expr(v, 'Gt'), anno: true },
1775
+ $Ge: { create: () => new Edm.Expr(v, 'Ge'), anno: true },
1776
+ $Lt: { create: () => new Edm.Expr(v, 'Lt'), anno: true },
1777
+ $Le: { create: () => new Edm.Expr(v, 'Le'), anno: true },
1778
+ // valueThingName: 'EnumMember' Implicit Cast Rule String => Primitive Type is OK
1779
+ $Has: { create: () => new Edm.Expr(v, 'Has'), anno: true },
1780
+ $In: { create: () => new Edm.Expr(v, 'In'), anno: true },
1781
+ $Add: { create: () => new Edm.Expr(v, 'Add'), anno: true },
1782
+ $Sub: { create: () => new Edm.Expr(v, 'Sub'), anno: true },
1783
+ $Neg: { create: () => new Edm.Expr(v, 'Neg'), anno: true },
1784
+ $Mul: { create: () => new Edm.Expr(v, 'Mul'), anno: true },
1785
+ $Div: { create: () => new Edm.Expr(v, 'Div'), anno: true },
1786
+ $DivBy: { create: () => new Edm.Expr(v, 'DivBy'), anno: true },
1787
+ $Mod: { create: () => new Edm.Expr(v, 'Mod'), anno: true },
1788
+ $Apply: {
1789
+ create: () => new Edm.Apply(v),
1772
1790
  attr: [ '$Function' ],
1773
- anno: true
1791
+ anno: true,
1774
1792
  },
1775
- '$Cast': {
1776
- create: () => { return new Edm.Cast(v) },
1793
+ $Cast: {
1794
+ create: () => new Edm.Cast(v),
1777
1795
  attr: [ '$Type' ],
1778
1796
  jsonAttr: [ '$Collection' ],
1779
- anno: true
1797
+ anno: true,
1780
1798
  },
1781
- '$IsOf': {
1782
- create: () => { return new Edm.IsOf(v) },
1799
+ $IsOf: {
1800
+ create: () => new Edm.IsOf(v),
1783
1801
  attr: [ '$Type' ],
1784
- anno: true
1802
+ anno: true,
1785
1803
  },
1786
- '$If': { create: () => { return new Edm.If(v) }, anno: true },
1787
- '$LabeledElement': {
1788
- create: () => { return new Edm.LabeledElement(v) },
1804
+ $If: { create: () => new Edm.If(v), anno: true },
1805
+ $LabeledElement: {
1806
+ create: () => new Edm.LabeledElement(v),
1789
1807
  attr: [ '$Name' ],
1790
- anno: true
1808
+ anno: true,
1791
1809
  },
1792
- '$LabeledElementReference': {
1793
- create: (obj) => { return new Edm.LabeledElementReference(v, obj['$LabeledElementReference']); },
1810
+ $LabeledElementReference: {
1811
+ create: obj => new Edm.LabeledElementReference(v, obj.$LabeledElementReference),
1794
1812
  },
1795
- '$UrlRef': { create: () => { return new Edm.UrlRef(v); }, anno: true },
1796
- '$Null': { create: () => { return new Edm.Null(v); }, anno: true, children: false },
1813
+ $UrlRef: { create: () => new Edm.UrlRef(v), anno: true },
1814
+ $Null: { create: () => new Edm.Null(v), anno: true, children: false },
1797
1815
  };
1798
1816
 
1799
- Object.entries(dynamicExpressions).forEach(([k, v]) => {
1800
- if(!v.name)
1801
- v.name = k.slice(1);
1802
- if(v.children === undefined)
1803
- v.children = true;
1817
+ Object.entries(dynamicExpressionsP).forEach(([ k, dv ]) => {
1818
+ if (!dv.name)
1819
+ dv.name = k.slice(1);
1820
+ if (dv.children === undefined)
1821
+ dv.children = true;
1804
1822
  });
1805
- return [ dynamicExpressions, Object.keys(dynamicExpressions) ];
1823
+ return [ dynamicExpressionsP, Object.keys(dynamicExpressionsP) ];
1806
1824
  }
1807
1825
  //-------------------------------------------------------------------------------------------------
1808
1826
  //-------------------------------------------------------------------------------------------------
1809
1827
  //-------------------------------------------------------------------------------------------------
1810
1828
 
1811
- // resolve "derived types"
1829
+ // resolve "derived types"
1812
1830
  // -> if dTypeName is a TypeDefinition, replace by
1813
1831
  // underlying type
1814
- function resolveTypeDefinition(dTypeName) {
1832
+ function resolveTypeDefinition( dTypeName ) {
1815
1833
  const type = getDictType(dTypeName);
1816
- if (type && type.UnderlyingType && type['$kind'] === 'TypeDefinition') {
1834
+ if (type && type.UnderlyingType && type.$kind === 'TypeDefinition')
1817
1835
  return type.UnderlyingType;
1818
- }
1836
+
1819
1837
  return dTypeName;
1820
1838
  }
1821
1839
 
1822
- function stripCollection(typeName) {
1823
- const match = typeName.match(/^Collection\((.+)\)/);
1824
- if (match) {
1825
- typeName = match[1];
1840
+ function stripCollection( typeName ) {
1841
+ if (typeName) {
1842
+ const match = typeName.match(/^Collection\((.+)\)/);
1843
+ if (match)
1844
+ return [ match[1], true ];
1826
1845
  }
1827
- return typeName;
1828
- }
1829
1846
 
1830
- function isPrimitiveType(typeName) {
1831
- return typeName.split('.')[0] === 'Edm';
1847
+ return [ typeName, false ];
1832
1848
  }
1833
1849
 
1834
- function isCollection(typeName) {
1835
- return typeName.match(/^Collection\((.+)\)/) !== null;
1850
+ function isPrimitiveType( typeName ) {
1851
+ return typeName.split('.')[0] === 'Edm';
1836
1852
  }
1837
1853
 
1838
- function isEnumType(dTypeName) {
1854
+ function isEnumType( dTypeName ) {
1839
1855
  const type = getDictType(dTypeName);
1840
- return type && type['$kind'] === 'EnumType';
1856
+ return type && type.$kind === 'EnumType';
1841
1857
  }
1842
1858
 
1843
- function isComplexType(dTypeName) {
1859
+ function isComplexType( dTypeName ) {
1844
1860
  const type = getDictType(dTypeName);
1845
- return dTypeName === 'Edm.ComplexType' || type && type['$kind'] === 'ComplexType';
1861
+ return dTypeName === 'Edm.ComplexType' || type && type.$kind === 'ComplexType';
1846
1862
  }
1847
1863
 
1848
- function isAbstractType(dTypeName) {
1864
+ function isAbstractType( dTypeName ) {
1849
1865
  const type = getDictType(dTypeName);
1850
- return type && type['Abstract'] === 'true';
1866
+ return type && type.Abstract === 'true';
1851
1867
  }
1852
1868
 
1853
1869
  // return true if derived has baseCandidate as direct or indirect base type
1854
- function isDerivedFrom(derived, baseCandidate) {
1870
+ function isDerivedFrom( derived, baseCandidate ) {
1855
1871
  while (derived) {
1856
- if (derived == baseCandidate) return true;
1872
+ if (derived === baseCandidate)
1873
+ return true;
1857
1874
  derived = getDictType(derived).BaseType;
1858
1875
  }
1859
1876
  return false;
1860
1877
  }
1861
1878
 
1862
1879
  // return dictionary of all properties of typeName, including those of base types
1863
- function getAllProperties(typeName) {
1864
- if (!typeName || !getDictType(typeName)) return null;
1880
+ function getAllProperties( typeName ) {
1881
+ if (!typeName || !getDictType(typeName))
1882
+ return null;
1865
1883
  return getDictType(typeName).Properties;
1866
1884
  }
1867
-
1868
1885
  }
1869
1886
 
1870
- function mergeOdataVocabularies(options, message) {
1887
+ function mergeOdataVocabularies( options, message ) {
1871
1888
  /* Merge options.odataVocabularies into vocabularyDefinitions and
1872
1889
  create a csn2edm stack local dictionary.
1873
1890
  odataVocabularies is an object, each property is the
@@ -1878,43 +1895,40 @@ function mergeOdataVocabularies(options, message) {
1878
1895
  inverted index of mergedVocDefs above)
1879
1896
  */
1880
1897
  const mergedVocDefs = Object.assign({}, vocabularyDefinitions);
1881
- const reqProps = ['Alias', 'Namespace', 'Uri'];
1882
- if(options.odataVocabularies) {
1898
+ const reqProps = [ 'Alias', 'Namespace', 'Uri' ];
1899
+ if (options.odataVocabularies) {
1883
1900
  const vocRefs = options.odataVocabularies;
1884
1901
  if (typeof vocRefs === 'object' && !Array.isArray(vocRefs)) {
1885
- Object.entries(vocRefs).forEach(([id, def]) => {
1902
+ Object.entries(vocRefs).forEach(([ id, def ]) => {
1886
1903
  let defOk = true;
1887
- reqProps.forEach(name => {
1888
- if(!def[name] || typeof def[name] !== 'string') {
1904
+ reqProps.forEach((name) => {
1905
+ if (!def[name] || typeof def[name] !== 'string') {
1889
1906
  message('odata-anno-vocref', null,
1890
- { id, name, '#': 'malformed' } );
1907
+ { id, name, '#': 'malformed' } );
1891
1908
  defOk = false;
1892
1909
  }
1893
- else if(name === 'Alias' && !edmUtils.isODataSimpleIdentifier(def[name])) {
1894
- message('odata-spec-violation-id', null, { id:name, value: def[name], '#': 'vocrefalias' });
1910
+ else if (name === 'Alias' && !edmUtils.isODataSimpleIdentifier(def[name])) {
1911
+ message('odata-spec-violation-id', null, { id: name, value: def[name], '#': 'vocrefalias' });
1895
1912
  defOk = false;
1896
1913
  }
1897
1914
  });
1898
- if(defOk) {
1915
+ if (defOk) {
1899
1916
  const vocDef = mergedVocDefs[id];
1900
- if(vocDef && !vocDef.$optVocRef) {
1917
+ if (vocDef && !vocDef.$optVocRef) {
1918
+ message('odata-anno-vocref', null,
1919
+ { id, type: mergedVocDefs[id].inc.Namespace, '#': 'redef' } );
1920
+ }
1921
+ else if (id !== def.Alias) {
1901
1922
  message('odata-anno-vocref', null,
1902
- { id, type: mergedVocDefs[id].inc.Namespace, '#': 'redef' } );
1923
+ { id, name: def.Alias } );
1903
1924
  }
1904
1925
  else {
1905
- if(id !== def.Alias) {
1906
- message('odata-anno-vocref', null,
1907
- { id, name: def.Alias } );
1908
- }
1909
- else {
1910
- // no int.filename => no validation
1911
- const vocDef = {
1912
- ref: { Uri: def.Uri },
1913
- inc: { Alias: def.Alias, Namespace: def.Namespace }
1914
- };
1915
- setProp(vocDef, '$optVocRef', true);
1916
- mergedVocDefs[id] = vocDef;
1917
- }
1926
+ // no int.filename => no validation
1927
+ mergedVocDefs[id] = {
1928
+ ref: { Uri: def.Uri },
1929
+ inc: { Alias: def.Alias, Namespace: def.Namespace },
1930
+ };
1931
+ setProp(mergedVocDefs[id], '$optVocRef', true);
1918
1932
  }
1919
1933
  }
1920
1934
  });