@sap/cds-compiler 4.6.0 → 4.7.4

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 (70) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/bin/cds_update_identifiers.js +6 -2
  3. package/bin/cdsc.js +1 -1
  4. package/doc/CHANGELOG_ARCHIVE.md +9 -9
  5. package/doc/CHANGELOG_BETA.md +6 -0
  6. package/lib/api/main.js +56 -9
  7. package/lib/api/options.js +6 -3
  8. package/lib/api/validate.js +20 -29
  9. package/lib/base/message-registry.js +27 -3
  10. package/lib/base/messages.js +8 -3
  11. package/lib/base/model.js +2 -0
  12. package/lib/checks/dbFeatureFlags.js +28 -0
  13. package/lib/checks/elements.js +81 -13
  14. package/lib/checks/enricher.js +3 -2
  15. package/lib/checks/validator.js +38 -4
  16. package/lib/compiler/assert-consistency.js +4 -4
  17. package/lib/compiler/checks.js +5 -4
  18. package/lib/compiler/define.js +2 -2
  19. package/lib/compiler/generate.js +2 -1
  20. package/lib/compiler/populate.js +5 -1
  21. package/lib/compiler/propagator.js +3 -11
  22. package/lib/compiler/shared.js +2 -1
  23. package/lib/compiler/tweak-assocs.js +43 -24
  24. package/lib/edm/annotations/edmJson.js +3 -0
  25. package/lib/edm/annotations/genericTranslation.js +156 -106
  26. package/lib/edm/annotations/preprocessAnnotations.js +11 -14
  27. package/lib/edm/csn2edm.js +27 -24
  28. package/lib/edm/edm.js +8 -8
  29. package/lib/edm/edmPreprocessor.js +135 -37
  30. package/lib/edm/edmUtils.js +20 -7
  31. package/lib/gen/Dictionary.json +2 -1
  32. package/lib/gen/language.checksum +1 -1
  33. package/lib/gen/language.interp +9 -11
  34. package/lib/gen/languageParser.js +5942 -5446
  35. package/lib/json/to-csn.js +7 -114
  36. package/lib/language/genericAntlrParser.js +106 -48
  37. package/lib/model/cloneCsn.js +203 -0
  38. package/lib/model/csnRefs.js +11 -3
  39. package/lib/model/csnUtils.js +42 -85
  40. package/lib/optionProcessor.js +2 -2
  41. package/lib/render/manageConstraints.js +1 -1
  42. package/lib/render/toCdl.js +133 -88
  43. package/lib/render/toHdbcds.js +1 -5
  44. package/lib/render/toSql.js +7 -9
  45. package/lib/render/utils/common.js +9 -16
  46. package/lib/transform/addTenantFields.js +277 -102
  47. package/lib/transform/db/applyTransformations.js +14 -9
  48. package/lib/transform/db/backlinks.js +2 -1
  49. package/lib/transform/db/constraints.js +60 -82
  50. package/lib/transform/db/expansion.js +6 -6
  51. package/lib/transform/db/featureFlags.js +5 -0
  52. package/lib/transform/db/flattening.js +4 -4
  53. package/lib/transform/db/killAnnotations.js +1 -0
  54. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  55. package/lib/transform/db/transformExists.js +12 -0
  56. package/lib/transform/db/views.js +5 -2
  57. package/lib/transform/draft/odata.js +7 -6
  58. package/lib/transform/effective/associations.js +2 -1
  59. package/lib/transform/effective/main.js +3 -2
  60. package/lib/transform/effective/types.js +6 -3
  61. package/lib/transform/forOdata.js +39 -24
  62. package/lib/transform/forRelationalDB.js +34 -27
  63. package/lib/transform/localized.js +29 -9
  64. package/lib/transform/odata/flattening.js +419 -0
  65. package/lib/transform/odata/toFinalBaseType.js +95 -15
  66. package/lib/transform/odata/typesExposure.js +9 -7
  67. package/lib/transform/transformUtils.js +7 -6
  68. package/lib/transform/translateAssocsToJoins.js +3 -3
  69. package/lib/utils/objectUtils.js +14 -0
  70. package/package.json +1 -1
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const { isEdmPropertyRendered, isBuiltinType } = require('../../model/csnUtils');
3
+ const { isEdmPropertyRendered, isBuiltinType, transformExpression } = require('../../model/csnUtils');
4
4
  const edmUtils = require('../edmUtils.js');
5
5
  const oDataDictionary = require('../../gen/Dictionary.json');
