@sap/cds-compiler 4.0.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 (101) hide show
  1. package/CHANGELOG.md +200 -5
  2. package/bin/cdsc.js +18 -15
  3. package/doc/CHANGELOG_BETA.md +16 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +33 -13
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +25 -25
  8. package/lib/base/location.js +6 -7
  9. package/lib/base/message-registry.js +123 -42
  10. package/lib/base/messages.js +18 -10
  11. package/lib/base/model.js +43 -10
  12. package/lib/checks/defaultValues.js +6 -6
  13. package/lib/checks/elements.js +11 -10
  14. package/lib/checks/foreignKeys.js +0 -5
  15. package/lib/checks/manyNavigations.js +33 -0
  16. package/lib/checks/onConditions.js +22 -14
  17. package/lib/checks/queryNoDbArtifacts.js +132 -73
  18. package/lib/checks/selectItems.js +4 -55
  19. package/lib/checks/sql-snippets.js +15 -4
  20. package/lib/checks/types.js +3 -3
  21. package/lib/checks/utils.js +4 -3
  22. package/lib/checks/validator.js +3 -1
  23. package/lib/compiler/.eslintrc.json +2 -1
  24. package/lib/compiler/assert-consistency.js +71 -40
  25. package/lib/compiler/base.js +7 -2
  26. package/lib/compiler/builtins.js +40 -41
  27. package/lib/compiler/checks.js +415 -367
  28. package/lib/compiler/classes.js +62 -0
  29. package/lib/compiler/cycle-detector.js +9 -9
  30. package/lib/compiler/define.js +124 -90
  31. package/lib/compiler/extend.js +115 -88
  32. package/lib/compiler/finalize-parse-cdl.js +26 -25
  33. package/lib/compiler/generate.js +57 -49
  34. package/lib/compiler/index.js +56 -56
  35. package/lib/compiler/kick-start.js +10 -7
  36. package/lib/compiler/moduleLayers.js +1 -1
  37. package/lib/compiler/populate.js +180 -144
  38. package/lib/compiler/propagator.js +10 -9
  39. package/lib/compiler/resolve.js +321 -246
  40. package/lib/compiler/shared.js +812 -433
  41. package/lib/compiler/tweak-assocs.js +114 -50
  42. package/lib/compiler/utils.js +241 -46
  43. package/lib/edm/.eslintrc.json +40 -1
  44. package/lib/edm/annotations/genericTranslation.js +721 -707
  45. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  46. package/lib/edm/csn2edm.js +389 -378
  47. package/lib/edm/edm.js +679 -770
  48. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  49. package/lib/edm/edmInboundChecks.js +29 -27
  50. package/lib/edm/edmPreprocessor.js +689 -648
  51. package/lib/edm/edmUtils.js +279 -300
  52. package/lib/gen/Dictionary.json +34 -10
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +1 -1
  55. package/lib/gen/languageParser.js +2857 -2856
  56. package/lib/json/from-csn.js +77 -51
  57. package/lib/json/to-csn.js +15 -15
  58. package/lib/language/antlrParser.js +2 -1
  59. package/lib/language/genericAntlrParser.js +52 -43
  60. package/lib/language/language.g4 +61 -64
  61. package/lib/language/multiLineStringParser.js +2 -0
  62. package/lib/main.d.ts +65 -0
  63. package/lib/model/csnRefs.js +37 -19
  64. package/lib/model/csnUtils.js +51 -18
  65. package/lib/model/revealInternalProperties.js +30 -22
  66. package/lib/modelCompare/compare.js +149 -41
  67. package/lib/modelCompare/utils/filter.js +55 -25
  68. package/lib/optionProcessor.js +21 -9
  69. package/lib/render/manageConstraints.js +20 -17
  70. package/lib/render/toCdl.js +63 -23
  71. package/lib/render/toHdbcds.js +2 -2
  72. package/lib/render/toRename.js +4 -9
  73. package/lib/render/toSql.js +82 -35
  74. package/lib/render/utils/common.js +11 -9
  75. package/lib/render/utils/unique.js +52 -0
  76. package/lib/transform/db/applyTransformations.js +62 -21
  77. package/lib/transform/db/assertUnique.js +7 -8
  78. package/lib/transform/db/associations.js +2 -2
  79. package/lib/transform/db/cdsPersistence.js +9 -9
  80. package/lib/transform/db/constraints.js +47 -17
  81. package/lib/transform/db/expansion.js +138 -68
  82. package/lib/transform/db/flattening.js +98 -30
  83. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  84. package/lib/transform/db/temporal.js +1 -1
  85. package/lib/transform/db/transformExists.js +8 -7
  86. package/lib/transform/db/views.js +73 -33
  87. package/lib/transform/draft/db.js +11 -9
  88. package/lib/transform/draft/odata.js +1 -1
  89. package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
  90. package/lib/transform/forRelationalDB.js +148 -136
  91. package/lib/transform/localized.js +92 -54
  92. package/lib/transform/odata/toFinalBaseType.js +3 -3
  93. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
  94. package/lib/transform/translateAssocsToJoins.js +14 -28
  95. package/lib/utils/file.js +7 -7
  96. package/lib/utils/moduleResolve.js +210 -121
  97. package/lib/utils/objectUtils.js +1 -1
  98. package/package.json +5 -5
  99. package/share/messages/check-proper-type-of.md +1 -1
  100. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  101. package/share/messages/message-explanations.json +1 -1
@@ -6,32 +6,36 @@
6
6
  const NAVPROP_TRENNER = '_';
7
7
  const VALUELIST_NAVPROP_PREFIX = '';
8
8
 
9
- const edmUtils = require('./edmUtils.js')
9
+ const edmUtils = require('./edmUtils.js');
10
10
  const { initializeModel } = require('./edmPreprocessor.js');
11
11
  const translate = require('./annotations/genericTranslation.js');
12
12
  const { setProp, isBetaEnabled } = require('../base/model');
13
13
  const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
14
14
  const { checkCSNVersion } = require('../json/csnVersion');
15
15
  const { makeMessageFunction } = require('../base/messages');
16
- const { EdmTypeFacetMap, EdmTypeFacetNames, EdmPrimitiveTypeMap, getEdm } = require('./edm.js');
16
+ const {
17
+ EdmTypeFacetMap, EdmTypeFacetNames, EdmPrimitiveTypeMap, getEdm,
18
+ } = require('./edm.js');
17
19
 
18
20
  /*
19
21
  OData V2 spec 06/01/2017 PDF version is available from here:
20
22
  https://msdn.microsoft.com/en-us/library/dd541474.aspx
21
23
  */
22
24
 
