@sap/cds-compiler 4.7.4 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/bin/cdsc.js +15 -1
  3. package/bin/cdshi.js +13 -3
  4. package/doc/CHANGELOG_BETA.md +5 -1
  5. package/lib/api/main.js +61 -23
  6. package/lib/api/options.js +40 -0
  7. package/lib/base/builtins.js +89 -0
  8. package/lib/base/keywords.js +5 -1
  9. package/lib/base/location.js +91 -14
  10. package/lib/base/message-registry.js +50 -33
  11. package/lib/base/messages.js +71 -16
  12. package/lib/base/model.js +0 -2
  13. package/lib/checks/actionsFunctions.js +1 -1
  14. package/lib/checks/elements.js +2 -1
  15. package/lib/checks/enricher.js +2 -2
  16. package/lib/checks/queryNoDbArtifacts.js +2 -1
  17. package/lib/checks/utils.js +1 -1
  18. package/lib/checks/validator.js +6 -22
  19. package/lib/compiler/assert-consistency.js +3 -5
  20. package/lib/compiler/builtins.js +0 -74
  21. package/lib/compiler/checks.js +61 -11
  22. package/lib/compiler/define.js +3 -3
  23. package/lib/compiler/extend.js +2 -2
  24. package/lib/compiler/index.js +9 -9
  25. package/lib/compiler/populate.js +13 -5
  26. package/lib/compiler/propagator.js +3 -0
  27. package/lib/compiler/resolve.js +6 -20
  28. package/lib/compiler/shared.js +1 -1
  29. package/lib/compiler/tweak-assocs.js +2 -2
  30. package/lib/compiler/utils.js +3 -3
  31. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  32. package/lib/edm/annotations/edmJson.js +7 -5
  33. package/lib/edm/annotations/genericTranslation.js +113 -55
  34. package/lib/edm/csn2edm.js +25 -9
  35. package/lib/edm/edm.js +3 -3
  36. package/lib/edm/edmInboundChecks.js +24 -5
  37. package/lib/edm/edmPreprocessor.js +46 -20
  38. package/lib/edm/edmUtils.js +3 -16
  39. package/lib/gen/Dictionary.json +9 -0
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +1941 -1850
  43. package/lib/json/csnVersion.js +7 -4
  44. package/lib/json/from-csn.js +8 -7
  45. package/lib/json/to-csn.js +12 -7
  46. package/lib/language/antlrParser.js +1 -1
  47. package/lib/language/genericAntlrParser.js +9 -10
  48. package/lib/language/multiLineStringParser.js +2 -2
  49. package/lib/language/textUtils.js +1 -1
  50. package/lib/main.d.ts +23 -0
  51. package/lib/main.js +8 -1
  52. package/lib/model/cloneCsn.js +15 -6
  53. package/lib/model/csnRefs.js +141 -35
  54. package/lib/model/csnUtils.js +1 -4
  55. package/lib/model/enrichCsn.js +1 -1
  56. package/lib/modelCompare/compare.js +106 -92
  57. package/lib/optionProcessor.js +23 -1
  58. package/lib/render/toCdl.js +3 -2
  59. package/lib/render/toHdbcds.js +4 -48
  60. package/lib/render/toSql.js +6 -3
  61. package/lib/transform/addTenantFields.js +58 -35
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/expansion.js +3 -0
  64. package/lib/transform/db/flattening.js +71 -46
  65. package/lib/transform/db/views.js +1 -4
  66. package/lib/transform/draft/odata.js +16 -17
  67. package/lib/transform/effective/main.js +6 -3
  68. package/lib/transform/effective/misc.js +18 -8
  69. package/lib/transform/effective/types.js +4 -3
  70. package/lib/transform/forOdata.js +8 -7
  71. package/lib/transform/forRelationalDB.js +103 -112
  72. package/lib/transform/odata/flattening.js +82 -44
  73. package/lib/transform/odata/toFinalBaseType.js +9 -25
  74. package/lib/transform/odata/typesExposure.js +28 -15
  75. package/lib/transform/parseExpr.js +0 -3
  76. package/lib/transform/transformUtils.js +12 -8
  77. package/lib/transform/translateAssocsToJoins.js +2 -2
  78. package/lib/transform/universalCsn/coreComputed.js +2 -1
  79. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
  80. package/package.json +2 -2
  81. package/share/messages/README.md +4 -0
  82. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  83. package/share/messages/check-proper-type-of.md +1 -1
  84. package/share/messages/def-duplicate-autoexposed.md +1 -1
  85. package/share/messages/extend-repeated-intralayer.md +3 -16
  86. package/share/messages/extend-unrelated-layer.md +1 -1
  87. package/share/messages/message-explanations.json +1 -0
  88. package/share/messages/redirected-to-ambiguous.md +1 -1
  89. package/share/messages/redirected-to-complex.md +1 -1
  90. package/share/messages/redirected-to-unrelated.md +1 -1
  91. package/share/messages/rewrite-not-supported.md +1 -1
  92. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  93. package/share/messages/type-missing-enum-value.md +59 -0
  94. package/share/messages/wildcard-excluding-one.md +1 -1