6
6
  const preprocessAnnotations = require('./preprocessAnnotations.js');
7
7
  const { forEachDefinition } = require('../../model/csnUtils');
8
+ // const { csnRefs } = require('../../model/csnRefs');
8
9
  const { isBetaEnabled, setProp } = require('../../base/model.js');
9
10
  const { xpr2edmJson, getEdmJsonHandler } = require('./edmJson.js');
10
11
  const { vocabularyDefinitions } = require('./vocabularyDefinitions.js');
@@ -18,14 +19,16 @@ const { EdmPathTypeMap } = require('../EdmPrimitiveTypeDefinitions.js');
18
19
  * dictReplacement: for test purposes, replaces the standard oDataDictionary
19
20
  */
20
21
  function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
21
- Edm = undefined, options = undefined, messageFunctions = undefined, mergedVocDefs = vocabularyDefinitions ) {
22
+ Edm, options, messageFunctions, mergedVocDefs = vocabularyDefinitions ) {
22
23
  const gAnnosArray = []; // global variable where we store all the generated annotations
23
24
  const usedExperimentalTerms = {}; // take note of all experimental annos that have been used
24
25
  const usedDeprecatedTerms = {}; // take note of all deprecated annos that have been used
25
26
 
26
27
  const { v } = options;
27
- const { message } = messageFunctions;
28
+ const { message, error } = messageFunctions;
28
29
  const { handleEdmJson } = getEdmJsonHandler(Edm, options, messageFunctions, handleTerm);
30
+ // const { inspectRef } = csnRefs(reqDefs);
31
+
29
32
  const [ userDefinedTermDict, allKnownVocabularies ] = createUserDefinedTermDictionary();
30
33
 
31
34
 
@@ -34,7 +37,7 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
34
37
  const whatsMyTermNamespace = anno => allKnownVocabularies.reduce((rc, ns) => (!rc && anno && anno.startsWith(`@${ns}.`) ? ns : rc), undefined);
35
38
 
36
39
  // annotation preprocessing
37
- preprocessAnnotations.preprocessAnnotations(reqDefs, serviceName, options);
40
+ preprocessAnnotations.preprocessAnnotations(reqDefs, serviceName, options, messageFunctions);
38
41
 
39
42
  // we take note of which vocabularies are actually used in a service in order to avoid
40
43
  // producing useless references; reset everything to "unused"
@@ -54,57 +57,27 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
54
57
  { name: serviceName, '#': 'service' } );
55
58
  }
56
59
 
57
- // Copy annotations from origin to parameter entity if it's
58
- // qualified with #$parameters or if its applicable to an EntitySet or Singleton
59
- forEachDefinition(reqDefs, (object) => {
60
- if (object.$isParamEntity && object._origin) {
61
- for (const attr in object._origin) {
62
- if (attr[0] === '@') {
63
- const [ prefix, innerAnnotation ] = attr.split('.@');
64
- const ns = whatsMyTermNamespace(prefix);
65
- if (ns) {
66
- const steps = prefix.replace(`@${ns}.`, '').split('.');
67
- const paramAnnoParts = steps[0].split('#$parameters');
68
- const dictTerm = getDictTerm(`${ns}.${paramAnnoParts[0]}`, options);
69
- if (paramAnnoParts.length > 1 || [ 'Singleton', 'EntitySet' ].some(y => dictTerm?.AppliesTo?.includes(y))) {
70
- steps[0] = `@${ns}.${paramAnnoParts.join('')}`;
71
- let newAnno = steps.join('.');
72
- if (innerAnnotation)
73
- newAnno += `.@${innerAnnotation}`;
74
- edmUtils.assignAnnotation(object, newAnno, object._origin[attr]);
75
- // delete original annotation only if it was qualified with $parameters
76
- if (paramAnnoParts.length > 1)
77
- delete object._origin[attr];
78
- }
79
- }
80
- }
81
- }
82
- }
83
- });
60
+ assignParameterAnnotations();
84
61
 
85
62
  // Crawl over the csn and trigger the annotation translation for all kinds
86
63
  // of annotated things.
87
64
  // Note: only works for single service
88
65
  // Note: we assume that all objects ly flat in the service, i.e. objName always
89
66
  // looks like <service name, can contain dots>.<id>