23
- function csn2edm(_csn, serviceName, _options) {
24
- return csn2edmAll(_csn, _options, [ serviceName ])[ serviceName ];
25
+ function csn2edm( _csn, serviceName, _options ) {
26
+ return csn2edmAll(_csn, _options, [ serviceName ])[serviceName];
25
27
  }
26
28
 
27
- function csn2edmAll(_csn, _options, serviceNames=undefined) {
29
+ function csn2edmAll( _csn, _options, serviceNames = undefined ) {
28
30
  // get us a fresh model copy that we can work with
29
31
  const csn = cloneCsnNonDict(_csn, _options);
30
32
  const special$self = !csn?.definitions?.$self && '$self';
31
33
 
32
34
  // use original options for messages; cloned CSN for semantic location
33
35
  const messageFunctions = makeMessageFunction(csn, _options, 'to.edmx');
34
- const { info, warning, error, message, throwWithError } = messageFunctions;
36
+ const {
37
+ info, warning, error, message, throwWithError,
38
+ } = messageFunctions;
35
39
  checkCSNVersion(csn, _options);
36
40
 
37
41
  let rc = Object.create(null);
@@ -40,7 +44,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
40
44
  // There is the need to assign the odata options because we would like to determine
41
45
  // whether to execute toFinalBaseType in the edmPreprocessor or not
42
46
  if (_csn.meta && _csn.meta.transformation === 'odata' && _csn.meta.options) {
43
- if (!csn.meta) setProp(csn, 'meta', Object.create(null));
47
+ if (!csn.meta)
48
+ setProp(csn, 'meta', Object.create(null));
44
49
  setProp(csn.meta, 'options', _csn.meta.options);
45
50
  }
46
51
 
@@ -50,35 +55,36 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
50
55
  reqDefs,
51
56
  whatsMyServiceRootName,
52
57
  fallBackSchemaName,
53
- options ] = initializeModel(csn, _options, messageFunctions, serviceNames);
58
+ options,
59
+ ] = initializeModel(csn, _options, messageFunctions, serviceNames);
54
60
 
55
61
  const mergedVocabularies = translate.mergeOdataVocabularies(options, message);
56
62
 
57
63
  const Edm = getEdm(options, messageFunctions);
58
64
 
59
- const v = options.v;
60
- if(Object.keys(allServices).length === 0) {
65
+ const { v } = options;
66
+ if (Object.keys(allServices).length === 0) {
61
67
  info(null, null, 'No Services in model');
62
68
  return rc;
63
69
  }
64
70
 
65
- if(serviceNames === undefined)
71
+ if (serviceNames === undefined)
66
72
  serviceNames = options.serviceNames;
67
- if(serviceNames) {
68
- serviceNames.forEach(name => {
69
- let serviceCsn = allServices[name];
70
- if(serviceCsn == undefined) {
73
+ if (serviceNames) {
74
+ serviceNames.forEach((name) => {
75
+ const serviceCsn = allServices[name];
76
+ if (!serviceCsn)
71
77
  warning(null, null, { name }, 'No service definition with name $(NAME) found in the model');
72
- }
73
- else {
78
+
79
+ else
74
80
  rc[name] = createEdm(serviceCsn);
75
- }
76
81
  });
77
82
  }
78
83
  else {
79
84
  rc = Object.values(allServices).reduce((services, serviceCsn) => {
80
85
  services[serviceCsn.name] = createEdm(serviceCsn);
81
- return services; }, rc);
86
+ return services;
87
+ }, rc);
82
88
  }
83
89
 
84
90
  throwWithError();
@@ -87,16 +93,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
87
93
  //--------------------------------------------------------------------------------
88
94
  // embedded functions
89
95
  //--------------------------------------------------------------------------------
90
- function createEdm(serviceCsn) {
91
-
92
- function baseName(str, del) { let l = str.lastIndexOf(del); // eslint-disable-line no-unused-vars
93
- return (l >= 0) ? str.slice(l+del.length, str.length) : str; }
96
+ function createEdm( serviceCsn ) {
97
+ // eslint-disable-next-line no-unused-vars
98
+ function baseName( str, del ) {
99
+ const l = str.lastIndexOf(del);
100
+ return (l >= 0) ? str.slice(l + del.length, str.length) : str;
101
+ }
94
102
 
95
103
  // if we have a real alias take it, otherwise use basename of service
96
104
  // let alias = serviceCsn.alias || baseName(baseName(serviceCsn.name, '::'), '.');
97
105
  // FIXME: UI5 cannot deal with spec conforming simpleid alias names
98
106
 
99
- function markRendered(def) { setProp(def, '$isRendered', true); }
107
+ function markRendered( def ) {
108
+ setProp(def, '$isRendered', true);
109
+ }
100
110
 
101
111
  const service = new Edm.DataServices(v);
102
112
  /** @type {object} */
@@ -137,26 +147,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
137
147
  entries.
138
148
  -----------------------------------------------*/
139
149
  let LeadSchema;
140
- const fqSchemaXRef = [serviceCsn.name];
141
- const whatsMySchemaName = function(n) {
142
- return fqSchemaXRef.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') ? sn : rc, undefined);
143
- }
150
+ const fqSchemaXRef = [ serviceCsn.name ];
151
+ const whatsMySchemaName = n => fqSchemaXRef.reduce((acc, sn) => (!acc && n && n.startsWith(`${sn}.`) ? sn : acc), undefined);
144
152
 
145
153
  // tunnel schema xref and servicename in options to edm.Typebase to rectify
146
154
  // type references that are eventually also prefixed with the service schema name.
147
155
  options.serviceName = serviceCsn.name;
148
- // List of all schema names in this service, including the service itself
156
+ // List of all schema names in this service, including the service itself
149
157
  options.whatsMySchemaName = whatsMySchemaName;
150
158
  options.whatsMyServiceRootName = whatsMyServiceRootName;
151
159
 
152
160
  let xServiceRefs = {};
153
161
  const UsedTypes = {};
154
- function collectUsedType(csn, typeName = (csn.items?.type || csn.type)) {
155
- if(typeName) {
156
- if(UsedTypes[typeName])
157
- UsedTypes[typeName].push(csn)
162
+ function collectUsedType( def, typeName = (def.items?.type || def.type) ) {
163
+ if (typeName) {
164
+ if (UsedTypes[typeName])
165
+ UsedTypes[typeName].push(def);
158
166
  else
159
- UsedTypes[typeName] = [ csn ];
167
+ UsedTypes[typeName] = [ def ];
160
168
  }
161
169
  }
162
170
 
@@ -167,34 +175,34 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
167
175
  fqName: serviceCsn.name,
168
176
  _csn: serviceCsn,
169
177
  container: true,
170
- definitions: Object.create(null)
171
- }
178
+ definitions: Object.create(null),
179
+ },
172
180
  };
173
181
 
174
- if(options.isV4()) {
182
+ if (options.isV4()) {
175
183
  // Add additional schema containers as sub contexts to the service
176
- Object.entries(allSchemas).forEach(([fqName, art]) => {
177
- if(serviceCsn.name === whatsMyServiceRootName(fqName) &&
178
- fqName.startsWith(serviceCsn.name + '.')) {
179
- if(art.kind === 'reference')
184
+ Object.entries(allSchemas).forEach(([ fqName, art ]) => {
185
+ if (serviceCsn.name === whatsMyServiceRootName(fqName) &&
186
+ fqName.startsWith(`${serviceCsn.name}.`)) {
187
+ if (art.kind === 'reference')
180
188
  fqSchemaXRef.push(fqName);
181
- if(art.kind === 'schema') {
189
+ if (art.kind === 'schema') {
182
190
  fqSchemaXRef.push(fqName);
183
191
  // Strip the toplevel service schema name (see comment above)
184
- const name = fqName.replace(serviceCsn.name + '.', '');
192
+ const name = fqName.replace(`${serviceCsn.name}.`, '');
185
193
  subSchemaDictionary[name] = {
186
194
  name,
187
195
  fqName,
188
196
  _csn: art,
189
197
  container: false,
190
- definitions: Object.create(null)
198
+ definitions: Object.create(null),
191
199
  };
192
200
  }
193
201
  }
194
202
  }, subSchemaDictionary);
195
203
 
196
204
  // Sort schema names in reverse order to allow longest match
197
- fqSchemaXRef.sort((a,b) => b.length-a.length);
205
+ fqSchemaXRef.sort((a, b) => b.length - a.length);
198
206
 
199
207
  // Fill the schemas and references, fqSchemaXRef must be complete
200
208
  populateSchemas(subSchemaDictionary);
@@ -202,14 +210,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
202
210
 
203
211
  // Bring the schemas in alphabetical order, service first, root last
204
212
  const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== fallBackSchemaName && n !== serviceCsn.name).sort();
205
- if(subSchemaDictionary[fallBackSchemaName])
213
+ if (subSchemaDictionary[fallBackSchemaName])
206
214
  sortedSchemaNames.push(fallBackSchemaName);
207
215
 
208
216
  // Finally create the schemas and register them in the service.
209
217
  LeadSchema = createSchema(subSchemaDictionary[serviceCsn.name]);
210
218
  service.registerSchema(serviceCsn.name, LeadSchema);
211
219
 
212
- sortedSchemaNames.forEach(name => {
220
+ sortedSchemaNames.forEach((name) => {
213
221
  const schema = subSchemaDictionary[name];
214
222
  service.registerSchema(schema.fqName, createSchema(schema));
215
223
  });
@@ -223,30 +231,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
223
231
  /*
224
232
  EntityContainer duplicate check
225
233
  */
226
- service._children.forEach(c => {
227
- c._ec && Object.entries(c._ec._registry).forEach((([setName, arr]) => {
228
- if(arr.length > 1) {
229
- error(null, null, {
230
- name: c._edmAttributes.Namespace,
231
- id: setName,
232
- names: arr.map(a => a.getDuplicateMessage())
233
- }, 'Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for $(NAMES)');
234
- }
235
- }));
234
+ service._children.forEach((c) => {
235
+ if (c._ec) {
236
+ Object.entries(c._ec._registry).forEach((([ setName, arr ]) => {
237
+ if (arr.length > 1) {
238
+ error(null, null, {
239
+ name: c._edmAttributes.Namespace,
240
+ id: setName,
241
+ names: arr.map(a => a.getDuplicateMessage()),
242
+ }, 'Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for $(NAMES)');
243
+ }
244
+ }));
245
+ }
236
246
  });
237
247
  // Create annotations and distribute into Schemas, merge vocabulary cross refs into xServiceRefs
238
- addAnnotations(xServiceRefs);
248
+ addAnnotations2XServiceRefs();
239
249
 
240
250
  // Finally add cross service references into the EDM and extract the targetSchemaNames
241
251
  // for the type cross check
242
- Object.values(xServiceRefs).forEach(ref => {
243
- let r = new Edm.Reference(v, ref.ref);
244
- r.append(new Edm.Include(v, ref.inc))
252
+ Object.values(xServiceRefs).forEach((ref) => {
253
+ const r = new Edm.Reference(v, ref.ref);
254
+ r.append(new Edm.Include(v, ref.inc));
245
255
  edm._defaultRefs.push(r);
246
256
  });
247
257
 
248
- for(let typeName in UsedTypes) {
249
- if(!isBuiltinType(typeName)) {
258
+ for (const typeName in UsedTypes) {
259
+ if (!isBuiltinType(typeName)) {
250
260
  let iTypeName = typeName;
251
261
  /*
252
262
  Report type ref, if the type is
@@ -255,31 +265,30 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
255
265
  - not a type clash (reported in type exposure),
256
266
  - a @cds.external service member but can't be rendered
257
267
  */
258
- if(!typeName.startsWith(serviceCsn.name + '.'))
259
- iTypeName = serviceCsn.name + '.' + typeName;
268
+ if (!typeName.startsWith(`${serviceCsn.name}.`))
269
+ iTypeName = `${serviceCsn.name}.${typeName}`;
260
270
  const def = reqDefs.definitions[iTypeName];
261
271
 
262
272
  const usages = UsedTypes[typeName].filter(u => !u.$NameClashReported);
263
- if(usages.length > 0 && def && !def.$isRendered && def['@cds.external']) {
273
+ if (usages.length > 0 && def && !def.$isRendered && def['@cds.external']) {
264
274
  message('odata-spec-violation-type', usages[0].$location,
265
- {
266
- type: typeName,
267
- anno: '@cds.external',
268
- name: serviceCsn.name,
269
- code: def.elements ? 'Edm.ComplexType' : 'Edm.TypeDefinition',
270
- version: options.isV4() ? '4.0' : '2.0',
271
- '#': 'external'
272
- }
273
- );
275
+ {
276
+ type: typeName,
277
+ anno: '@cds.external',
278
+ name: serviceCsn.name,
279
+ code: def.elements ? 'Edm.ComplexType' : 'Edm.TypeDefinition',
280
+ version: options.isV4() ? '4.0' : '2.0',
281
+ '#': 'external',
282
+ });
274
283
  }
275
284
  }
276
285
  }
277
286
 
278
- return edm
287
+ return edm;
279
288
 
280
289
  // Sort definitions into their schema container
281
- function populateSchemas(schemas) {
282
- Object.entries(reqDefs.definitions).forEach(([fqName, art]) => {
290
+ function populateSchemas( schemas ) {
291
+ Object.entries(reqDefs.definitions).forEach(([ fqName, art ]) => {
283
292
  // Identify service members by their definition name only, this allows
284
293
  // to let the internal object.name have the sub-schema name.
285
294
  // With nested services we must do a longest path match and check whether
@@ -292,16 +301,16 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
292
301
  // Add this definition to a (sub) schema, if it is not
293
302
  // a container (context, service) and
294
303
  // not marked to be ignored as schema member
295
- if(mySchemaName &&
304
+ if (mySchemaName &&
296
305
  serviceCsn.name === whatsMyServiceRootName(fqName, false) &&
297
306
  art.kind !== 'context' && art.kind !== 'service') {
298
-
299
307
  // Strip the toplevel serviceName from object.name
300
308
  // except if the schema name is the service name itself.
301
309
  // Proxy names are not prefixed, as they need to be reused.
302
- if(mySchemaName !== serviceCsn.name) {
303
- fqName = art.name = fqName.replace(serviceCsn.name + '.', '');
304
- mySchemaName = mySchemaName.replace(serviceCsn.name + '.', '');
310
+ if (mySchemaName !== serviceCsn.name) {
311
+ art.name = fqName.replace(`${serviceCsn.name}.`, '');
312
+ fqName = art.name;
313
+ mySchemaName = mySchemaName.replace(`${serviceCsn.name}.`, '');
305
314
  }
306
315
  schemas[mySchemaName].definitions[fqName] = art;
307
316
  }
@@ -332,30 +341,31 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
332
341
  };
333
342
  */
334
343
 
335
- return Object.entries(allSchemas).reduce((references, [fqName, art]) => {
344
+ return Object.entries(allSchemas).reduce((references, [ fqName, art ]) => {
336
345
  // add references
337
- if(art.kind === 'reference' &&
346
+ if (art.kind === 'reference' &&
338
347
  whatsMySchemaName(fqName) &&
339
- serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
348
+ serviceCsn.name === whatsMyServiceRootName(fqName, false))
340
349
  references[art.inc.Namespace] = art;
341
- }
350
+
342
351
  return references;
343
352
  }, {});
344
353
  }
345
354
 
346
355
  // Main schema creator function
347
- function createSchema(schema) {
356
+ function createSchema( schema ) {
348
357
  /** @type {object} */
349
358
 
350
359
  // Same check for alias (if supported by us)
351
- const reservedNames = ['Edm', 'odata', 'System', 'Transient'];
352
- const loc = ['definitions', schema.name];
353
- if(reservedNames.includes(schema.name))
360
+ const reservedNames = [ 'Edm', 'odata', 'System', 'Transient' ];
361
+ const loc = [ 'definitions', schema.name ];
362
+ if (reservedNames.includes(schema.name))
354
363
  message('odata-spec-violation-namespace', loc, { names: reservedNames });
355
- if (schema.name.length > 511)
364
+ if (schema.name.length > 511) {
356
365
  message('odata-spec-violation-namespace', loc, { '#': 'length' });
366
+ }
357
367
  else {
358
- schema.name.split('.').forEach(id => {
368
+ schema.name.split('.').forEach((id) => {
359
369
  if (!edmUtils.isODataSimpleIdentifier(id))
360
370
  message('odata-spec-violation-id', loc, { id });
361
371
  });
@@ -365,7 +375,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
365
375
  const Schema = new Edm.Schema(v, schema.name, undefined /* unset alias */, schema._csn, /* annotations */ [], schema.container);
366
376
  const EntityContainer = Schema._ec || (LeadSchema && LeadSchema._ec);
367
377
  // now namespace and alias are used to create the fullQualified(name)
368
- const schemaNamePrefix = schema.name + '.'
378
+ const schemaNamePrefix = `${schema.name}.`;
369
379
  const schemaAliasPrefix = schemaNamePrefix;
370
380
  const schemaCsn = schema;
371
381
  const navigationProperties = [];
@@ -376,49 +386,48 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
376
386
  Entity starts with 'localserviceNameized.' or ends with '_localized'
377
387
  */
378
388
  edmUtils.foreach(schemaCsn.definitions,
379
- a => a.kind === 'entity' && !a.abstract && a.name.startsWith(schemaNamePrefix),
380
- [createEntityTypeAndSet, markRendered]
381
- );
389
+ a => a.kind === 'entity' && !a.abstract && a.name.startsWith(schemaNamePrefix),
390
+ [ createEntityTypeAndSet, markRendered ]);
382
391
  // create unbound actions/functions
383
392
  edmUtils.foreach(schemaCsn.definitions,
384
- a => (a.kind === 'action' || a.kind === 'function') && a.name.startsWith(schemaNamePrefix),
385
- [(options.isV4()) ? createActionV4 : createActionV2, markRendered]);
393
+ a => (a.kind === 'action' || a.kind === 'function') && a.name.startsWith(schemaNamePrefix),
394
+ [ (options.isV4()) ? createActionV4 : createActionV2, markRendered ]);
386
395
 
387
- // create the complex types
396
+ // create the complex types
388
397
  edmUtils.foreach(schemaCsn.definitions,
389
- a => edmUtils.isStructuredType(a) && a.name.startsWith(schemaNamePrefix),
390
- [createComplexType, markRendered]);
398
+ a => edmUtils.isStructuredType(a) && a.name.startsWith(schemaNamePrefix),
399
+ [ createComplexType, markRendered ]);
391
400
 
392
- if(options.isV4())
393
- {
401
+ if (options.isV4()) {
394
402
  edmUtils.foreach(schemaCsn.definitions,
395
- artifact => edmUtils.isDerivedType(artifact) &&
396
- !artifact.target&&
403
+ artifact => edmUtils.isDerivedType(artifact) &&
404
+ !artifact.target &&
397
405
  artifact.name.startsWith(schemaNamePrefix),
398
- [createTypeDefinitionV4, markRendered]);
406
+ [ createTypeDefinitionV4, markRendered ]);
399
407
  }
400
408
 
401
- if(isBetaEnabled(options, 'odataTerms')) {
409
+ if (isBetaEnabled(options, 'odataTerms')) {
402
410
  edmUtils.foreach(schemaCsn.definitions,
403
- a => a.kind === 'annotation' && a.name.startsWith(schemaNamePrefix),
404
- createTerm);
411
+ a => a.kind === 'annotation' && a.name.startsWith(schemaNamePrefix),
412
+ createTerm);
405
413
  }
406
414
 
407
415
  // fetch all existing children names in a map
408
416
  const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
409
417
  const name = cur._edmAttributes.Name;
410
- if(acc[name] === undefined) {
418
+ if (acc[name] === undefined)
411
419
  acc[name] = [ cur ];
412
- } else {
420
+
421
+ else
413
422
  acc[name].push(cur);
414
- }
423
+
415
424
  return acc;
416
425
  }, Object.create(null) );
417
426
 
418
- navigationProperties.forEach(np => {
419
- if(options.isV4()) {
427
+ navigationProperties.forEach((np) => {
428
+ if (options.isV4()) {
420
429
  // V4: No referential constraints for Containment Relationships
421
- if((!np.isContainment() || (options.renderForeignKeys)) && !np.isToMany())
430
+ if ((!np.isContainment() || (options.renderForeignKeys)) && !np.isToMany())
422
431
  np.addReferentialConstraintNodes();
423
432
  }
424
433
  else {
@@ -440,122 +449,126 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
440
449
  This sentence expresses that an OData SERVICE must contain an entity container, but an EDMX is not required to have a container.
441
450
  Therefore it is absolutely legal and necessary to remove an empty container from the IR!
442
451
  */
443
- if(Schema._ec && Schema._ec._children.length === 0) {
452
+ if (Schema._ec && Schema._ec._children.length === 0)
444
453
  Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
445
- }
446
454
 
447
- Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
448
- if(refs.length > 1) {
449
- error(null, ['definitions', `${Schema._edmAttributes.Namespace}.${name}`], { name: Schema._edmAttributes.Namespace },
450
- 'Duplicate name in Schema $(NAME)');
455
+
456
+ Object.entries(NamesInSchemaXRef).forEach(([ name, refs ]) => {
457
+ if (refs.length > 1) {
458
+ error(null, [ 'definitions', `${Schema._edmAttributes.Namespace}.${name}` ], { name: Schema._edmAttributes.Namespace },
459
+ 'Duplicate name in Schema $(NAME)');
451
460
  }
452
461
  });
453
462
 
454
463
  return Schema;
455
464
 
456
- function createEntityTypeAndSet(entityCsn)
457
- {
465
+ function createEntityTypeAndSet( entityCsn ) {
458
466
  const EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
459
467
  const EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
460
468
  const isSingleton = edmUtils.isSingleton(entityCsn) && options.isV4();
461
469
  const [ properties, hasStream ] = createProperties(entityCsn);
462
470
 
463
- const loc = ['definitions', entityCsn.name];
471
+ const location = [ 'definitions', entityCsn.name ];
464
472
  const type = `${schema.name}.${EntityTypeName}`;
465
- if(properties.length === 0)
466
- warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no properties');
467
- else if(entityCsn.$edmKeyPaths.length === 0 && !isSingleton)
468
- message('odata-spec-violation-no-key', loc);
473
+ if (properties.length === 0)
474
+ warning(null, location, { type }, 'EDM EntityType $(TYPE) has no properties');
475
+ else if (entityCsn.$edmKeyPaths.length === 0 && !isSingleton)
476
+ message('odata-spec-violation-no-key', location);
469
477
 
470
- if(!edmUtils.isODataSimpleIdentifier(EntityTypeName))
471
- message('odata-spec-violation-id', loc, { id: EntityTypeName });
478
+ if (!edmUtils.isODataSimpleIdentifier(EntityTypeName))
479
+ message('odata-spec-violation-id', location, { id: EntityTypeName });
472
480
 
473
- properties.forEach(p => {
474
- const pLoc = [...loc, 'elements', p._edmAttributes.Name];
481
+ properties.forEach((p) => {
482
+ const pLoc = [ ...location, 'elements', p._edmAttributes.Name ];
475
483
  edmTypeCompatibilityCheck(p, pLoc);
476
- if(p._edmAttributes.Name === EntityTypeName)
484
+ if (p._edmAttributes.Name === EntityTypeName)
477
485
  message('odata-spec-violation-property-name', pLoc, { meta: entityCsn.kind });
478
486
 
479
- if(options.isV2() && p._isCollection && !p._csn.target)
487
+ if (options.isV2() && p._isCollection && !p._csn.target)
480
488
  message('odata-spec-violation-array', pLoc, { version: '2.0' });
481
489
 
482
- if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
490
+ if (!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name)) {
483
491
  message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
492
+ }
484
493
  else if (options.isV2() && /^(_|\d)/.test(p._edmAttributes.Name)) {
485
494
  // FIXME: Rewrite signalIllegalIdentifier function to be more flexible
486
495
  message('odata-spec-violation-id', pLoc,
487
- { prop: p._edmAttributes.Name[0], id: p._edmAttributes.Name, version: '2.0', '#': 'v2firstchar' });
496
+ {
497
+ prop: p._edmAttributes.Name[0], id: p._edmAttributes.Name, version: '2.0', '#': 'v2firstchar',
498
+ });
488
499
  }
489
500
  });
490
501
 
491
502
  // construct EntityType attributes
492
- const attributes = { Name : EntityTypeName };
503
+ const attributes = { Name: EntityTypeName };
493
504
 
494
505
  // CDXCORE-CDXCORE-173
495
- if(options.isV2() && hasStream) {
506
+ if (options.isV2() && hasStream) {
496
507
  attributes['m:HasStream'] = true;
497
508
  edmUtils.assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
498
509
  }
499
510
 
500
511
  Schema.append(new Edm.EntityType(v, attributes, properties, entityCsn));
501
512
 
502
- if (EntityContainer && entityCsn.$hasEntitySet)
503
- {
513
+ if (EntityContainer && entityCsn.$hasEntitySet) {
504
514
  /** @type {object} */
505
515
  let containerEntry;
506
516
 
507
- if(edmUtils.isSingleton(entityCsn) && options.isV4()) {
517
+ if (edmUtils.isSingleton(entityCsn) && options.isV4()) {
508
518
  containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
509
- if(entityCsn['@odata.singleton.nullable'])
510
- containerEntry._edmAttributes.Nullable= true;
519
+ if (entityCsn['@odata.singleton.nullable'])
520
+ containerEntry._edmAttributes.Nullable = true;
511
521
  }
512
522
  else {
513
523
  containerEntry = new Edm.EntitySet(v, { Name: EntitySetName, EntityType: fullQualified(EntityTypeName) }, entityCsn);
514
524
  }
515
525
 
516
526
  // V4: Create NavigationPropertyBinding in EntitySet
517
- if(options.isV4()) {
518
- entityCsn.$edmNPBs.forEach(npb => {
519
- containerEntry.append(new Edm.NavigationPropertyBinding(v, npb))
527
+ if (options.isV4()) {
528
+ entityCsn.$edmNPBs.forEach((npb) => {
529
+ containerEntry.append(new Edm.NavigationPropertyBinding(v, npb));
520
530
  });
521
531
  }
522
532
  EntityContainer.register(containerEntry);
523
533
  }
524
534
 
525
535
  // put actions behind entity types in Schema/EntityContainer
526
- entityCsn.actions && Object.entries(entityCsn.actions).forEach(([ n, a ]) => {
527
- (options.isV4()) ? createActionV4(a, n, entityCsn)
528
- : createActionV2(a, n, entityCsn)
529
- });
536
+ if (entityCsn.actions) {
537
+ Object.entries(entityCsn.actions).forEach(([ n, a ]) => {
538
+ if (options.isV4())
539
+ createActionV4(a, n, entityCsn);
540
+ else
541
+ createActionV2(a, n, entityCsn);
542
+ });
543
+ }
530
544
  }
531
545
 
532
- function createComplexType(structuredTypeCsn)
533
- {
546
+ function createComplexType( structuredTypeCsn ) {
534
547
  // V4 attributes: Name, BaseType, Abstract, OpenType
535
548
  const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
536
549
 
537
550
  const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
538
551
  const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
539
552
  const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
540
- const loc = ['definitions', structuredTypeCsn.name];
553
+ const location = [ 'definitions', structuredTypeCsn.name ];
541
554
 
542
- if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
543
- message('odata-spec-violation-id', loc, { id: attributes.Name });
555
+ if (!edmUtils.isODataSimpleIdentifier(attributes.Name))
556
+ message('odata-spec-violation-id', location, { id: attributes.Name });
544
557
 
545
- properties.forEach(p => {
546
- const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p._edmAttributes.Name ];
558
+ properties.forEach((p) => {
559
+ const pLoc = [ ...location, ...(structuredTypeCsn.items ? [ 'items', 'elements' ] : [ 'elements' ]), p._edmAttributes.Name ];
547
560
  edmTypeCompatibilityCheck(p, pLoc);
548
- if(p._edmAttributes.Name === complexType._edmAttributes.Name)
561
+ if (p._edmAttributes.Name === complexType._edmAttributes.Name)
549
562
  message('odata-spec-violation-property-name', pLoc, { meta: structuredTypeCsn.kind });
550
563
 
551
- if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
564
+ if (!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
552
565
  message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
553
566
 
554
- if(options.isV2()) {
555
- if(p._isCollection && !p._csn.target)
567
+ if (options.isV2()) {
568
+ if (p._isCollection && !p._csn.target)
556
569
  message('odata-spec-violation-array', pLoc, { version: '2.0' });
557
570
 
558
- if(p._csn.target)
571
+ if (p._csn.target)
559
572
  message('odata-spec-violation-assoc', pLoc, { version: '2.0' });
560
573
  }
561
574
  });
@@ -573,86 +586,82 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
573
586
  * array of Edm Properties
574
587
  * hasStream : value of @Core.MediaType assignment
575
588
  */
576
- function createProperties(elementsCsn, edmParentCsn=elementsCsn)
577
- {
589
+ function createProperties( elementsCsn, edmParentCsn = elementsCsn ) {
578
590
  const props = [];
579
591
  let hasStream = false;
580
592
  const streamProps = [];
581
593
 
582
- elementsCsn.elements && Object.entries(elementsCsn.elements).forEach(([elementName, elementCsn]) =>
583
- {
584
- if(elementCsn._edmParentCsn == undefined)
585
- setProp(elementCsn, '_edmParentCsn', edmParentCsn);
594
+ if (elementsCsn.elements) {
595
+ Object.entries(elementsCsn.elements).forEach(([ elementName, elementCsn ]) => {
596
+ if (!elementCsn._edmParentCsn)
597
+ setProp(elementCsn, '_edmParentCsn', edmParentCsn);
586
598
 
587
- if(elementCsn.target) {
588
- // Foreign keys are part of the generic elementCsn.elements property creation
599
+ if (elementCsn.target) {
600
+ // Foreign keys are part of the generic elementCsn.elements property creation
589
601
 
590
602
  // This is the V4 edmx:NavigationProperty
591
603
  // gets rewritten for V2 in addAssociations()
592
604
 
593
605
  // suppress navprop creation only if @odata.navigable:false is not annotated.
594
606
  // (undefined !== false) still evaluates to true
595
- if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
596
- {
597
- const navProp = new Edm.NavigationProperty(v, {
598
- Name: elementName,
599
- Type: elementCsn._target.name
600
- }, elementCsn);
601
- collectUsedType(elementCsn, elementCsn._target.name);
602
- props.push(navProp);
607
+ if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false) {
608
+ const navProp = new Edm.NavigationProperty(v, {
609
+ Name: elementName,
610
+ Type: elementCsn._target.name,
611
+ }, elementCsn);
612
+ collectUsedType(elementCsn, elementCsn._target.name);
613
+ props.push(navProp);
603
614
  // save the navProp in the global array for late constraint building
604
- navigationProperties.push(navProp);
615
+ navigationProperties.push(navProp);
616
+ }
605
617
  }
606
- }
607
618
  // render ordinary property if element is NOT ...
608
619
  // 1) ... annotated @cds.api.ignore
609
620
  // 2) ... annotated @odata.foreignKey4 and odataFormat: structured
610
621
 
611
- else if(isEdmPropertyRendered(elementCsn, options))
612
- {
613
- // CDXCORE-CDXCORE-173
614
- // V2: filter @Core.MediaType
615
- if ( options.isV2() && elementCsn['@Core.MediaType']) {
616
- hasStream = elementCsn['@Core.MediaType'];
617
- delete elementCsn['@Core.MediaType'];
622
+ else if (isEdmPropertyRendered(elementCsn, options)) {
623
+ // CDXCORE-CDXCORE-173
624
+ // V2: filter @Core.MediaType
625
+ if ( options.isV2() && elementCsn['@Core.MediaType']) {
626
+ hasStream = elementCsn['@Core.MediaType'];
627
+ delete elementCsn['@Core.MediaType'];
618
628
  // CDXCORE-CDXCORE-177:
619
629
  // V2: don't render element but add attribute 'm:HasStream="true' to EntityType
620
630
  // V4: render property type 'Edm.Stream'
621
- streamProps.push(elementName);
622
-
623
- } else {
624
- collectUsedType(elementCsn);
625
- props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
631
+ streamProps.push(elementName);
632
+ }
633
+ else {
634
+ collectUsedType(elementCsn);
635
+ props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
636
+ }
626
637
  }
638
+ });
639
+ }
640
+ if (options.isV2()) {
641
+ if (streamProps.length > 1) { // TODO: why not mention 2.0 in text?
642
+ error(null, [ 'definitions', elementsCsn.name ], { names: streamProps, version: '2.0', anno: '@Core.MediaType' },
643
+ 'Expected only one element to be annotated with $(ANNO) for OData $(VERSION) but found $(NAMES)');
627
644
  }
628
-
629
- });
630
- if(options.isV2()) {
631
- if(streamProps.length > 1) { // TODO: why not mention 2.0 in text?
632
- error(null, ['definitions', elementsCsn.name], { names: streamProps, version: '2.0', anno: '@Core.MediaType' },
633
- 'Expected only one element to be annotated with $(ANNO) for OData $(VERSION) but found $(NAMES)');
634
- }
635
- else if(streamProps.length === 1) {
636
- info(null, ['definitions', elementsCsn.name], { id: streamProps[0], version: '2.0', anno: '@Core.MediaType' },
637
- 'Property $(ID) annotated with $(ANNO) is removed from EDM for OData $(VERSION)');
645
+ else if (streamProps.length === 1) {
646
+ info(null, [ 'definitions', elementsCsn.name ], { id: streamProps[0], version: '2.0', anno: '@Core.MediaType' },
647
+ 'Property $(ID) annotated with $(ANNO) is removed from EDM for OData $(VERSION)');
638
648
  }
639
649
  }
640
650
  return [ props, hasStream ];
641
651
  }
642
652
 
643
- function createTerm(termCsn) {
653
+ function createTerm( termCsn ) {
644
654
  const attributes = { Name: termCsn.name.replace(schemaNamePrefix, '') };
645
655
  const term = new Edm.Term(v, attributes, termCsn);
646
656
  Schema.append(term);
647
657
  }
648
658
 
649
659
  // V4 <TypeDefintion>
650
- function createTypeDefinitionV4(typeCsn)
651
- {
660
+ function createTypeDefinitionV4( typeCsn ) {
652
661
  // derived types are already resolved to base types
653
662
  const attributes = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
654
- if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
655
- message('odata-spec-violation-id', ['definitions', typeCsn.name], { id: attributes.Name });
663
+ if (!edmUtils.isODataSimpleIdentifier(attributes.Name))
664
+ message('odata-spec-violation-id', [ 'definitions', typeCsn.name ], { id: attributes.Name });
656
665
 
657
666
  const typeDef = new Edm.TypeDefinition(v, attributes, typeCsn );
658
667
  edmTypeCompatibilityCheck(typeDef, [ 'definitions', typeCsn.name ]);
@@ -660,25 +669,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
660
669
  }
661
670
 
662
671
  // add bound/unbound actions/functions for V4
663
- function createActionV4(actionCsn, _name, entityCsn=undefined)
664
- {
672
+ function createActionV4( actionCsn, _name, entityCsn = undefined ) {
665
673
  const iAmAnAction = actionCsn.kind === 'action';
666
674
  const actionName = edmUtils.getBaseName(actionCsn.name);
667
- const attributes = { Name: actionName, IsBound : false };
675
+ const attributes = { Name: actionName, IsBound: false };
668
676
 
669
- const loc = entityCsn
670
- ? [ 'definitions', entityCsn.name, 'actions', actionCsn.name ]
671
- : [ 'definitions', actionCsn.name ];
677
+ const location = entityCsn
678
+ ? [ 'definitions', entityCsn.name, 'actions', actionCsn.name ]
679
+ : [ 'definitions', actionCsn.name ];
672
680
 
673
- if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
674
- message('odata-spec-violation-id', loc, { id: attributes.Name });
681
+ if (!edmUtils.isODataSimpleIdentifier(attributes.Name))
682
+ message('odata-spec-violation-id', location, { id: attributes.Name });
675
683
 
676
- if(!iAmAnAction)
684
+ if (!iAmAnAction)
677
685
  attributes.IsComposable = false;
678
686
 
679
687
  /** @type {object} */
680
688
  const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
681
- : new Edm.FunctionDefinition(v, attributes);
689
+ : new Edm.FunctionDefinition(v, attributes);
682
690
 
683
691
  const bpType = entityCsn ? fullQualified(entityCsn.name) : undefined;
684
692
  /*
@@ -689,97 +697,91 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
689
697
  */
690
698
 
691
699
  let bpName = 'in';
692
- if(actionCsn.params) {
693
- const entries = Object.entries(actionCsn.params);
700
+ if (actionCsn.params) {
701
+ const entries = Object.entries(actionCsn.params);
694
702
  const firstParam = entries[0][1];
695
703
  const type = firstParam?.items?.type || firstParam?.type;
696
- if(type === special$self) {
704
+ if (type === special$self) {
697
705
  bpName = entries[0][0];
698
706
  setProp(actionCsn, '$bindingParam', firstParam);
699
- if(bpType) {
700
- if(firstParam.items?.type)
707
+ if (bpType) {
708
+ if (firstParam.items?.type)
701
709
  firstParam.items.type = bpType;
702
- if(firstParam.type)
710
+ if (firstParam.type)
703
711
  firstParam.type = bpType;
704
712
  }
705
- if(!edmUtils.isODataSimpleIdentifier(bpName))
706
- message('odata-spec-violation-id', [ ...loc, 'params', bpName ], { id: bpName });
713
+ if (!edmUtils.isODataSimpleIdentifier(bpName))
714
+ message('odata-spec-violation-id', [ ...location, 'params', bpName ], { id: bpName });
707
715
  }
708
716
  }
709
717
 
710
718
  // bpName is eventually used later for EntitySetPath
711
719
  // No explicit binding parameter, check (user defined) annotation value)
712
- if(!actionCsn.$bindingParam) {
720
+ if (!actionCsn.$bindingParam) {
713
721
  const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
714
- if(bpNameAnno != null) {
715
- if(typeof bpNameAnno === 'string')
722
+ if (bpNameAnno != null) {
723
+ if (typeof bpNameAnno === 'string')
716
724
  bpName = bpNameAnno;
717
- if(typeof bpNameAnno === 'object' && bpNameAnno['='])
725
+ if (typeof bpNameAnno === 'object' && bpNameAnno['='])
718
726
  bpName = bpNameAnno['='];
719
727
  }
720
- if(!edmUtils.isODataSimpleIdentifier(bpName))
721
- message('odata-spec-violation-id', [...loc, '@cds.odata.bindingparameter.name'], { id: bpName });
722
- if(actionCsn.params && actionCsn.params[bpName]) {
723
- error('duplicate-definition', [...loc, '@cds.odata.bindingparameter.name'], { '#': 'param', name: bpName });
724
- }
728
+ if (!edmUtils.isODataSimpleIdentifier(bpName))
729
+ message('odata-spec-violation-id', [ ...location, '@cds.odata.bindingparameter.name' ], { id: bpName });
730
+ if (actionCsn.params && actionCsn.params[bpName])
731
+ error('duplicate-definition', [ ...location, '@cds.odata.bindingparameter.name' ], { '#': 'param', name: bpName });
725
732
  }
726
- if(entityCsn != undefined)
727
- {
733
+ if (entityCsn) {
728
734
  actionNode.setEdmAttribute('IsBound', true);
729
- if(!actionCsn.$bindingParam) {
735
+ if (!actionCsn.$bindingParam) {
730
736
  // Binding Parameter: 'in' at first position in sequence, this is decisive!
731
- if(actionCsn['@cds.odata.bindingparameter.collection'])
732
- actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true/*, Nullable: false*/ } ));
737
+ if (actionCsn['@cds.odata.bindingparameter.collection'])
738
+ actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection: true/* , Nullable: false */ } ));
733
739
  else
734
- actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType } ));
740
+ actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType } ));
735
741
  }
736
742
  }
737
- else if(EntityContainer)// unbound => produce Action/FunctionImport
738
- {
743
+ else if (EntityContainer) { // unbound => produce Action/FunctionImport
739
744
  /** @type {object} */
740
745
  const actionImport = iAmAnAction
741
- ? new Edm.ActionImport(v, { Name: actionName, Action : fullQualified(actionName) })
742
- : new Edm.FunctionImport(v, { Name: actionName, Function : fullQualified(actionName) });
746
+ ? new Edm.ActionImport(v, { Name: actionName, Action: fullQualified(actionName) })
747
+ : new Edm.FunctionImport(v, { Name: actionName, Function: fullQualified(actionName) });
743
748
 
744
749
  const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
745
- if(rt) // add EntitySet attribute only if return type is a non abstract entity
746
- {
750
+ if (rt) { // add EntitySet attribute only if return type is a non abstract entity
747
751
  const definition = schemaCsn.definitions[rt];
748
- if(definition && definition.kind === 'entity' && !definition.abstract)
749
- {
752
+ if (definition && definition.kind === 'entity' && !definition.abstract)
750
753
  actionImport.setEdmAttribute('EntitySet', edmUtils.getBaseName(rt));
751
- }
752
754
  }
753
755
  EntityContainer.register(actionImport);
754
756
  }
755
757
 
756
758
  // Parameter Nodes
757
- actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
758
- const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
759
- const pLoc = [ ...loc, 'params', p._edmAttributes.Name ];
760
- if(!edmUtils.isODataSimpleIdentifier(parameterName))
761
- message('odata-spec-violation-id', pLoc, { id: parameterName });
762
- collectUsedType(parameterCsn);
763
- edmTypeCompatibilityCheck(p, pLoc);
764
- actionNode.append(p);
765
- });
759
+ if (actionCsn.params) {
760
+ Object.entries(actionCsn.params).forEach(([ parameterName, parameterCsn ]) => {
761
+ const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
762
+ const pLoc = [ ...location, 'params', p._edmAttributes.Name ];
763
+ if (!edmUtils.isODataSimpleIdentifier(parameterName))
764
+ message('odata-spec-violation-id', pLoc, { id: parameterName });
765
+ collectUsedType(parameterCsn);
766
+ edmTypeCompatibilityCheck(p, pLoc);
767
+ actionNode.append(p);
768
+ });
769
+ }
766
770
 
767
771
  // return type if any
768
- if(actionCsn.returns) {
772
+ if (actionCsn.returns) {
769
773
  actionNode._returnType = new Edm.ReturnType(v, actionCsn.returns);
770
774
  collectUsedType(actionCsn.returns);
771
- edmTypeCompatibilityCheck(actionNode._returnType, [ ...loc, 'returns' ]);
775
+ edmTypeCompatibilityCheck(actionNode._returnType, [ ...location, 'returns' ]);
772
776
  // if binding type matches return type add attribute EntitySetPath
773
- if(entityCsn != undefined && fullQualified(entityCsn.name) === actionNode._returnType._type) {
777
+ if (entityCsn && fullQualified(entityCsn.name) === actionNode._returnType._type)
774
778
  actionNode.setEdmAttribute('EntitySetPath', bpName);
775
- }
776
779
  }
777
780
  Schema.addAction(actionNode);
778
781
  }
779
782
 
780
783
  // add bound/unbound actions/functions for V2
781
- function createActionV2(actionCsn, name, entityCsn=undefined)
782
- {
784
+ function createActionV2( actionCsn, name, entityCsn = undefined ) {
783
785
  /** @type {object} */
784
786
  const attributes = { Name: name.replace(schemaNamePrefix, '') };
785
787
  const functionImport = new Edm.FunctionImport(v, attributes );
@@ -795,117 +797,113 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
795
797
  in the spec and advised mention it as in V4
796
798
  */
797
799
 
798
- const loc = entityCsn
800
+ const location = entityCsn
799
801
  ? [ 'definitions', entityCsn.name, 'actions', actionCsn.name ]
800
802
  : [ 'definitions', actionCsn.name ];
801
803
 
802
- if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
803
- message('odata-spec-violation-id', loc, { id: attributes.Name });
804
+ if (!edmUtils.isODataSimpleIdentifier(attributes.Name))
805
+ message('odata-spec-violation-id', location, { id: attributes.Name });
804
806
 
805
807
  const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
806
- if(rt) // add EntitySet attribute only if return type is an entity
807
- {
808
+ if (rt) { // add EntitySet attribute only if return type is an entity
808
809
  const definition = schemaCsn.definitions[rt];
809
- if(definition && definition.kind === 'entity')
810
- {
810
+ if (definition && definition.kind === 'entity')
811
811
  functionImport.setEdmAttribute('EntitySet', rt.replace(schemaNamePrefix, ''));
812
- }
813
812
  }
814
813
 
815
- if(actionCsn.returns)
814
+ if (actionCsn.returns)
816
815
  functionImport.setEdmAttribute('ReturnType', getReturnType(actionCsn));
817
816
 
818
- if(actionCsn.kind === 'function')
819
- functionImport.setXml( {'m:HttpMethod': 'GET' });
820
- else if(actionCsn.kind === 'action')
821
- functionImport.setXml( {'m:HttpMethod': 'POST'});
817
+ if (actionCsn.kind === 'function')
818
+ functionImport.setXml( { 'm:HttpMethod': 'GET' });
819
+ else if (actionCsn.kind === 'action')
820
+ functionImport.setXml( { 'm:HttpMethod': 'POST' });
822
821
 
823
- if(entityCsn != undefined)
824
- {
822
+ if (entityCsn) {
825
823
  // Make bound function names always unique as per Ralf's recommendation
826
- functionImport.setXml( {'sap:action-for': fullQualified(entityCsn.name) } );
827
- const name = entityCsn.name.replace(schemaNamePrefix, '') + '_' + functionImport._edmAttributes.Name;
828
- functionImport.setEdmAttribute('Name', name);
824
+ functionImport.setXml( { 'sap:action-for': fullQualified(entityCsn.name) } );
825
+ const entityName = `${entityCsn.name.replace(schemaNamePrefix, '')}_${functionImport._edmAttributes.Name}`;
826
+ functionImport.setEdmAttribute('Name', entityName);
829
827
 
830
828
  // Binding Parameter: Primary Keys at first position in sequence, this is decisive!
831
829
  // V2 XML: Nullable=false is set because we reuse the primary key property for the parameter
832
830
  edmUtils.foreach(entityCsn.elements,
833
- elementCsn => elementCsn.key && !elementCsn.target,
834
- (elementCsn, elementName) => {
835
- functionImport.append(new Edm.Parameter(v, { Name: elementName }, elementCsn, 'In' ));
836
- }
837
- );
831
+ elementCsn => elementCsn.key && !elementCsn.target,
832
+ (elementCsn, elementName) => {
833
+ functionImport.append(new Edm.Parameter(v, { Name: elementName }, elementCsn, 'In' ));
834
+ });
838
835
  }
839
836
 
840
837
  // is this still required?
841
- Object.entries(actionCsn).forEach(([p, v]) => {
842
- if (p.match(/^@sap\./))
843
- functionImport.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v });
838
+ Object.entries(actionCsn).forEach(([ key, val ]) => {
839
+ if (key.match(/^@sap\./))
840
+ functionImport.setXml( { [`sap:${key.slice(5).replace(/\./g, '-')}`]: val });
844
841
  });
845
842
  // then append all other parameters
846
843
  // V2 XML: Parameters that are not explicitly marked as Nullable or NotNullable in the CSN must become Nullable=true
847
844
  // V2 XML spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
848
845
  // the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
849
- actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn], i) => {
850
- const type = parameterCsn?.items?.type || parameterCsn?.type;
851
- if(i === 0 && type === special$self) {
846
+ if (actionCsn.params) {
847
+ Object.entries(actionCsn.params).forEach(([ parameterName, parameterCsn ], i) => {
848
+ const type = parameterCsn?.items?.type || parameterCsn?.type;
849
+ if (i === 0 && type === special$self) {
852
850
  // skip and remove the first parameter if it is a $self binding parameter to
853
851
  // omit annotation rendering later on
854
- delete actionCsn.params[parameterName];
855
- }
856
- else {
857
- const pLoc = [...loc, 'params', parameterName];
858
- const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
859
- collectUsedType(parameterCsn);
860
- edmTypeCompatibilityCheck(param, pLoc);
861
- if(!edmUtils.isODataSimpleIdentifier(parameterName))
862
- message('odata-spec-violation-id', pLoc, { id: parameterName });
863
-
864
- // only scalar or structured type in V2 (not entity)
865
- if(param._type &&
852
+ delete actionCsn.params[parameterName];
853
+ }
854
+ else {
855
+ const pLoc = [ ...location, 'params', parameterName ];
856
+ const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
857
+ collectUsedType(parameterCsn);
858
+ edmTypeCompatibilityCheck(param, pLoc);
859
+ if (!edmUtils.isODataSimpleIdentifier(parameterName))
860
+ message('odata-spec-violation-id', pLoc, { id: parameterName });
861
+
862
+ // only scalar or structured type in V2 (not entity)
863
+ if (param._type &&
866
864
  !param._type.startsWith('Edm.') &&
867
865
  csn.definitions[param._type] &&
868
866
  !edmUtils.isStructuredType(csn.definitions[param._type]))
869
- message('odata-spec-violation-param', pLoc, { version: '2.0' });
867
+ message('odata-spec-violation-param', pLoc, { version: '2.0' });
870
868
 
871
- if(param._isCollection)
872
- message('odata-spec-violation-array', pLoc, { version: '2.0' });
869
+ if (param._isCollection)
870
+ message('odata-spec-violation-array', pLoc, { version: '2.0' });
873
871
 
874
- functionImport.append(param);
875
- }
876
- });
872
+ functionImport.append(param);
873
+ }
874
+ });
875
+ }
877
876
 
878
- if(EntityContainer)
877
+ if (EntityContainer)
879
878
  EntityContainer.register(functionImport);
880
879
 
881
- function getReturnType(action)
882
- {
880
+ function getReturnType( action ) {
883
881
  // it is safe to assume that either type or items.type are set
884
- const returnsLoc = [ ...loc, 'returns'];
882
+ const returnsLoc = [ ...location, 'returns' ];
885
883
  const returns = action.returns.items || action.returns;
886
884
  let type = returns['@odata.Type'];
887
- if(!type) {
885
+ if (!type) {
888
886
  type = returns.type;
889
887
  if (type) {
890
888
  collectUsedType(action.returns);
891
889
  if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
892
890
  message('odata-spec-violation-returns', returnsLoc, { kind: action.kind, version: '2.0' });
893
891
  }
894
- else if(isBuiltinType(type)) {
892
+ else if (isBuiltinType(type)) {
895
893
  type = edmUtils.mapCdsToEdmType(returns, messageFunctions, true);
896
- if(type) {
894
+ if (type) {
897
895
  const td = EdmPrimitiveTypeMap[type];
898
- if(td && !td.v2) {
896
+ if (td && !td.v2) {
899
897
  message('odata-spec-violation-type', returnsLoc,
900
- { type, version: '2.0', '#': 'incompatible' });
898
+ { type, version: '2.0', '#': 'incompatible' });
901
899
  }
902
900
  }
903
901
  else {
904
902
  message('odata-spec-violation-type-unknown', returnsLoc, { type });
905
903
  }
906
904
  }
907
- if(action.returns._isCollection)
908
- type = `Collection(${type})`
905
+ if (action.returns._isCollection)
906
+ type = `Collection(${type})`;
909
907
  }
910
908
  else {
911
909
  // type is missing
@@ -930,16 +928,15 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
930
928
  add <End>, <ReferentialConstraint>, <Dependent> and <Principal> sub elements to <Association>
931
929
  add <AssociationSet> to the EntityContainer for each <Association>
932
930
  */
933
- function addAssociationV2(navigationProperty)
934
- {
931
+ function addAssociationV2( navigationProperty ) {
935
932
  let constraints = navigationProperty._csn._constraints;
936
933
  let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
937
934
  let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty._edmAttributes.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
938
935
  let assocName = plainAssocName;
939
936
  let i = 1;
940
- while(NamesInSchemaXRef[assocName] !== undefined) {
941
- assocName = plainAssocName + '_' + i++;
942
- }
937
+ while (NamesInSchemaXRef[assocName] !== undefined)
938
+ assocName = `${plainAssocName}_${i++}`;
939
+
943
940
 
944
941
  let fromRole = parentName;
945
942
  let toRole = navigationProperty._edmAttributes.Type.replace(schemaAliasPrefix, ''); // <= navprops type should be prefixed with alias
@@ -954,8 +951,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
954
951
 
955
952
  // from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })
956
953
 
957
- if(fromRole === toRole) {
958
- if(constraints._partnerCsn)
954
+ if (fromRole === toRole) {
955
+ if (constraints._partnerCsn)
959
956
  fromRole += '1';
960
957
  else
961
958
  toRole += '1';
@@ -980,20 +977,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
980
977
  */
981
978
 
982
979
  let reuseAssoc = false;
983
- let forwardAssocCsn = constraints._partnerCsn;
984
- if(forwardAssocCsn)
985
- {
980
+ const forwardAssocCsn = constraints._partnerCsn;
981
+ if (forwardAssocCsn) {
986
982
  // This is a backlink, swap the roles and types, rewrite assocName
987
983
  [ fromRole, toRole ] = [ toRole, fromRole ];
988
984
  [ fromEntityType, toEntityType ] = [ toEntityType, fromEntityType ];
989
985
  [ fromEntitySet, toEntitySet ] = [ toEntitySet, fromEntitySet ];
990
986
 
991
987
  parentName = forwardAssocCsn._edmParentCsn.name.replace(schemaNamePrefix, '');
992
- assocName = plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
988
+ plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
989
+ assocName = plainAssocName;
993
990
  i = 1;
994
- while(NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association)) {
995
- assocName = plainAssocName + '_' + i++;
996
- }
991
+ while (NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association))
992
+ assocName = `${plainAssocName}_${i++}`;
993
+
997
994
 
998
995
  navigationProperty.setEdmAttribute('Relationship', fullQualified(assocName));
999
996
 
@@ -1002,111 +999,125 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
1002
999
  constraints._multiplicity = edmUtils.determineMultiplicity(forwardAssocCsn);
1003
1000
  }
1004
1001
 
1005
- if(reuseAssoc)
1002
+ if (reuseAssoc)
1006
1003
  return;
1007
1004
 
1008
1005
  // Create Association and AssociationSet if this is not a backlink association.
1009
1006
  // Store association at navigation property because in case the Ends must be modified
1010
1007
  // later by the partner (backlink) association
1011
1008
  const edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
1012
- [ fromRole, fullQualified(fromEntityType) ],
1013
- [ toRole, fullQualified(toEntityType) ],
1014
- constraints._multiplicity );
1015
- if(NamesInSchemaXRef[assocName] === undefined) {
1009
+ [ fromRole, fullQualified(fromEntityType) ],
1010
+ [ toRole, fullQualified(toEntityType) ],
1011
+ constraints._multiplicity );
1012
+ if (NamesInSchemaXRef[assocName] === undefined)
1016
1013
  NamesInSchemaXRef[assocName] = [ edmAssociation ];
1017
- }
1018
- else {
1014
+
1015
+ else
1019
1016
  NamesInSchemaXRef[assocName].push(edmAssociation);
1020
- }
1017
+
1021
1018
  // Add ReferentialConstraints if any
1022
- if(!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
1019
+ if (!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
1023
1020
  // A managed composition is treated as association
1024
- if(navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
1021
+ if (navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
1025
1022
  edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
1026
- toRole, fromRole, constraints.constraints));
1023
+ toRole, fromRole, constraints.constraints));
1027
1024
  }
1028
1025
  else {
1029
1026
  edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
1030
- fromRole, toRole, constraints.constraints));
1027
+ fromRole, toRole, constraints.constraints));
1031
1028
  }
1032
1029
  }
1033
1030
 
1034
1031
  Schema.append(edmAssociation);
1035
- if(EntityContainer && !navigationProperty._targetCsn.$proxy) {
1036
- const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
1037
- fromRole, toRole, fromEntitySet, toEntitySet);
1038
- if(navigationProperty._csn._SetAttributes)
1032
+ if (EntityContainer && !navigationProperty._targetCsn.$proxy) {
1033
+ const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
1034
+ fromRole, toRole, fromEntitySet, toEntitySet);
1035
+ if (navigationProperty._csn._SetAttributes)
1039
1036
  assocSet.setSapVocabularyAsAttributes(navigationProperty._csn._SetAttributes);
1040
1037
  EntityContainer.register(assocSet);
1041
1038
  }
1042
1039
  }
1043
1040
 
1044
1041
  // produce a full qualified name replacing the namespace with the alias (if provided)
1045
- function fullQualified(name)
1046
- {
1047
- return schemaAliasPrefix + name.replace(schemaNamePrefix, '')
1042
+ function fullQualified( name ) {
1043
+ return schemaAliasPrefix + name.replace(schemaNamePrefix, '');
1048
1044
  }
1049
1045
  }
1050
1046
 
1051
1047
  // generate the Edm.Annotations tree and append it to the corresponding schema
1052
- function addAnnotations(xServiceRefs) {
1053
- let { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
1048
+ function addAnnotations2XServiceRefs( ) {
1049
+ const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
1054
1050
  // distribute edm:Annotations into the schemas
1055
1051
  // Distribute each anno into Schema
1056
- annos.forEach(anno => {
1052
+ annos.forEach((anno) => {
1057
1053
  let targetSchema = whatsMySchemaName(anno._edmAttributes.Target);
1058
1054
  // if no target schema has been found, it's a service annotation that applies to the service schema
1059
- if(targetSchema === undefined)
1055
+ if (targetSchema === undefined)
1060
1056
  targetSchema = serviceCsn.name;
1061
- if(targetSchema !== serviceCsn.name) {
1062
- const newTarget = anno._edmAttributes.Target.replace(serviceCsn.name + '.', '');
1057
+ if (targetSchema !== serviceCsn.name) {
1058
+ const newTarget = anno._edmAttributes.Target.replace(`${serviceCsn.name}.`, '');
1063
1059
  anno.setEdmAttribute('Target', newTarget);
1064
1060
  }
1065
1061
  edm._service._schemas[targetSchema]._annotations.push(anno);
1066
1062
  });
1067
1063
 
1068
1064
  // create service cross reference and merge it into xServiceRefs
1069
- xrefs.forEach(xr => {
1070
- if(xr !== serviceCsn.name) {
1065
+ xrefs.forEach((xr) => {
1066
+ if (xr !== serviceCsn.name) {
1071
1067
  const art = edmUtils.createSchemaRef(allServices, xr);
1072
- if(xServiceRefs[art.inc.Namespace] === undefined)
1068
+ if (xServiceRefs[art.inc.Namespace] === undefined)
1073
1069
  xServiceRefs[art.inc.Namespace] = art;
1074
1070
  }
1075
1071
  });
1076
1072
  // merge vocabulary cross references into xServiceRefs
1077
- usedVocabularies.forEach(art => xServiceRefs[art.inc.Namespace] = art);
1073
+ usedVocabularies.forEach((art) => {
1074
+ xServiceRefs[art.inc.Namespace] = art;
1075
+ } );
1078
1076
  }
1079
1077
 
1080
- function edmTypeCompatibilityCheck(p, pLoc) {
1078
+ function edmTypeCompatibilityCheck( p, pLoc ) {
1081
1079
  const edmType = p._type;
1082
- if(!edmType) {
1080
+ if (!edmType) {
1083
1081
  message('odata-spec-violation-type', pLoc);
1084
1082
  }
1085
- else if(p._scalarType) {
1083
+ else if (p._scalarType) {
1086
1084
  const td = EdmPrimitiveTypeMap[edmType];
1087
- if(td) {
1085
+ if (td) {
1088
1086
  // The renderer/type mapper doesn't/shouldn't produce incompatible types and facets.
1089
1087
  // Only the unknown type warning may be triggered by an unknown @odata.Type override.
1090
- if(td.v2 !== p.v2 && td.v4 !== p.v4)
1088
+ if (td.v2 !== p.v2 && td.v4 !== p.v4) {
1091
1089
  message('odata-spec-violation-type', pLoc,
1092
- { type: edmType, version: (p.v4 ? '4.0' : '2.0'), '#': 'incompatible' });
1093
- EdmTypeFacetNames.forEach(name => {
1090
+ { type: edmType, version: (p.v4 ? '4.0' : '2.0'), '#': 'incompatible' });
1091
+ }
1092
+ EdmTypeFacetNames.forEach((name) => {
1094
1093
  const facet = EdmTypeFacetMap[name];
1095
- const optional =
1096
- (facet.optional !== undefined)
1094
+ const optional
1095
+ = (facet.optional !== undefined)
1097
1096
  ? (Array.isArray(facet.optional)
1098
1097
  ? facet.optional.includes(edmType)
1099
1098
  : facet.optional)
1100
- : false;
1099
+ : false;
1101
1100
 
1102
1101
  // facet is not in attributes
1103
1102
  // facet is member of type definition and mandatory
1104
1103
  // node and facet version match
1105
- if(!p._edmAttributes[name] && td[name] && !optional && (p.v2 === facet.v2 || p.v4 === facet.v4)) {
1104
+ if (!p._edmAttributes[name] && td[name] && !optional && (p.v2 === facet.v2 || p.v4 === facet.v4)) {
1106
1105
  message('odata-spec-violation-type', pLoc,
1107
- { type: edmType, name, version: (p.v4 ? '4.0' : '2.0'), '#': 'facet' });
1106
+ {
1107
+ type: edmType, name, version: (p.v4 ? '4.0' : '2.0'), '#': 'facet',
1108
+ });
1108
1109
  }
1109
1110
  });
1111
+ if (edmType === 'Edm.Decimal') {
1112
+ const precision = Number.parseInt(p._edmAttributes.Precision, 10);
1113
+ const scale = Number.parseInt(p._edmAttributes.Scale, 10);
1114
+ if (!Number.isNaN(precision) && !Number.isNaN(scale) && scale > precision) {
1115
+ message('odata-spec-violation-type', pLoc,
1116
+ {
1117
+ type: edmType, number: scale, rawvalue: precision, ersion: (p.v4 ? '4.0' : '2.0'), '#': 'scale',
1118
+ });
1119
+ }
1120
+ }
1110
1121
  }
1111
1122
  else {
1112
1123
  message('odata-spec-violation-type-unknown', pLoc, { type: edmType });