@@ -7,7 +7,7 @@ const {
7
7
  EdmTypeFacetNames,
8
8
  EdmPrimitiveTypeMap,
9
9
  } = require('../EdmPrimitiveTypeDefinitions.js');
10
- const { isBuiltinType, isAnnotationExpression } = require('../../model/csnUtils');
10
+ const { isBuiltinType, isAnnotationExpression } = require('../../base/builtins');
11
11
  const { transformExpression } = require('../../transform/db/applyTransformations.js');
12
12
 
13
13
  /**
@@ -270,7 +270,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
270
270
  evalArgs({ exact: 2 }, xpr, prop);
271
271
  transformExpression(xpr, undefined, transform);
272
272
  delete parent[prop];
273
- parentparent[parentprop].$Apply = [ { $Function: 'odata.concat' }, ...xpr ];
273
+ parentparent[parentprop].$Apply = xpr;
274
+ parentparent[parentprop].$Function = 'odata.concat';
274
275
  };
275
276
  //----------------------------------
276
277
  // ARITHMETICAL AND UNARY
@@ -305,8 +306,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
305
306
  };
306
307
  transform.ref = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
307
308
  if (xpr.some(ps => ps.args || ps.where)) {
308
- error('odata-anno-xpr-args', location, {
309
- anno, elemref: parent, '#': 'wrongref',
309
+ error('odata-anno-xpr-ref', location, {
310
+ anno, elemref: parent, '#': 'args',
310
311
  });
311
312
  }
312
313
  const [ head, ...tail ] = xpr;
@@ -742,7 +743,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
742
743
  }
743
744
  else {
744
745
  evalArgs(argDef, parent.args, xpr);
745
- parentparent[parentprop].$Apply = [ { $Function: funcName }, ...(parent.args || []) ];
746
+ parentparent[parentprop].$Apply = [ ...(parent.args || []) ];
747
+ parentparent[parentprop].$Function = funcName;
746
748
  delete parentparent[parentprop].func;
747
749
  delete parentparent[parentprop].args;
748
750
  transformExpression(parentparent, parentprop, transform);
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const { isEdmPropertyRendered, isBuiltinType, transformExpression } = require('../../model/csnUtils');
3
+ const { isEdmPropertyRendered, transformExpression } = require('../../model/csnUtils');
4
+ const { isBuiltinType } = require('../../base/builtins');
4
5
  const edmUtils = require('../edmUtils.js');
5
6
  const oDataDictionary = require('../../gen/Dictionary.json');
6
7
  const preprocessAnnotations = require('./preprocessAnnotations.js');
7
8
  const { forEachDefinition } = require('../../model/csnUtils');
8
- // const { csnRefs } = require('../../model/csnRefs');
9
9
  const { isBetaEnabled, setProp } = require('../../base/model.js');
10
10
  const { xpr2edmJson, getEdmJsonHandler } = require('./edmJson.js');
11
11
  const { vocabularyDefinitions } = require('./vocabularyDefinitions.js');
@@ -18,7 +18,7 @@ const { EdmPathTypeMap } = require('../EdmPrimitiveTypeDefinitions.js');
18
18
  * v - array with two boolean entries, first is for v2, second is for v4
19
19
  * dictReplacement: for test purposes, replaces the standard oDataDictionary
20
20
  */
21
- function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
21
+ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
22
22
  Edm, options, messageFunctions, mergedVocDefs = vocabularyDefinitions ) {
23
23
  const gAnnosArray = []; // global variable where we store all the generated annotations
24
24
  const usedExperimentalTerms = {}; // take note of all experimental annos that have been used
@@ -27,7 +27,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
27
27
  const { v } = options;
28
28
  const { message, error } = messageFunctions;
29
29
  const { handleEdmJson } = getEdmJsonHandler(Edm, options, messageFunctions, handleTerm);
30
- // const { inspectRef } = csnRefs(reqDefs);
31
30
 
32
31
  const [ userDefinedTermDict, allKnownVocabularies ] = createUserDefinedTermDictionary();
33
32
 
@@ -57,8 +56,10 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
57
56
  { name: serviceName, '#': 'service' } );
58
57
  }
59
58
 