90
- forEachDefinition(reqDefs, (object, objName) => {
91
- if (objName === serviceName || objName.startsWith(`${serviceName}.`)) {
92
- const location = [ 'definitions', objName ];
93
- if (object.kind === 'action' || object.kind === 'function') {
94
- handleAction(objName, object, null, location);
95
- }
96
- else { // service, entity, anything else?
97
- // handle the annotations directly tied to the object
98
- handleAnnotations(objName, object, location);
99
- // handle the annotations of the object's elements
100
- handleElements(objName, object, location);
101
- // handle the annotations of the object's actions
102
- handleBoundActions(objName, object, location);
103
- }
67
+ forEachDefinition(reqDefs, (def, defName) => {
68
+ if (defName === serviceName || defName.startsWith(`${serviceName}.`)) {
69
+ const location = [ 'definitions', defName ];
70
+ // the <objName> is not the carrier name for <objName>Type
71
+ // and sometimes the object.name doesn't have a service prefix
72
+ if (def.name && def.name.startsWith(`${serviceName}.`))
73
+ defName = def.name;
74
+ if (def.kind === 'action' || def.kind === 'function')
75
+ handleAction(defName, def, null, location);
76
+ else
77
+ handleDefinition(defName, def, location);
104
78
  }
105
79
  });
106
80
 
107
-
108
81
  // filter out empty <Annotations...> elements
109
82
  // add references for the used vocabularies
110
83
  return {
@@ -123,6 +96,77 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
123
96
  return v && v[0];
124
97
  }
125
98
 
99
+ function assignParameterAnnotations() {
100
+ // Copy annotations from origin to parameter entity if it's
101
+ // qualified with #$parameters or if its applicable to an EntitySet or Singleton
102
+ const scopeCheck = {
103
+ ref: (elemref, prop, xpr, path) => {
104
+ if (scopeCheck.scope === 'param' && (!elemref.param || (xpr[0].id || xpr[0]) === '$self'))
105
+ error('odata-anno-xpr-ref', path, { elemref, anno: scopeCheck.anno, '#': 'notaparam' });
106
+ if (scopeCheck.scope === 'type' && elemref.param)
107
+ error('odata-anno-xpr-ref', path, { elemref, anno: scopeCheck.anno, '#': 'notaneelement' });
108
+ },
109
+ };
110
+ const checkDict = (dict, scope) => {
111
+ if (dict) {
112
+ scopeCheck.scope = scope;
113
+ Object.values(dict).forEach((carrier) => {
114
+ const knownAnnos = filterKnownAnnotations(carrier);
115
+ knownAnnos.forEach((pn) => {
116
+ scopeCheck.anno = pn;
117
+ transformExpression(carrier, pn, scopeCheck, carrier.$path);
118
+ });
119
+ });
120
+ }
121
+ };
122
+ const checkDef = (def, scope) => {
123
+ scopeCheck.scope = scope;
124
+ const knownAnnos = filterKnownAnnotations(def);
125
+ knownAnnos.forEach((pn) => {
126
+ scopeCheck.anno = pn;
127
+ transformExpression(def, pn, scopeCheck, def.$path);
128
+ });
129
+ };
130
+ forEachDefinition(reqDefs, (object) => {
131
+ if (object.$isParamEntity && object._origin) {
132
+ // check for correct paths
133
+ if (object._origin.$paramAnnoProxies) {
134
+ object.$eltAnnoProxies = object._origin.$paramAnnoProxies;
135
+ object._origin.$paramAnnoProxies = null;
136
+ checkDict(object.$eltAnnoProxies, 'param');
137
+ }
138
+ checkDict(object._origin.$eltAnnoProxies, 'type');
139
+ checkDict(object.elements, 'param');
140
+ checkDict(object._origin.elements, 'type');
141
+
142
+ scopeCheck.scope = 'param';
143
+ Object.keys(object._origin).forEach((attr) => {
144
+ if (attr[0] === '@') {
145
+ scopeCheck.anno = attr;
146
+ const [ prefix, innerAnnotation ] = attr.split('.@');
147
+ const ns = whatsMyTermNamespace(prefix);
148
+ if (ns) {
149
+ const steps = prefix.replace(`@${ns}.`, '').split('.');
150
+ const paramAnnoParts = steps[0].split('#$parameters');
151
+ const dictTerm = getDictTerm(`${ns}.${paramAnnoParts[0]}`, options);
152
+ if (paramAnnoParts.length > 1 || [ 'Singleton', 'EntitySet' ].some(y => dictTerm?.AppliesTo?.includes(y))) {
153
+ steps[0] = `@${ns}.${paramAnnoParts.join('')}`;
154
+ let newAnno = steps.join('.');
155
+ if (innerAnnotation)
156
+ newAnno += `.@${innerAnnotation}`;
157
+ edmUtils.assignAnnotation(object, newAnno, object._origin[attr]);
158
+ transformExpression(object._origin, attr, scopeCheck, object._origin.$path);
159
+ if (paramAnnoParts.length > 1)
160
+ delete object._origin[attr];
161
+ }
162
+ }
163
+ }
164
+ });
165
+ checkDef(object._origin, 'type');
166
+ }
167
+ });
168
+ }
169
+
126
170
  /*
127
171
  Mapping annotated thing in cds/csn => annotated thing in edmx:
128
172
 
@@ -160,20 +204,27 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
160
204
  like above, but append "/<parameter" to the Target
161
205
  */
162
206
 
163
-
164
- // handle the annotations of the elements of an object
165
- // in: objname : name of the object
166
- // object : the object itself
167
- function handleElements( objname, object, location ) {
168
- if (!object.elements)
169
- return;
170
- Object.entries(object.elements).forEach(([ elemName, element ]) => {
171
- // determine the name of the target in the resulting edm
172
- // for non-assoc element, this simply is "<objectName>/<elementName>"
173
- const edmTargetName = `${objname}/${elemName}`;
174
- const eLocation = [ ...location, 'elements', elemName ];
175
- handleAnnotations(edmTargetName, element, eLocation);
176
- });
207
+ function handleDefinition( defName, def, location ) {
208
+ // definition bound annotations
209
+ handleAnnotations(defName, def, location);
210
+ // definition bound element annotations
211
+ if (def.$eltAnnoProxies) {
212
+ Object.entries(def.$eltAnnoProxies).forEach(([ elemPath, element ]) => {
213
+ const edmTargetName = `${defName}/${elemPath}`;
214
+ handleAnnotations(edmTargetName, element, element.$path);
215
+ });
216
+ }
217
+ // element bound annotations
218
+ if (def.elements) {
219
+ Object.entries(def.elements).forEach(([ elemName, element ]) => {
220
+ const edmTargetName = `${defName}/${elemName}`;
221
+ const eLocation = [ ...location, 'elements', elemName ];
222
+ handleAnnotations(edmTargetName, element, eLocation);
223
+ });
224
+ }
225
+ // bound actions
226
+ if (def.actions)
227
+ handleBoundActions(defName, def, location);
177
228
  }
178
229
 
179
230
  // Annotations for actions and functions (and their parameters)
@@ -188,8 +239,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
188
239
  // in: cObjectname : qualified name of the object that holds the actions
189
240
  // cObject : the object itself
190
241
  function handleBoundActions( cObjectname, cObject, location ) {
191
- if (!cObject.actions)
192
- return;
193
242
  // get service name: remove last part of the object name
194
243
  // only works if all objects ly flat in the service
195
244
  const nameParts = cObjectname.split('.');
@@ -293,12 +342,12 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
293
342
  // Filter unknown toplevel annotations
294
343
  // Final filtering of all annotations is done in handleTerm
295
344
 
296
- let knownAnnos = filterKnownAnnotations();
345
+ let knownAnnos = filterKnownAnnotations(carrier);
297
346
  if (knownAnnos.length === 0)
298
347
  return;
299
348
 
300
349
  if (rewriteInnerAnnotations()) {
301
- knownAnnos = filterKnownAnnotations();
350
+ knownAnnos = filterKnownAnnotations(carrier);
302
351
  if (knownAnnos.length === 0)
303
352
  return;
304
353
  }
@@ -350,47 +399,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
350
399
  gAnnosArray.push(annotations);
351
400
  }
352
401
 
353
- function filterKnownAnnotations() {
354
- const annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
355
- const nullWhitelist = [ '@Core.OperationAvailable' ];
356
- const knownAnnosP = annoNames.filter((n) => {
357
- const tns = whatsMyTermNamespace(n);
358
- return tns &&
359
- (mergedVocDefs[tns] && !mergedVocDefs[tns].$ignore ||
360
- !mergedVocDefs[tns]);
361
- }).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
362
- if (isBetaEnabled(options, 'odataTerms')) {
363
- // Extend knownAnnos with the in-service term definitions
364
- annoNames.forEach((an) => {
365
- const paths = an.slice(1).split('.');
366
- const hasNSPrefix = paths[0] === serviceName;
367
- if (!hasNSPrefix)
368
- paths.splice(0, 0, serviceName);
369
-
370
- const fqName = `@${paths.join('.')}`;
371
- const i = paths[1].indexOf('#');
372
- const termNameWithoutQualifiers = i > 0 ? paths[1].substring(0, i) : paths[1];
373
- const def = reqDefs.definitions[`${paths[0]}.${termNameWithoutQualifiers}`];
374
- // if there is a term definition inside the service and the
375
- // annotation value is != null, then add the annotation to the list
376
- // of known annotations
377
- if (def?.kind === 'annotation' && carrier[an] !== null) {
378
- // Subsequent annotation handler code expects that first path segment
379
- // is the Vocabulary namespace. The ad-hoc namespace is the service
380
- // name itself.
381
- // For service S an annotation assignment could be addressed
382
- // relative or absolute to the service @S.foo or @foo
383
- if (!hasNSPrefix) {
384
- carrier[fqName] = carrier[an];
385
- delete carrier[an];
386
- }
387
- knownAnnosP.push(fqName);
388
- }
389
- });
390
- }
391
- return knownAnnosP;
392
- }
393
-
394
402
  // construct a function that is used to add an <Annotation ...> to the
395
403
  // respective collector array
396
404
  // this function is specific to the actual carrier, following the mapping rules given above
@@ -486,7 +494,7 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
486
494
  // this might be more precise if handleAnnotation would know more about the carrier
487
495
  testToStandardEdmTargetP = (x => (x
488
496
  ? [ 'Parameter', 'Property' ].some(y => x.includes(y) ||
489
- carrier._isCollection && x.includes('Collection'))
497
+ carrier.$isCollection && x.includes('Collection'))
490
498
  : true));
491
499
  }
492
500
  }
@@ -1448,6 +1456,48 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1448
1456
  }