60
- assignParameterAnnotations();
61
-
59
+ forEachDefinition(reqDefs, (def, defName) => {
60
+ if (defName.startsWith(`${serviceName}.`))
61
+ assignParameterAnnotations(def);
62
+ });
62
63
  // Crawl over the csn and trigger the annotation translation for all kinds
63
64
  // of annotated things.
64
65
  // Note: only works for single service
@@ -96,15 +97,23 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
96
97
  return v && v[0];
97
98
  }
98
99
 
99
- function assignParameterAnnotations() {
100
+ function assignParameterAnnotations( def ) {
100
101
  // Copy annotations from origin to parameter entity if it's
101
102
  // qualified with #$parameters or if its applicable to an EntitySet or Singleton
102
103
  const scopeCheck = {
103
104
  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' });
105
+ if (scopeCheck.scope === 'param' && (!elemref.param || (xpr[0].id || xpr[0]) === '$self')) {
106
+ error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaparam' });
107
+ // don't try to resolve those paths later on
108
+ delete elemref[prop];
109
+ }
110
+ if (scopeCheck.scope === 'type' && elemref.param) {
111
+ error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaneelement' });
112
+ delete elemref[prop];
113
+ }
114
+ if (scopeCheck.scope === 'param' && elemref.param)
115
+ // make sure that path is resolvable as element path later on
116
+ delete elemref.param;
108
117
  },
109
118
  };
110
119
  const checkDict = (dict, scope) => {
@@ -119,52 +128,49 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
119
128
  });
120
129
  }
121
130
  };
122
- const checkDef = (def, scope) => {
131
+ const checkObj = (obj, scope) => {
123
132
  scopeCheck.scope = scope;
124
- const knownAnnos = filterKnownAnnotations(def);
133
+ const knownAnnos = filterKnownAnnotations(obj);
125
134
  knownAnnos.forEach((pn) => {
126
135
  scopeCheck.anno = pn;
127
- transformExpression(def, pn, scopeCheck, def.$path);
136
+ transformExpression(obj, pn, scopeCheck, obj.$path);
128
137
  });
129
138
  };
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
- }
139
+ if (def.$isParamEntity && def._origin) {
140
+ // check for correct paths
141
+ if (def._origin.$paramsAnnoProxies) {
142
+ def.$elementsAnnoProxies = def._origin.$paramsAnnoProxies;
143
+ checkDict(def.$elementsAnnoProxies, 'param');
144
+ }
145
+ checkDict(def._origin.$elementsAnnoProxies, 'type');
146
+ checkDict(def.elements, 'param');
147
+ checkDict(def._origin.elements, 'type');
148
+
149
+ scopeCheck.scope = 'param';
150
+ Object.keys(def._origin).forEach((attr) => {
151
+ if (attr[0] === '@') {
152
+ scopeCheck.anno = attr;
153
+ const [ prefix, innerAnnotation ] = attr.split('.@');
154
+ const ns = whatsMyTermNamespace(prefix);
155
+ if (ns) {
156
+ const steps = prefix.replace(`@${ns}.`, '').split('.');
157
+ const paramAnnoParts = steps[0].split('#$parameters');
158
+ const dictTerm = getDictTerm(`${ns}.${paramAnnoParts[0]}`, options);
159
+ if (paramAnnoParts.length > 1 || [ 'Singleton', 'EntitySet' ].some(y => dictTerm?.AppliesTo?.includes(y))) {
160
+ steps[0] = `@${ns}.${paramAnnoParts.join('')}`;
161
+ let newAnno = steps.join('.');
162
+ if (innerAnnotation)
163
+ newAnno += `.@${innerAnnotation}`;
164
+ edmUtils.assignAnnotation(def, newAnno, def._origin[attr]);
165
+ transformExpression(def._origin, attr, scopeCheck, def._origin.$path);
166
+ if (paramAnnoParts.length > 1)
167
+ delete def._origin[attr];
162
168
  }
163
169
  }
164
- });
165
- checkDef(object._origin, 'type');
166
- }
167
- });
170
+ }
171
+ });
172
+ checkObj(def._origin, 'type');
173
+ }
168
174
  }
169
175
 
170
176
  /*
@@ -208,10 +214,10 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
208
214
  // definition bound annotations
209
215
  handleAnnotations(defName, def, location);
210
216
  // definition bound element annotations
211
- if (def.$eltAnnoProxies) {
212
- Object.entries(def.$eltAnnoProxies).forEach(([ elemPath, element ]) => {
217
+ if (def.$elementsAnnoProxies) {
218
+ Object.entries(def.$elementsAnnoProxies).forEach(([ elemPath, element ]) => {
213
219
  const edmTargetName = `${defName}/${elemPath}`;
214
- handleAnnotations(edmTargetName, element, element.$path);
220
+ handleAnnotations(edmTargetName, element, element.$path, [ ...location, '$elementsAnnoProxies', elemPath ]);
215
221
  });
216
222
  }
217
223
  // element bound annotations
@@ -269,12 +275,24 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
269
275
  handleAnnotations(actionName, cAction, location);
270
276
 
271
277
  if (cAction.params) {
278
+ if (cAction.$paramsAnnoProxies) {
279
+ Object.entries(cAction.$paramsAnnoProxies).forEach(([ paramPath, param ]) => {
280
+ const edmTargetName = `${actionName}/${paramPath}`;
281
+ handleAnnotations(edmTargetName, param, param.$path, [ ...location, '$paramsAnnoProxies', paramPath ]);
282
+ });
283
+ }
272
284
  Object.entries(cAction.params).forEach(([ n, p ]) => {
273
285
  const edmTargetName = `${actionName}/${n}`;
274
286
  handleAnnotations(edmTargetName, p, [ ...location, 'params', n ]);
275
287
  });
276
288
  }
277
289
  if (cAction.returns) {
290
+ if (cAction.$returnsAnnoProxies) {
291
+ Object.entries(cAction.$returnsAnnoProxies).forEach(([ returnsPath, returns ]) => {
292
+ const edmTargetName = `${actionName}/${returnsPath}`;
293
+ handleAnnotations(edmTargetName, returns, returns.$path, [ ...location, '$returnsAnnoProxies', returnsPath ]);
294
+ });
295
+ }
278
296
  const edmTargetName = `${actionName}/$ReturnType`;
279
297
  setProp(cAction.returns, '$appliesToReturnType', true);
280
298
  handleAnnotations(edmTargetName, cAction.returns, [ ...location, 'returns' ]);
@@ -324,7 +342,7 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
324
342
  // edmTargetName : string, name of the target in edm
325
343
  // carrier: object, the annotated cds thing, contains all the annotations
326
344
  // as properties with names starting with @
327
- function handleAnnotations( edmTargetName, carrier, location ) {
345
+ function handleAnnotations( edmTargetName, carrier, location, csnPathResolutionLocation = location ) {
328
346
  // collect the names of the carrier's annotation properties
329
347
  // keep only those annotations that - start with a known vocabulary name
330
348
  // - have a value other than null
@@ -353,8 +371,46 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
353
371
  }
354
372
  if (isBetaEnabled(options, 'odataAnnotationExpressions')) {
355
373
  knownAnnos.forEach((knownAnno) => {
356
- if (knownAnno.search(/\.\$edmJson\./g) < 0)
374
+ if (knownAnno.search(/\.\$edmJson\./g) < 0) {
375
+ if (isBetaEnabled(options, 'odataPathsInAnnotationExpressions')) {
376
+ transformExpression(carrier, knownAnno, {
377
+ ref: (elemref, prop, xpr, csnPath) => {
378
+ const { links, scope } = reqDefsUtils.inspectRef(csnPath);
379
+ let i = scope === '$self' ? 1 : 0;
380
+ const getProp = propName => (links[i].art ? (links[i].art[propName] || reqDefsUtils.effectiveType(links[i].art)[propName]) : undefined);
381
+ if (scope === '$magic')
382
+ return;
383
+ let stop = false;
384
+ for (; i < links.length && !stop; i++) {
385
+ const cardinality = getProp('cardinality');
386
+ if ((cardinality && cardinality.max !== 1) || getProp('items')) {
387
+ error('odata-anno-xpr-ref', location, {
388
+ count: i + 1, elemref, anno: knownAnno, '#': 'tomany',
389
+ });
390
+ stop = true;
391
+ }
392
+ if (!isEdmPropertyRendered(links[i].art, csnPath)) {
393
+ error('odata-anno-xpr-ref', location, {
394
+ count: i + 1, elemref, anno: knownAnno, '#': 'notrendered',
395
+ });
396
+ stop = true;
397
+ }
398
+ if (links[i].art?._target?.$proxy && i < links.length - 1) {
399
+ const proxy = links[i].art?._target;
400
+ const eltName = links[i + 1].art?.name;
401
+ if (!proxy.elements[eltName]) {
402
+ error('odata-anno-xpr-ref', location, {
403
+ count: i + 2, elemref, anno: knownAnno, '#': 'notrendered',
404
+ });
405
+ stop = true;
406
+ }
407
+ }
408
+ }
409
+ },
410
+ }, csnPathResolutionLocation);
411
+ }
357
412
  xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
413
+ }
358
414
  });
359
415
  }
360
416
 
@@ -1358,6 +1414,8 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1358
1414
  dict.xrefs[myServiceRoot] = { $myServiceRoot: myServiceRoot, used: false };
1359
1415
  const edmType = new Edm.TypeBase(options.v, {}, annoDef);
1360
1416
  dictDef = edmType._edmAttributes;
1417
+ if (dictDef.Type?.startsWith('Edm.Int'))
1418
+ dictDef.Type = 'Edm.Int';
1361
1419
  dictDef.$myServiceRoot = myServiceRoot;
1362
1420
  let val = annoDef['@odata.term.AppliesTo'];
1363
1421
  if (val != null)
@@ -11,8 +11,9 @@ 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
- isEdmPropertyRendered, isBuiltinType, getUtils,
14
+ isEdmPropertyRendered, getUtils, findAnnotationExpression,
15
15
  } = require('../model/csnUtils');
16
+ const { isBuiltinType } = require('../base/builtins');
16
17
  const { checkCSNVersion } = require('../json/csnVersion');
17
18
  const {
18
19
  EdmTypeFacetMap,
@@ -86,7 +87,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
86
87
  return rc;
87
88
  }
88
89
 
89
- const reqDefsUtils = getUtils(reqDefs);
90
+ // refresh csn cache after preprocessor model augmentation with parameters, types, proxies etc
91
+ const csnUtils = getUtils(csn);
90
92
 
91
93
  if (serviceNames === undefined)
92
94
  serviceNames = options.serviceNames;
@@ -744,17 +746,31 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
744
746
  // bpName is eventually used later for EntitySetPath
745
747
  // No explicit binding parameter, check (user defined) annotation value)
746
748
  if (!actionCsn.$bindingParam) {
747
- const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
749
+ const bpnAnnoName = '@cds.odata.bindingparameter.name';
750
+ const bpnAnnoLoc = [ ...location, bpnAnnoName ];
751
+ const bpNameAnno = actionCsn[bpnAnnoName];
748
752
  if (bpNameAnno != null) {
749
753
  if (typeof bpNameAnno === 'string')
750
754
  bpName = bpNameAnno;
751
- if (typeof bpNameAnno === 'object' && bpNameAnno['='])
752
- bpName = bpNameAnno['='];
755
+ if (typeof bpNameAnno === 'object' && bpNameAnno['=']) {
756
+ if (findAnnotationExpression(actionCsn, bpnAnnoName)) {
757
+ if (!bpNameAnno.ref)
758
+ message('odata-anno-xpr', bpnAnnoLoc, { anno: bpnAnnoName, '#': 'unexpected' });
759
+ else if (bpNameAnno.ref.length !== 1)
760
+ error('odata-anno-xpr-ref', bpnAnnoLoc, { anno: bpnAnnoName, elemref: bpNameAnno, '#': 'invalid' });
761
+ else
762
+ bpName = bpNameAnno.ref[0];
763
+ }
764
+ else {
765
+ bpName = bpNameAnno['='];
766
+ }
767
+ }
753
768
  }
769
+
754
770
  if (!edmUtils.isODataSimpleIdentifier(bpName))
755
- message('odata-spec-violation-id', [ ...location, '@cds.odata.bindingparameter.name' ], { id: bpName });
771
+ message('odata-spec-violation-id', bpnAnnoLoc, { id: bpName });
756
772
  if (actionCsn.params && actionCsn.params[bpName])
757
- error('duplicate-definition', [ ...location, '@cds.odata.bindingparameter.name' ], { '#': 'param', name: bpName });
773
+ error('duplicate-definition', bpnAnnoLoc, { '#': 'param', name: bpName });
758
774
  }
759
775
  if (entityCsn) {
760
776
  actionNode.setEdmAttribute('IsBound', true);
@@ -1072,8 +1088,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
1072
1088
 
1073
1089
  // generate the Edm.Annotations tree and append it to the corresponding schema
1074
1090
  function addAnnotations2XServiceRefs( ) {
1075
- options.getFinalTypeInfo = reqDefsUtils.getFinalTypeInfo;
1076
- const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
1091
+ options.getFinalTypeInfo = csnUtils.getFinalTypeInfo;
1092
+ const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csnUtils, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
1077
1093
  // distribute edm:Annotations into the schemas
1078
1094
  // Distribute each anno into Schema
1079
1095
  annos.forEach((anno) => {
package/lib/edm/edm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const edmUtils = require('./edmUtils.js');
4
- const { isBuiltinType } = require('../model/csnUtils.js');
3
+ const edmUtils = require('./edmUtils');
4
+ const { isBuiltinType } = require('../base/builtins');
5
5
  const { forEach } = require('../utils/objectUtils');
6
6
  const {
7
7
  EdmTypeFacetMap,
@@ -782,7 +782,7 @@ function getEdm( options, messageFunctions ) {
782
782
  super(version, attributes, csn);
783
783
  const appliesTo = csn['@odata.term.AppliesTo'];
784
784
  if (appliesTo)
785
- this.setEdmAttribute('AppliesTo', Array.isArray(appliesTo) ? appliesTo.map(v => v['='] || v).join(' ') : appliesTo['='] || appliesTo);
785
+ this.setEdmAttribute('AppliesTo', Array.isArray(appliesTo) ? appliesTo.join(' ') : appliesTo);
786
786
  }
787
787
  }
788
788
 
@@ -2,8 +2,9 @@
2
2
 
3
3
  const { setProp, isBetaEnabled } = require('../base/model');
4
4
  const {
5
- forEachDefinition, forEachMemberRecursively, isBuiltinType, getUtils,
5
+ forEachDefinition, forEachMemberRecursively, getUtils,
6
6
  } = require('../model/csnUtils');
7
+ const { isBuiltinType } = require('../base/builtins');
7
8
  const { assignAnnotation } = require('./edmUtils.js');
8
9
 
9
10
  // eslint-disable-next-line no-unused-vars
@@ -49,6 +50,15 @@ function inboundQualificationChecks( csn, options, messageFunctions,
49
50
  // check items.items
50
51
  checkIfItemsOfItems(def, undefined, undefined, location);
51
52
  forEachMemberRecursively(def, checkIfItemsOfItems, location);
53
+ checkIfItemsOfItems(def.returns, undefined, undefined, location.concat('returns'));
54
+ if (def.actions) {
55
+ Object.entries(def.actions).forEach(([ n, action ]) => {
56
+ const aLoc = location.concat('actions', n);
57
+ checkIfItemsOfItems(action, undefined, undefined, aLoc);
58
+ forEachMemberRecursively(action, checkIfItemsOfItems, aLoc);
59
+ checkIfItemsOfItems(action.returns, undefined, undefined, aLoc.concat('returns'));
60
+ });
61
+ }
52
62
 
53
63
  // decorate UUID keys with @Core.ComputedDefaultValue and complain
54
64
  // on named type UUID elements that have no such annotation
@@ -88,14 +98,23 @@ function inboundQualificationChecks( csn, options, messageFunctions,
88
98
  }
89
99
 
90
100
  function checkIfItemsOfItems( member, _memberName, _prop, path ) {
101
+ if (!member)
102
+ return;
91
103
  const memberType = csnUtils.effectiveType(member);
92
- if (memberType.items) {
93
- if (memberType.items.target) {
94
- const isComp = memberType.items.type === 'cds.Composition';
104
+ let { items } = memberType;
105
+ if (items) {
106
+ if (items.target) {
107
+ const isComp = items.type === 'cds.Composition';
95
108
  message('type-invalid-items', path, { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
96
109
  return;
97
110
  }
98
- if (memberType.items.items) {
111
+ let i = 1;
112
+ while (items) {
113
+ items = items.items;
114
+ if (items)
115
+ i++;
116
+ }
117
+ if (i > 1) {
99
118
  message('chained-array-of', path);
100
119
  return;
101
120
  }
@@ -5,9 +5,10 @@ const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model')
5
5
  const {
6
6
  forEachDefinition, forEachGeneric, forEachMemberRecursively,
7
7
  isEdmPropertyRendered, getUtils,
8
- isBuiltinType, applyTransformations, transformExpression, findAnnotationExpression,
9
- cardinality2str, isMagicVariable,
8
+ applyTransformations, transformExpression, findAnnotationExpression,
9
+ cardinality2str,
10
10
  } = require('../model/csnUtils');
11
+ const { isBuiltinType, isMagicVariable } = require('../base/builtins');
11
12
  const edmUtils = require('./edmUtils.js');
12
13
  const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
13
14
  const { inboundQualificationChecks } = require('./edmInboundChecks.js');
@@ -540,7 +541,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
540
541
  // preserve the original target for constraint calculation
541
542
  setProp(entityCsn.$sources[n], '_originalTarget', entityCsn.$sources[n]._target);
542
543
  entityCsn.$sources[n]._target = parameterCsn;
543
- entityCsn.$sources[n].target = parameterCsn.name;
544
544
  });
545
545
  }
546
546
 
@@ -1905,6 +1905,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1905
1905
  markCollection(member.returns);
1906
1906
  mapCdsToEdmProp(member.returns);
1907
1907
  annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
1908
+ rewriteAnnotationExpressions(member.returns);
1908
1909
  }
1909
1910
  }, defLocation);
1910
1911
  // mark members that need to be rendered as collections
@@ -2038,11 +2039,12 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2038
2039
 
2039
2040
  if (edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
2040
2041
  const localAssocPath = path.join('.');
2041
- let navPropEntry = localRestrictions.find(p => p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
2042
+ const laprefix = prefix.concat(localAssocPath).join('.');
2043
+ let navPropEntry = localRestrictions.find(p => p.NavigationProperty && p.NavigationProperty['='] === laprefix);
2042
2044
  const hasEntry = !!navPropEntry;
2043
2045
 
2044
2046
  if (!hasEntry)
2045
- navPropEntry = { NavigationProperty: { '=': prefix.concat(localAssocPath).join('.') } };
2047
+ navPropEntry = { NavigationProperty: { '=': laprefix } };
2046
2048
 
2047
2049
 
2048
2050
  const props = Object.entries(containee);
@@ -2172,8 +2174,8 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2172
2174
  // rewrite annotation expression paths such that they are defined against the definition
2173
2175
  const absPath = $path2path(carrier.$path);
2174
2176
  let isSubTreeSpan = true;
2175
- const rootPrefix = absPath.slice(1, absPath.length - 1);
2176
2177
 
2178
+ let rootPrefix = absPath.slice(1, absPath.length - 1);
2177
2179
  const isExprAnno = (obj, pn) => isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
2178
2180
 
2179
2181
  const subTreeSpan = {
@@ -2181,8 +2183,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2181
2183
  const head = xpr[0].id || xpr[0];
2182
2184
  if (head === '$self' || parent.param) {
2183
2185
  let j = parent.param ? 0 : 1;
2186
+ const k = parent.param ? 1 : 0;
2187
+ isSubTreeSpan = isSubTreeSpan && (absPath.length - k <= xpr.length);
2184
2188
  for (let i = 1; i < absPath.length - 1 && isSubTreeSpan; i++, j++)
2185
- isSubTreeSpan = (xpr[j].id || xpr[j]) === absPath[i];
2189
+ isSubTreeSpan = isSubTreeSpan && (xpr[j].id || xpr[j]) === absPath[i];
2186
2190
  }
2187
2191
  },
2188
2192
  };
@@ -2197,10 +2201,13 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2197
2201
  for (let i = 1; i < absPath.length - 1 && absPathPrefixEqual; i++, j++)
2198
2202
  absPathPrefixEqual = (xpr[j].id || xpr[j]) === absPath[i];
2199
2203
 
2200
- if (absPathPrefixEqual)
2204
+ if (absPathPrefixEqual) {
2201
2205
  // remove prefix between $self and leaf element name
2202
2206
  // or starting with the parameter name
2203
2207
  xpr.splice(parent.param ? 0 : 1, absPath.length - 2);
2208
+ if (parent.param)
2209
+ parent.param = null;
2210
+ }
2204
2211
  }
2205
2212
  },
2206
2213
  };
@@ -2224,6 +2231,29 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2224
2231
  acc[isExprAnno(carrier, pn) ? 0 : 1].push(pn);
2225
2232
  return acc;
2226
2233
  }, [ [], [] ]);
2234
+
2235
+ let scope = carrier.$path[2];
2236
+ let def = csn.definitions[carrier.$path[1]];
2237
+ // unbound
2238
+ if (scope === 'returns') {
2239
+ absPath[1] = '$ReturnType';
2240
+ rootPrefix = absPath.slice(3, absPath.length - 1);
2241
+ }
2242
+ let eltPath = absPath.slice(1).join('/');
2243
+ // bound action
2244
+ if (scope === 'actions' && def[scope][carrier.$path[3]]) {
2245
+ def = def[scope][carrier.$path[3]];
2246
+ scope = carrier.$path[4];
2247
+ if (scope === 'params')
2248
+ rootPrefix = absPath.slice(2, absPath.length - 1);
2249
+ if (scope === 'returns') {
2250
+ absPath[2] = '$ReturnType';
2251
+ rootPrefix = absPath.slice(3, absPath.length - 1);
2252
+ eltPath = absPath.slice(2).join('/');
2253
+ }
2254
+ }
2255
+ const proxyDict = `$${scope}AnnoProxies`;
2256
+
2227
2257
  xprANames.forEach((xprAName) => {
2228
2258
  isSubTreeSpan = true;
2229
2259
  transformExpression(carrier, xprAName, subTreeSpan);
@@ -2231,18 +2261,14 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2231
2261
  transformExpression(carrier, xprAName, relativize);
2232
2262
  }
2233
2263
  else {
2234
- const scope = carrier.$path[2];
2235
2264
  absolutize.scope = scope;
2236
2265
  transformExpression(carrier, xprAName, absolutize);
2237
- const def = csn.definitions[absPath[0]];
2238
- const eltPath = absPath.slice(1).join('/');
2239
- const proxyDict = scope === 'elements' ? '$eltAnnoProxies' : '$paramAnnoProxies';
2240
2266
 
2241
2267
  if (!def[proxyDict])
2242
2268
  setProp(def, proxyDict, Object.create(null));
2243
- let eltProxy = def[proxyDict][eltPath];
2244
- if (!eltProxy) {
2245
- eltProxy = Object.create(null);
2269
+ let proxyCarrier = def[proxyDict][eltPath];
2270
+ if (!proxyCarrier) {
2271
+ proxyCarrier = Object.create(null);
2246
2272
  // these attributes are needed to test for
2247
2273
  // Property, Parameter, NavigationProperty, Collection
2248
2274
  // Applicability
@@ -2255,18 +2281,18 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2255
2281
  '@cds.api.ignore',
2256
2282
  '@odata.navigable' ].forEach((prop) => {
2257
2283
  if (carrier[prop] != null)
2258
- setProp(eltProxy, prop, carrier[prop]);
2284
+ setProp(proxyCarrier, prop, carrier[prop]);
2259
2285
  });
2260
2286
 
2261
2287
  Object.keys(carrier).filter(pn => pn[0] !== '@').forEach((pn) => {
2262
- eltProxy[pn] = carrier[pn];
2288
+ proxyCarrier[pn] = carrier[pn];
2263
2289
  });
2264
- def[proxyDict][eltPath] = eltProxy;
2290
+ def[proxyDict][eltPath] = proxyCarrier;
2265
2291
  }
2266
- eltProxy[xprAName] = carrier[xprAName];
2292
+ proxyCarrier[xprAName] = carrier[xprAName];
2267
2293
  carrier[xprAName] = null;
2268
2294
  nxprANames.filter(an => an.startsWith(`${xprAName}.`)).forEach((nxprAName) => {
2269
- eltProxy[nxprAName] = carrier[nxprAName];
2295
+ proxyCarrier[nxprAName] = carrier[nxprAName];
2270
2296
  carrier[nxprAName] = null;
2271
2297
  });
2272
2298
  }
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { setProp, isBetaEnabled } = require('../base/model');
3
+ const { setProp } = require('../base/model');
4
4
  const {
5
- isBuiltinType, isEdmPropertyRendered, applyTransformations,
5
+ isEdmPropertyRendered, applyTransformations,
6
6
  } = require('../model/csnUtils');
7
+ const { isBuiltinType } = require('../base/builtins');
7
8
  const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
8
9
  const { CompilerAssertion } = require('../base/error');
9
10
  const { cloneAnnotationValue } = require('../model/cloneCsn');
@@ -31,20 +32,6 @@ function validateOptions( _options ) {
31
32
  options.isV4 = () => v4;
32
33
 
33
34
  options.pathDelimiter = options.isStructFormat ? '/' : '_';
34
-
35
- if (isBetaEnabled(options, 'v5preview')) {
36
- if (!options.severities)
37
- options.severities = {};
38
- options.severities['odata-spec-violation-array'] ??= 'Error';
39
- options.severities['odata-spec-violation-assoc'] ??= 'Error';
40
- options.severities['odata-spec-violation-namespace'] ??= 'Error';
41
- options.severities['odata-spec-violation-param'] ??= 'Error';
42
- options.severities['odata-spec-violation-returns'] ??= 'Error';
43
- options.severities['odata-spec-violation-type-unknown'] ??= 'Error';
44
- options.severities['odata-spec-violation-no-key'] ??= 'Error';
45
- options.severities['odata-spec-violation-key-type'] ??= 'Error';
46
- options.severities['odata-spec-violation-property-name'] ??= 'Error';
47
- }
48
35
  return options;
49
36
  }
50
37
  return _options;
@@ -3030,6 +3030,15 @@
3030
3030
  "ValueListProperty": "Edm.String"
3031
3031
  }
3032
3032
  },
3033
+ "Common.ValueListParameterConstants": {
3034
+ "$kind": "ComplexType",
3035
+ "BaseType": "Common.ValueListParameter",
3036
+ "Properties": {
3037
+ "Constants": "Collection(Edm.PrimitiveType)",
3038
+ "ValueListProperty": "Edm.String"
3039
+ },
3040
+ "$experimental": true
3041
+ },
3033
3042
  "Common.ValueListParameterInOut": {
3034
3043
  "$kind": "ComplexType",
3035
3044
  "BaseType": "Common.ValueListParameter",