1449
1457
  }
1450
1458
 
1459
+ function filterKnownAnnotations( carrier ) {
1460
+ const annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
1461
+ const nullWhitelist = [ '@Core.OperationAvailable' ];
1462
+ const knownAnnosP = annoNames.filter((n) => {
1463
+ const tns = whatsMyTermNamespace(n);
1464
+ return tns &&
1465
+ (mergedVocDefs[tns] && !mergedVocDefs[tns].$ignore ||
1466
+ !mergedVocDefs[tns]);
1467
+ }).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
1468
+ if (isBetaEnabled(options, 'odataTerms')) {
1469
+ // Extend knownAnnos with the in-service term definitions
1470
+ annoNames.forEach((an) => {
1471
+ const paths = an.slice(1).split('.');
1472
+ const hasNSPrefix = paths[0] === serviceName;
1473
+ if (!hasNSPrefix)
1474
+ paths.splice(0, 0, serviceName);
1475
+
1476
+ const fqName = `@${paths.join('.')}`;
1477
+ const i = paths[1].indexOf('#');
1478
+ const termNameWithoutQualifiers = i > 0 ? paths[1].substring(0, i) : paths[1];
1479
+ const def = reqDefs.definitions[`${paths[0]}.${termNameWithoutQualifiers}`];
1480
+ // if there is a term definition inside the service and the
1481
+ // annotation value is != null, then add the annotation to the list
1482
+ // of known annotations
1483
+ if (def?.kind === 'annotation' && carrier[an] !== null) {
1484
+ // Subsequent annotation handler code expects that first path segment
1485
+ // is the Vocabulary namespace. The ad-hoc namespace is the service
1486
+ // name itself.
1487
+ // For service S an annotation assignment could be addressed
1488
+ // relative or absolute to the service @S.foo or @foo
1489
+ if (!hasNSPrefix) {
1490
+ carrier[fqName] = carrier[an];
1491
+ delete carrier[an];
1492
+ }
1493
+ knownAnnosP.push(fqName);
1494
+ }
1495
+ });
1496
+ }
1497
+ return knownAnnosP;
1498
+ }
1499
+
1500
+
1451
1501
  //-------------------------------------------------------------------------------------------------
1452
1502
  // Dictionary access
1453
1503
  //-------------------------------------------------------------------------------------------------
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { makeMessageFunction } = require('../../base/messages.js');
4
3
  const { forEachDefinition, forEachGeneric } = require('../../model/csnUtils.js');
5
4
 
6
5
 
@@ -14,8 +13,8 @@ const { forEachDefinition, forEachGeneric } = require('../../model/csnUtils.js')
14
13
  * try to proceed with the processing as good as possible.
15
14
  *
16
15
  */
17
- function preprocessAnnotations( csn, serviceName, options ) {
18
- const { message } = makeMessageFunction(csn, options);
16
+ function preprocessAnnotations( csn, serviceName, options, messageFunctions ) {
17
+ const { message } = messageFunctions;
19
18
  const fkSeparator = '_';
20
19
 
21
20
  resolveShortcuts();
@@ -237,20 +236,18 @@ function preprocessAnnotations( csn, serviceName, options ) {
237
236
  // or shortcut expansion array of paths
238
237
  // OR
239
238
  // the (single) non-key string field, if there is one
240
- let textField = null;
239
+ let stringFields = [];
241
240
  const Identification = vlEntity['@UI.Identification'];
242
241
  if (Identification && Identification[0] && Identification[0]['=']) {
243
- textField = Identification[0]['='];
242
+ stringFields.push(Identification[0]['=']);
244
243
  }
245
244
  else if (Identification && Identification[0] && Identification[0].Value && Identification[0].Value['=']) {
246
- textField = Identification[0].Value['='];
245
+ stringFields.push(Identification[0].Value['=']);
247
246
  }
248
247
  else {
249
- const stringFields = Object.keys(vlEntity.elements).filter(
248
+ stringFields = Object.keys(vlEntity.elements).filter(
250
249
  x => !vlEntity.elements[x].key && vlEntity.elements[x].type === 'cds.String'
251
250
  );
252
- if (stringFields.length === 1)
253
- textField = stringFields[0];
254
251
  }
255
252
 
256
253
  // explicitly provided parameters win
@@ -261,12 +258,12 @@ function preprocessAnnotations( csn, serviceName, options ) {
261
258
  LocalDataProperty: { '=': localDataProp },
262
259
  ValueListProperty: valueListProp,
263
260
  } ];
264
- if (textField) {
265
- parameters[1] = {
261
+ stringFields.forEach((n) => {
262
+ parameters.push({
266
263
  $Type: 'Common.ValueListParameterDisplayOnly',
267
- ValueListProperty: textField,
268
- };
269
- }
264
+ ValueListProperty: n,
265
+ });
266
+ });
270
267
  }
271
268
 
272
269
  const newObj = Object.create( Object.getPrototypeOf(carrier) );
@@ -11,7 +11,7 @@ const { initializeModel } = require('./edmPreprocessor.js');
11
11
  const translate = require('./annotations/genericTranslation.js');
12
12
  const { setProp, isBetaEnabled } = require('../base/model');
13
13
  const {
14
- cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType, getUtils,
14
+ isEdmPropertyRendered, isBuiltinType, getUtils,
15
15
  } = require('../model/csnUtils');
16
16
  const { checkCSNVersion } = require('../json/csnVersion');
17
17
  const {
@@ -20,7 +20,8 @@ const {
20
20
  EdmPrimitiveTypeMap,
21
21
  } = require('./EdmPrimitiveTypeDefinitions.js');
22
22
  const { getEdm } = require('./edm.js');
23
-
23
+ const { cloneFullCsn } = require('../model/cloneCsn');
24
+ const { forEach, forEachValue } = require('../utils/objectUtils.js');
24
25
  /*
25
26
  OData V2 spec 06/01/2017 PDF version is available here:
26
27
  https://msdn.microsoft.com/en-us/library/dd541474.aspx
@@ -46,7 +47,7 @@ function csn2edm( _csn, serviceName, _options, messageFunctions ) {
46
47
  */
47
48
  function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
48
49
  // get us a fresh model copy that we can work with
49
- const csn = cloneCsnNonDict(_csn, _options);
50
+ const csn = cloneFullCsn(_csn, _options);
50
51
  const special$self = !csn?.definitions?.$self && '$self';
51
52
  messageFunctions.setModel(csn);
52
53
 
@@ -85,6 +86,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
85
86
  return rc;
86
87
  }
87
88
 
89
+ const reqDefsUtils = getUtils(reqDefs);
90
+
88
91
  if (serviceNames === undefined)
89
92
  serviceNames = options.serviceNames;
90
93
  if (serviceNames) {
@@ -198,7 +201,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
198
201
 
199
202
  if (options.isV4()) {
200
203
  // Add additional schema containers as sub contexts to the service
201
- Object.entries(allSchemas).forEach(([ fqName, art ]) => {
204
+ forEach(allSchemas, (fqName, art) => {
202
205
  if (serviceCsn.name === whatsMyServiceRootName(fqName) &&
203
206
  fqName.startsWith(`${serviceCsn.name}.`)) {
204
207
  if (art.kind === 'reference')
@@ -250,7 +253,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
250
253
  */
251
254
  service._children.forEach((c) => {
252
255
  if (c._ec) {
253
- Object.entries(c._ec._registry).forEach((([ setName, arr ]) => {
256
+ forEach(c._ec._registry, ( setName, arr ) => {
254
257
  if (arr.length > 1) {
255
258
  error(null, null, {
256
259
  name: c._edmAttributes.Namespace,
@@ -258,7 +261,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
258
261
  names: arr.map(a => a.getDuplicateMessage()),
259
262
  }, 'Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for $(NAMES)');
260
263
  }
261
- }));
264
+ });
262
265
  }
263
266
  });
264
267
  // Create annotations and distribute into Schemas, merge vocabulary cross refs into xServiceRefs
@@ -266,7 +269,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
266
269
 
267
270
  // Finally add cross service references into the EDM and extract the targetSchemaNames
268
271
  // for the type cross check
269
- Object.values(xServiceRefs).forEach((ref) => {
272
+ forEachValue(xServiceRefs, (ref) => {
270
273
  const r = new Edm.Reference(v, ref.ref);
271
274
  r.append(new Edm.Include(v, ref.inc));
272
275
  edm._defaultRefs.push(r);
@@ -305,7 +308,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
305
308
 
306
309
  // Sort definitions into their schema container
307
310
  function populateSchemas( schemas ) {
308
- Object.entries(reqDefs.definitions).forEach(([ fqName, art ]) => {
311
+ forEach(reqDefs.definitions, ( fqName, art ) => {
309
312
  // Identify service members by their definition name only, this allows
310
313
  // to let the internal object.name have the sub-schema name.
311
314
  // With nested services we must do a longest path match and check whether
@@ -412,7 +415,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
412
415
 
413
416
  // create the complex types
414
417
  edmUtils.foreach(schemaCsn.definitions,
415
- a => edmUtils.isStructuredType(a) && a.name.startsWith(schemaNamePrefix),
418
+ a => edmUtils.isStructuredType(a) && a.name.startsWith(schemaNamePrefix) && !a.$ignoreInAPI,
416
419
  [ createComplexType, markRendered ]);
417
420
 
418
421
  if (options.isV4()) {
@@ -470,7 +473,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
470
473
  Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
471
474
 
472
475
 
473
- Object.entries(NamesInSchemaXRef).forEach(([ name, refs ]) => {
476
+ forEach(NamesInSchemaXRef, ( name, refs ) => {
474
477
  if (refs.length > 1) {
475
478
  error(null, [ 'definitions', `${Schema._edmAttributes.Namespace}.${name}` ], { name: Schema._edmAttributes.Namespace },
476
479
  'Duplicate name in Schema $(NAME)');
@@ -485,7 +488,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
485
488
  const isSingleton = edmUtils.isSingleton(entityCsn) && options.isV4();
486
489
  const [ properties, hasStream ] = createProperties(entityCsn);
487
490
 
488
- const location = [ 'definitions', entityCsn.name ];
491
+ const location = reqDefs.definitions[entityCsn.name] ? [ 'definitions', entityCsn.name ] : entityCsn.$path;
489
492
  const type = `${schema.name}.${EntityTypeName}`;
490
493
  if (properties.length === 0)
491
494
  warning(null, location, { type }, 'EDM EntityType $(TYPE) has no properties');
@@ -504,7 +507,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
504
507
  if (p._edmAttributes.Name === EntityTypeName)
505
508
  message('odata-spec-violation-property-name', pLoc, { meta: entityCsn.kind });
506
509
 
507
- if (options.isV2() && p._isCollection && !p._csn.target)
510
+ if (options.isV2() && p.$isCollection && !p._csn.target)
508
511
  message('odata-spec-violation-array', pLoc, { version: '2.0' });
509
512
 
510
513
  if (!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name)) {
@@ -554,7 +557,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
554
557
 
555
558
  // put actions behind entity types in Schema/EntityContainer
556
559
  if (entityCsn.actions) {
557
- Object.entries(entityCsn.actions).forEach(([ n, a ]) => {
560
+ forEach(entityCsn.actions, ( n, a ) => {
558
561
  if (options.isV4())
559
562
  createActionV4(a, n, entityCsn);
560
563
  else
@@ -585,7 +588,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
585
588
  message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
586
589
 
587
590
  if (options.isV2()) {
588
- if (p._isCollection && !p._csn.target)
591
+ if (p.$isCollection && !p._csn.target)
589
592
  message('odata-spec-violation-array', pLoc, { version: '2.0' });
590
593
 
591
594
  if (p._csn.target)
@@ -612,7 +615,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
612
615
  const streamProps = [];
613
616
 
614
617
  if (elementsCsn.elements) {
615
- Object.entries(elementsCsn.elements).forEach(([ elementName, elementCsn ]) => {
618
+ forEach(elementsCsn.elements, ( elementName, elementCsn ) => {
616
619
  if (!elementCsn._edmParentCsn)
617
620
  setProp(elementCsn, '_edmParentCsn', edmParentCsn);
618
621
 
@@ -683,10 +686,10 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
683
686
  // derived types are already resolved to base types
684
687
  const attributes = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
685
688
  if (!edmUtils.isODataSimpleIdentifier(attributes.Name))
686
- message('odata-spec-violation-id', [ 'definitions', typeCsn.name ], { id: attributes.Name });
689
+ message('odata-spec-violation-id', typeCsn.$path, { id: attributes.Name });
687
690
 
688
691
  const typeDef = new Edm.TypeDefinition(v, attributes, typeCsn );
689
- edmTypeCompatibilityCheck(typeDef, [ 'definitions', typeCsn.name ]);
692
+ edmTypeCompatibilityCheck(typeDef, typeCsn.$path);
690
693
  Schema.append(typeDef);
691
694
  }
692
695
 
@@ -700,6 +703,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
700
703
  ? [ 'definitions', entityCsn.name, 'actions', actionCsn.name ]
701
704
  : [ 'definitions', actionCsn.name ];
702
705
 
706
+
703
707
  if (!edmUtils.isODataSimpleIdentifier(attributes.Name))
704
708
  message('odata-spec-violation-id', location, { id: attributes.Name });
705
709
 
@@ -779,7 +783,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
779
783
 
780
784
  // Parameter Nodes
781
785
  if (actionCsn.params) {
782
- Object.entries(actionCsn.params).forEach(([ parameterName, parameterCsn ]) => {
786
+ forEach(actionCsn.params, ( parameterName, parameterCsn ) => {
783
787
  const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
784
788
  const pLoc = [ ...location, 'params', p._edmAttributes.Name ];
785
789
  if (!edmUtils.isODataSimpleIdentifier(parameterName))
@@ -857,7 +861,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
857
861
  }
858
862
 
859
863
  // is this still required?
860
- Object.entries(actionCsn).forEach(([ key, val ]) => {
864
+ forEach(actionCsn, ( key, val ) => {
861
865
  if (key.match(/^@sap\./))
862
866
  functionImport.setXml( { [`sap:${key.slice(5).replace(/\./g, '-')}`]: val });
863
867
  });
@@ -888,7 +892,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
888
892
  !edmUtils.isStructuredType(csn.definitions[param._type]))
889
893
  message('odata-spec-violation-param', pLoc, { version: '2.0' });
890
894
 
891
- if (param._isCollection)
895
+ if (param.$isCollection)
892
896
  message('odata-spec-violation-array', pLoc, { version: '2.0' });
893
897
 
894
898
  functionImport.append(param);
@@ -924,7 +928,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
924
928
  message('odata-spec-violation-type-unknown', returnsLoc, { type });
925
929
  }
926
930
  }
927
- if (action.returns._isCollection)
931
+ if (action.returns.$isCollection)
928
932
  type = `Collection(${type})`;
929
933
  }
930
934
  else {
@@ -1038,7 +1042,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
1038
1042
  NamesInSchemaXRef[assocName].push(edmAssociation);
1039
1043
 
1040
1044
  // Add ReferentialConstraints if any
1041
- if (!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
1045
+ if (!navigationProperty.$isCollection && Object.keys(constraints.constraints).length > 0) {
1042
1046
  // A managed composition is treated as association
1043
1047
  if (navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
1044
1048
  edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
@@ -1068,8 +1072,7 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
1068
1072
 
1069
1073
  // generate the Edm.Annotations tree and append it to the corresponding schema
1070
1074
  function addAnnotations2XServiceRefs( ) {
1071
- const { getFinalTypeInfo } = getUtils(csn);
1072
- options.getFinalTypeInfo = getFinalTypeInfo;
1075
+ options.getFinalTypeInfo = reqDefsUtils.getFinalTypeInfo;
1073
1076
  const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
1074
1077
  // distribute edm:Annotations into the schemas
1075
1078
  // Distribute each anno into Schema