@sap/cds-compiler 4.8.0 → 4.9.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 (92) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +14 -1
  6. package/doc/CHANGELOG_BETA.md +19 -0
  7. package/lib/api/main.js +59 -24
  8. package/lib/api/options.js +12 -1
  9. package/lib/api/validate.js +1 -5
  10. package/lib/base/builtins.js +27 -0
  11. package/lib/base/message-registry.js +32 -19
  12. package/lib/base/messages.js +50 -19
  13. package/lib/base/model.js +4 -5
  14. package/lib/checks/actionsFunctions.js +2 -2
  15. package/lib/checks/annotationsOData.js +3 -0
  16. package/lib/checks/defaultValues.js +5 -2
  17. package/lib/checks/queryNoDbArtifacts.js +3 -2
  18. package/lib/checks/validator.js +2 -34
  19. package/lib/compiler/assert-consistency.js +8 -2
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +34 -22
  22. package/lib/compiler/extend.js +33 -10
  23. package/lib/compiler/index.js +0 -1
  24. package/lib/compiler/lsp-api.js +5 -0
  25. package/lib/compiler/propagator.js +21 -18
  26. package/lib/compiler/resolve.js +44 -28
  27. package/lib/compiler/shared.js +60 -20
  28. package/lib/compiler/tweak-assocs.js +13 -88
  29. package/lib/compiler/xpr-rewrite.js +689 -0
  30. package/lib/edm/annotations/genericTranslation.js +80 -60
  31. package/lib/edm/edm.js +4 -4
  32. package/lib/edm/edmInboundChecks.js +33 -0
  33. package/lib/edm/edmPreprocessor.js +9 -6
  34. package/lib/gen/Dictionary.json +129 -14
  35. package/lib/gen/language.checksum +1 -1
  36. package/lib/gen/language.interp +1 -1
  37. package/lib/gen/languageParser.js +1523 -1518
  38. package/lib/json/from-csn.js +13 -4
  39. package/lib/json/to-csn.js +10 -11
  40. package/lib/language/genericAntlrParser.js +14 -6
  41. package/lib/main.d.ts +67 -14
  42. package/lib/main.js +1 -0
  43. package/lib/model/cloneCsn.js +6 -3
  44. package/lib/model/csnRefs.js +12 -7
  45. package/lib/model/csnUtils.js +13 -7
  46. package/lib/model/enrichCsn.js +3 -1
  47. package/lib/model/revealInternalProperties.js +2 -1
  48. package/lib/model/sortViews.js +14 -6
  49. package/lib/modelCompare/compare.js +33 -34
  50. package/lib/optionProcessor.js +27 -2
  51. package/lib/render/DuplicateChecker.js +6 -6
  52. package/lib/render/manageConstraints.js +1 -0
  53. package/lib/render/toCdl.js +3 -1
  54. package/lib/transform/db/applyTransformations.js +33 -0
  55. package/lib/transform/db/constraints.js +1 -1
  56. package/lib/transform/db/expansion.js +8 -3
  57. package/lib/transform/db/groupByOrderBy.js +2 -2
  58. package/lib/transform/db/temporal.js +6 -3
  59. package/lib/transform/db/transformExists.js +2 -2
  60. package/lib/transform/effective/annotations.js +194 -0
  61. package/lib/transform/effective/main.js +6 -8
  62. package/lib/transform/effective/misc.js +31 -10
  63. package/lib/transform/forOdata.js +23 -7
  64. package/lib/transform/forRelationalDB.js +1 -1
  65. package/lib/transform/localized.js +7 -6
  66. package/lib/transform/odata/flattening.js +189 -106
  67. package/lib/transform/odata/toFinalBaseType.js +1 -1
  68. package/lib/transform/odata/typesExposure.js +15 -12
  69. package/lib/transform/parseExpr.js +4 -4
  70. package/lib/transform/transformUtils.js +40 -37
  71. package/lib/transform/translateAssocsToJoins.js +47 -47
  72. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  73. package/package.json +1 -1
  74. package/share/messages/anno-missing-rewrite.md +45 -0
  75. package/share/messages/message-explanations.json +1 -0
  76. package/bin/.eslintrc.json +0 -17
  77. package/lib/api/.eslintrc.json +0 -37
  78. package/lib/checks/.eslintrc.json +0 -31
  79. package/lib/compiler/.eslintrc.json +0 -8
  80. package/lib/edm/.eslintrc.json +0 -46
  81. package/lib/inspect/.eslintrc.json +0 -4
  82. package/lib/json/.eslintrc.json +0 -4
  83. package/lib/language/.eslintrc.json +0 -4
  84. package/lib/model/.eslintrc.json +0 -13
  85. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  86. package/lib/render/.eslintrc.json +0 -22
  87. package/lib/transform/.eslintrc.json +0 -13
  88. package/lib/transform/db/.eslintrc.json +0 -41
  89. package/lib/transform/draft/.eslintrc.json +0 -4
  90. package/lib/transform/effective/.eslintrc.json +0 -4
  91. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  92. package/lib/utils/.eslintrc.json +0 -7
@@ -5,7 +5,7 @@ const { forEachDefinition,
5
5
  transformExpression, findAnnotationExpression } = require('../../model/csnUtils');
6
6
  const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
7
7
  const transformUtils = require('../transformUtils');
8
- const { setProp, isBetaEnabled } = require('../../base/model');
8
+ const { setProp } = require('../../base/model');
9
9
  const { applyTransformationsOnDictionary,
10
10
  applyTransformationsOnNonDictionary } = require('../db/applyTransformations.js');
11
11
  const { linkForeignKeyAnnotationExtensionsToAssociation,
@@ -14,30 +14,25 @@ const { cloneCsnNonDict } = require('../../model/cloneCsn');
14
14
  const { forEach } = require('../../utils/objectUtils');
15
15
 
16
16
  function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options) {
17
-
18
- const isExprAnno = (obj, pn) => {
19
- return isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
20
- }
21
-
22
17
  forEachDefinition(csn, (def, defName) => {
23
- if(def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
18
+ if (def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
24
19
  ['elements', 'params'].forEach(dictName => {
25
20
  const dict = def[dictName];
26
- if(dict) {
21
+ if (dict) {
27
22
  const csnPath = ['definitions', defName, dictName];
28
23
  const orderedElementList = [];
29
24
 
30
25
  forEach(dict, (childName, child) => {
31
26
  const location = [ ...csnPath, childName ];
32
- let rootPrefix = [ defName ];
27
+ const rootPrefix = [ defName ];
33
28
  let resolvedElt = child;
34
29
  let typeIdx = 0;
35
- if(child.type && !child.elements) {
30
+ if (child.type && !child.elements) {
36
31
  resolvedElt = csnUtils.getFinalTypeInfo(child.type);
37
- if(resolvedElt.elements)
32
+ if (resolvedElt.elements)
38
33
  typeIdx = rootPrefix.length + 1;
39
34
  }
40
- if(resolvedElt.elements) {
35
+ if (resolvedElt.elements) {
41
36
  const flattenedSubTree = recurseIntoElement(dictName, child, resolvedElt,
42
37
  !!child.notNull, location, [ ...rootPrefix, childName ], typeIdx);
43
38
 
@@ -57,10 +52,10 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
57
52
  });
58
53
 
59
54
  const flatDict = orderedElementList.reduce((elements, [ flatEltName, flatElt ]) => {
60
- if(flatElt.items) {
55
+ if (flatElt.items) {
61
56
  // rewrite annotation paths inside items.elements
62
57
  forEachMemberRecursively(flatElt.items, (elt, _eltName, _prop, path) => {
63
- const exprAnnos = Object.keys(elt).filter(pn => isExprAnno(elt, pn));
58
+ const exprAnnos = Object.keys(elt).filter(pn => findAnnotationExpression(elt, pn));
64
59
  flattenAndPrefixExprPaths(elt, exprAnnos, elt.$path, path, 0, true);
65
60
  }, [ flatEltName ], true, { pathWithoutProp: true } );
66
61
  }
@@ -71,18 +66,74 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
71
66
  setProp(def, `$flat${dictName}`, flatDict);
72
67
  }
73
68
  });
69
+ // entity annotations
74
70
  const flatAnnos = Object.create(null);
75
- const annoNames = copyAnnotations(def, flatAnnos).filter(an => isExprAnno(def, an));
71
+ const annoNames = copyAnnotations(def, flatAnnos).filter(an => findAnnotationExpression(def, an));
76
72
  flattenAndPrefixExprPaths(flatAnnos, annoNames, [ 'definitions', defName ], [ defName ], 0);
77
73
  setProp(def, '$flatAnnotations', flatAnnos);
74
+ // explicit binding parameter of bound action
75
+ if (def.actions) {
76
+ const special$self = !csn?.definitions?.$self && '$self';
77
+ Object.entries(def.actions).forEach(([actionName, action]) => {
78
+ if (action.params) {
79
+ const params = Object.entries(action.params);
80
+ const firstParam = params[0][1];
81
+ const type = firstParam?.items?.type || firstParam?.type;
82
+ if (type === special$self) {
83
+
84
+ const bindingParamName = params[0][0];
85
+ const markBindingParam = {
86
+ ref: (parent, prop, xpr) => {
87
+ if ((xpr[0].id || xpr[0]) === bindingParamName)
88
+ setProp(parent, '$bparam', true)
89
+ },
90
+ };
91
+ const refCheck = {
92
+ ref: (elemref, prop, xpr, path) => {
93
+ const { art, scope } = inspectRef(path);
94
+ if (scope !== '$magic' && art) {
95
+ const ft = csnUtils.getFinalTypeInfo(art.type);
96
+ if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
97
+ error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
98
+ }
99
+ }
100
+ }
101
+ };
102
+
103
+ const flatAnnos = Object.create(null);
104
+ const annoNames = copyAnnotations(action, flatAnnos).filter(an => findAnnotationExpression(action, an));
105
+ annoNames.forEach((an) => {
106
+ refCheck.anno = an;
107
+ transformExpression(flatAnnos, an,
108
+ [ markBindingParam, refCheck, refFlattener ],
109
+ [ 'definitions', defName, 'actions', actionName ]);
110
+ adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
111
+ adaptRefs.length = 0;
112
+ });
113
+ setProp(action, '$flatAnnotations', flatAnnos);
114
+
115
+ forEachMemberRecursively(action, (member, memberName, prop, path, _parent) => {
116
+ const exprAnnos = Object.keys(member).filter(pn => findAnnotationExpression(member, pn));
117
+ exprAnnos.forEach((pn) => {
118
+ refCheck.anno = pn;
119
+ transformExpression(member, pn, [ markBindingParam, refCheck, refFlattener ], path);
120
+ adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
121
+ adaptRefs.length = 0;
122
+ });
123
+ },
124
+ [ 'definitions', defName, 'actions', actionName ]);
125
+ }
126
+ }
127
+ });
128
+ }
78
129
  }
79
130
  });
80
131
 
81
132
  function recurseIntoElement(scope, elt, resolvedElt, rootPathIsNotNull, location, rootPrefix = [], typeIdx = 0) {
82
133
  const eltName = rootPrefix[rootPrefix.length-1];
83
- if(!resolvedElt.elements) {
134
+ if (!resolvedElt.elements) {
84
135
  const flatElt = cloneElt(scope, elt, location, rootPrefix, typeIdx);
85
- if(rootPathIsNotNull)
136
+ if (rootPathIsNotNull)
86
137
  flatElt.notNull = true;
87
138
  else
88
139
  delete flatElt.notNull;
@@ -92,9 +143,9 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
92
143
  let flattenedSubTree = [];
93
144
  forEach(resolvedElt.elements, (childName, child) => {
94
145
  resolvedElt = child;
95
- if(child.type && !child.elements) {
146
+ if (child.type && !child.elements) {
96
147
  resolvedElt = csnUtils.getFinalTypeInfo(child.type);
97
- if(resolvedElt.elements)
148
+ if (resolvedElt.elements)
98
149
  typeIdx = rootPrefix.length + 1;
99
150
  }
100
151
  flattenedSubTree = flattenedSubTree.concat(recurseIntoElement(scope, child, resolvedElt,
@@ -107,7 +158,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
107
158
  return flattenedSubTree.map(([flatEltName, flatElt]) => {
108
159
  return [ `${eltName}_${flatEltName}`, flatElt ];
109
160
  }).filter(([name, flatElt]) =>{
110
- if(duplicateDict[name]) {
161
+ if (duplicateDict[name]) {
111
162
  error('name-duplicate-element', location,
112
163
  { '#': 'flatten-element-gen', name });
113
164
  return false;
@@ -143,7 +194,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
143
194
  };
144
195
  // TODO: copy only those expression annotations that have no path into unreachable subtree, starting
145
196
  // of typePathRoot (which could be some completely different path)
146
- const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => isExprAnno(flatElt, pn));
197
+ const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => findAnnotationExpression(flatElt, pn));
147
198
  flattenAndPrefixExprPaths(flatElt, exprAnnoNames, elt.$path, rootPrefix, typeIdx);
148
199
  // Copy selected type properties
149
200
  ['key', 'virtual', 'masked', 'viaAll', 'localized'].forEach(p => {
@@ -165,9 +216,9 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
165
216
  retypeCloneWithFinalBaseType(flatElt, location);
166
217
 
167
218
  const [ nonAnnoProps, exprAnnoProps ] = Object.keys(flatElt).reduce((acc, pn) => {
168
- if(pn[0] !== '@' && pn !== 'value')
219
+ if (pn[0] !== '@' && pn !== 'value')
169
220
  acc[0].push(pn);
170
- if(isExprAnno(flatElt, pn) || pn === 'value')
221
+ if (findAnnotationExpression(flatElt, pn) || pn === 'value')
171
222
  acc[1].push(pn);
172
223
  return acc;
173
224
  }, [[],[]]);
@@ -181,18 +232,18 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
181
232
  return flatElt;
182
233
 
183
234
  function retypeCloneWithFinalBaseType(elt, location) {
184
- if(elt.type &&
235
+ if (elt.type &&
185
236
  !isBuiltinType(elt.type) &&
186
237
  !isODataV4BuiltinFromService(elt.type, location)
187
238
  && !isItemsType(elt.type)) {
188
- let resolvedType = csnUtils.getFinalTypeInfo(elt);
239
+ const resolvedType = csnUtils.getFinalTypeInfo(elt);
189
240
 
190
241
  delete resolvedType.kind;
191
- if(resolvedType.items)
242
+ if (resolvedType.items)
192
243
  delete resolvedType.type;
193
244
 
194
- if(elt.items) {
195
- if(resolvedType.items) {
245
+ if (elt.items) {
246
+ if (resolvedType.items) {
196
247
  elt.items = resolvedType;
197
248
  delete elt.items.type;
198
249
  }
@@ -200,7 +251,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
200
251
  elt.items.type = resolvedType;
201
252
  }
202
253
  else {
203
- if(resolvedType.items) {
254
+ if (resolvedType.items) {
204
255
  elt.items = resolvedType.items;
205
256
  delete elt.type;
206
257
  }
@@ -210,7 +261,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
210
261
  }
211
262
 
212
263
  function isItemsType(typeName) {
213
- let typeDef = csn.definitions[typeName];
264
+ const typeDef = csn.definitions[typeName];
214
265
  return !!typeDef?.items;
215
266
  }
216
267
 
@@ -220,7 +271,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
220
271
 
221
272
  const typeServiceName = csnUtils.getServiceName(typeName);
222
273
  let finalBaseType = csnUtils.getFinalTypeInfo(typeName).type;
223
- if(!isBuiltinType(finalBaseType)) {
274
+ if (!isBuiltinType(finalBaseType)) {
224
275
  const typeDef = csn.definitions[finalBaseType];
225
276
  finalBaseType = typeDef?.items?.type || typeDef?.type;
226
277
  }
@@ -241,39 +292,52 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
241
292
  function flattenAndPrefixExprPaths(carrier, propNames, csnPath, rootPrefix, typeIdx, refParentIsItems = false) {
242
293
  const refCheck = {
243
294
  ref: (elemref, prop, xpr, path) => {
244
- const { art } = (elemref._art ? { art: elemref._art } : inspectRef(path));
245
- const ft = csnUtils.getFinalTypeInfo(art?.type);
246
- if(!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
247
- error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, name: refCheck.eltLocationStr, '#': 'flatten_builtin' });
295
+ const { art, scope } = inspectRef(path);
296
+ if (scope !== '$magic' && art) {
297
+ const ft = csnUtils.getFinalTypeInfo(art.type);
298
+ if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
299
+ error('odata-anno-xpr-ref', path,
300
+ {
301
+ anno: refCheck.anno,
302
+ elemref,
303
+ name: refCheck.eltLocationStr,
304
+ '#': 'flatten_builtin'
305
+ });
306
+ }
248
307
  }
249
308
  }
250
309
  }
251
310
 
252
311
  refCheck.eltLocationStr = (function() {
253
312
  const [head, ...tail ] = rootPrefix;
254
- return `${head}:${tail.join('.')}`;
313
+ if(tail.length)
314
+ return `${head}:${tail.join('.')}`;
315
+ else
316
+ return `${head}`;
255
317
  })();
256
318
 
257
319
  const absolutifier = {
258
320
  ref : (parent, prop, xpr) => {
259
321
  const head = xpr[0].id || xpr[0];
260
- if(typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
322
+ if (typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
261
323
  const [xprHead, ...xprTail] = xpr.slice(1, xpr.length);
262
- if(xprHead.id) {
263
- xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
264
- parent[prop] = [ xprHead, ...xprTail ];
324
+ if(xprHead) {
325
+ if (xprHead.id) {
326
+ xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
327
+ parent[prop] = [ xprHead, ...xprTail ];
328
+ }
329
+ else
330
+ parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
331
+ if (carrier.$scope === 'params')
332
+ parent.param = true;
333
+ else
334
+ parent[prop].unshift('$self');
265
335
  }
266
- else
267
- parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
268
- if(carrier.$scope === 'params')
269
- parent.param = true;
270
- else
271
- parent[prop].unshift('$self');
272
336
  }
273
- else if(rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
337
+ else if (rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
274
338
  const [xprHead, ...xprTail] = xpr;
275
- if(!refParentIsItems) {
276
- if(xprHead.id) {
339
+ if (!refParentIsItems) {
340
+ if (xprHead.id) {
277
341
  xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
278
342
  parent[prop] = [ xprHead, ...xprTail ];
279
343
  }
@@ -282,7 +346,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
282
346
  }
283
347
  else
284
348
  parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
285
- if(carrier.$scope === 'params')
349
+ if (carrier.$scope === 'params')
286
350
  parent.param = true;
287
351
  else
288
352
  parent[prop].unshift('$self');
@@ -320,57 +384,66 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
320
384
  }
321
385
  }
322
386
 
323
- function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType, csnUtils, error, options, iterateOptions = {} ) {
387
+ function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
388
+ csnUtils, error, options, iterateOptions = {} ) {
324
389
 
325
390
  // All anno path flattening is already done, don't do it on locations where we don't want it!
326
391
  const typeNames = [];
327
392
  forEachDefinition(csn, (def, defName) => {
328
- if(def.kind === 'entity') {
393
+ if (def.kind === 'entity') {
329
394
  ['query', 'projection'].forEach(dictName => {
330
- applyTransformationsOnNonDictionary(csn.definitions[defName], dictName, refFlattener, iterateOptions, [ 'definitions', defName ]);
395
+ applyTransformationsOnNonDictionary(csn.definitions[defName],
396
+ dictName, refFlattener, iterateOptions,
397
+ [ 'definitions', defName ]);
331
398
  })
332
- if(csn.definitions[defName].actions)
333
- applyTransformationsOnDictionary(csn.definitions[defName].actions, refFlattener, iterateOptions, [ 'definitions', defName, 'actions' ]);
399
+ if (csn.definitions[defName].actions)
400
+ applyTransformationsOnDictionary(csn.definitions[defName].actions,
401
+ refFlattener, iterateOptions,
402
+ [ 'definitions', defName, 'actions' ]);
334
403
  }
335
404
 
336
- if(['type'].includes(def.kind)) {
405
+ if (['type'].includes(def.kind)) {
337
406
  typeNames.push(defName);
338
- applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, iterateOptions, [ 'definitions' ]);
407
+ applyTransformationsOnNonDictionary(csn.definitions,
408
+ defName, refFlattener, iterateOptions, [ 'definitions' ]);
339
409
  }
340
410
  });
341
411
  adaptRefs.forEach(fn => fn());
342
412
  adaptRefs.length = 0;
343
- if(isBetaEnabled(options, 'odataPathsInAnnotationExpressions')) {
344
- const refCheck = {
345
- ref: (elemref, prop, xpr, path) => {
346
- const { links, art } = (elemref._links && elemref._art ? { links: elemref._links, art: elemref._art } : inspectRef(path) );
347
-
348
- let i = links.length-2;
349
- const getProp = (propName) =>
350
- (links[i].art?.[propName] ||
351
- effectiveType(links[i].art)[propName]);
352
413
 
353
- const ft = csnUtils.getFinalTypeInfo(art?.type);
354
- let target = undefined;
355
- for(; i >= 0 && !getProp('items') && !target; i--) {
356
- target = getProp('target');
357
- }
358
- if(target && csn.definitions[target].$flatelements && !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
359
- error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
360
- }
414
+ const refCheck = {
415
+ ref: (elemref, prop, xpr, path) => {
416
+ const { links, art } = (elemref._links && elemref._art
417
+ ? { links: elemref._links, art: elemref._art }
418
+ : inspectRef(path) );
419
+
420
+ let i = links.length-2;
421
+ const getProp = (propName) =>
422
+ (links[i].art?.[propName] ||
423
+ effectiveType(links[i].art)[propName]);
424
+
425
+ const ft = csnUtils.getFinalTypeInfo(art?.type);
426
+ let target = undefined;
427
+ for(; i >= 0 && !getProp('items') && !target; i--) {
428
+ target = getProp('target');
429
+ }
430
+ if (target && csn.definitions[target].$flatelements
431
+ && !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
432
+ error('odata-anno-xpr-ref', path,
433
+ { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
361
434
  }
362
435
  }
363
- typeNames.forEach(tn => {
364
- forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
365
- Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
366
- refCheck.anno = pn;
367
- transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
368
- });
369
- }, [ 'definitions', tn ]);
370
- })
371
- adaptRefs.forEach(fn => fn(true, 1));
372
- adaptRefs.length = 0;
373
436
  }
437
+ typeNames.forEach(tn => {
438
+ forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
439
+ Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
440
+ refCheck.anno = pn;
441
+ transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
442
+ });
443
+ }, [ 'definitions', tn ]);
444
+ })
445
+ adaptRefs.forEach(fn => fn(true, 1));
446
+ adaptRefs.length = 0;
374
447
  }
375
448
 
376
449
  function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, pathDelimiter) {
@@ -399,26 +472,31 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
399
472
  const resolvedLinkTypes = resolveLinkTypes(links);
400
473
  setProp(parent, '$path', [ ...path ]);
401
474
  const lastRef = ref[ref.length - 1];
402
- const fn = (suspend=false, suspendPos=0) => {
403
- const scopedPath = [ ...parent.$path ];
404
- // TODO: If foreign key annotations should be assigned via
405
- // full path into target, uncomment this line and
406
- // comment/remove setProp in expansion.js
407
- // setProp(parent, '$structRef', parent.ref);
408
- parent.ref = flattenStructStepsInRef(ref, scopedPath, links, scope, resolvedLinkTypes, suspend, suspendPos);
409
- resolved.set(parent, { links, art, scope });
410
- // Explicitly set implicit alias for things that are now flattened - but only in columns
411
- // TODO: Can this be done elegantly during expand phase already?
412
- if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
413
- if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
414
- delete parent.as;
415
- delete parent.$implicitAlias;
416
- }
417
- // To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
418
- else if (parent.ref[parent.ref.length - 1] !== lastRef &&
419
- (insideColumns(scopedPath) || insideKeys(scopedPath)) &&
420
- !parent.as) {
421
- parent.as = lastRef;
475
+ const fn = (suspend = false, suspendPos = 0,
476
+ refFilter = (_parent) => true) => {
477
+ if (refFilter(parent)) {
478
+ const scopedPath = [ ...parent.$path ];
479
+ // TODO: If foreign key annotations should be assigned via
480
+ // full path into target, uncomment this line and
481
+ // comment/remove setProp in expansion.js
482
+ // setProp(parent, '$structRef', parent.ref);
483
+ parent.ref = flattenStructStepsInRef(ref,
484
+ scopedPath, links, scope, resolvedLinkTypes,
485
+ suspend, suspendPos, parent.$bparam);
486
+ resolved.set(parent, { links, art, scope });
487
+ // Explicitly set implicit alias for things that are now flattened - but only in columns
488
+ // TODO: Can this be done elegantly during expand phase already?
489
+ if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
490
+ if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
491
+ delete parent.as;
492
+ delete parent.$implicitAlias;
493
+ }
494
+ // To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
495
+ else if (parent.ref[parent.ref.length - 1] !== lastRef &&
496
+ (insideColumns(scopedPath) || insideKeys(scopedPath)) &&
497
+ !parent.as) {
498
+ parent.as = lastRef;
499
+ }
422
500
  }
423
501
 
424
502
  /**
@@ -428,7 +506,10 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
428
506
  * @returns {boolean}
429
507
  */
430
508
  function insideColumns( path ) {
431
- return path.length >= 3 && (path[path.length - 3] === 'SELECT' || path[path.length - 3] === 'projection') && path[path.length - 2] === 'columns';
509
+ return path.length >= 3
510
+ && (path[path.length - 3] === 'SELECT'
511
+ || path[path.length - 3] === 'projection')
512
+ && path[path.length - 2] === 'columns';
432
513
  }
433
514
  /**
434
515
  * Return true if the path points inside keys
@@ -437,7 +518,9 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
437
518
  * @returns {boolean}
438
519
  */
439
520
  function insideKeys( path ) {
440
- return path.length >= 3 && path[path.length - 2] === 'keys' && typeof path[path.length - 1] === 'number';
521
+ return path.length >= 3
522
+ && path[path.length - 2] === 'keys'
523
+ && typeof path[path.length - 1] === 'number';
441
524
  }
442
525
  };
443
526
  // adapt queries later
@@ -88,7 +88,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
88
88
  // type Foo: array of { qux: Integer };
89
89
  function expandFirstLevelOfArrayed(def) {
90
90
  if (def.items.type && !isBuiltinType(def.items.type)) {
91
- let finalBaseType = csnUtils.getFinalTypeInfo(def.items.type);
91
+ const finalBaseType = csnUtils.getFinalTypeInfo(def.items.type);
92
92
  if (finalBaseType?.elements) {
93
93
  def.items.elements = cloneCsnDict(finalBaseType.elements, options);
94
94
  delete def.items.type;
@@ -8,7 +8,7 @@
8
8
 
9
9
  const { setProp, isBetaEnabled } = require('../../base/model');
10
10
  const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
11
- const { getNamespace, copyAnnotations, findAnnotationExpression,
11
+ const { getNamespace, copyAnnotations, findAnnotationExpression,
12
12
  forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');
13
13
  const { isBuiltinType } = require('../../base/builtins');
14
14
  const { CompilerAssertion } = require('../../base/error');
@@ -77,18 +77,21 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
77
77
  if (serviceName && requestedServiceNames.includes(serviceName)) {
78
78
  if (def.kind === 'type' || def.kind === 'entity') {
79
79
  forEachMember(def, (element, elementName, propertyName, path) => {
80
- if (propertyName === 'elements' || propertyName === 'params') {
80
+ if (propertyName === 'elements') {
81
81
  const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
82
- exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, newTypeName, defName, path);
82
+ exposeTypeOf(element, element.key, elementName, defName, serviceName, newTypeName, defName, path);
83
+ } else if (propertyName === 'params') {
84
+ const newTypeName = getNewTypeName(element, elementName, `ep_${defName.replace(/\./g, '_')}`, serviceName);
85
+ exposeTypeOf(element, true, elementName, defName, serviceName, newTypeName, defName, path);
83
86
  }
84
87
  }, path);
85
88
  }
86
89
 
87
90
  if (def.kind === 'action' || def.kind === 'function') {
88
- exposeTypesOfAction(def, defName, defName, serviceName, path);
91
+ exposeTypesOfAction(def, defName, defName, serviceName, path, false);
89
92
  }
90
93
  def.actions && Object.entries(def.actions).forEach(([actionName, action]) => {
91
- exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]));
94
+ exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]), true);
92
95
  });
93
96
  }
94
97
  });
@@ -122,7 +125,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
122
125
  * @param {String} actionName
123
126
  * @param {String} serviceName
124
127
  */
125
- function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
128
+ function exposeTypesOfAction(action, actionName, defName, serviceName, path, isBound) {
126
129
  if (action.returns) {
127
130
  const artificialName = `return_${actionName.replace(/\./g, '_')}`;
128
131
  const newTypeName = getNewTypeName(action.returns, undefined, artificialName, serviceName);
@@ -130,7 +133,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
130
133
  }
131
134
 
132
135
  action.params && Object.entries(action.params).forEach(([paramName, param]) => {
133
- const artificialName = `param_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
136
+ const artificialName = `${isBound ? 'bap' : 'ap'}_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
134
137
  const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);
135
138
  exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['params', paramName]));
136
139
  });
@@ -254,7 +257,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
254
257
  }
255
258
  else if(isTermDef) {
256
259
  newType = Object.create(null);
257
- for(let n in typeDef) {
260
+ for(const n in typeDef) {
258
261
  newType[n] = typeDef[n];
259
262
  }
260
263
  newType.kind = 'type';
@@ -293,7 +296,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
293
296
  function isTypeExposable() {
294
297
  let typeName = undefined;
295
298
  let typeDef = node;
296
- let elements = (node.items?.elements || node.elements)
299
+ const elements = (node.items?.elements || node.elements)
297
300
  // anonymous structured type
298
301
  if(elements)
299
302
  return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
@@ -315,7 +318,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
315
318
  typeName = (type.ref && csnUtils.artifactRef(type)) || type;
316
319
  }
317
320
  else {
318
- throw new CompilerAssertion(`Please debug me: ${typeName} not found`);
321
+ throw new CompilerAssertion(`Debug me: ${typeName} not found`);
319
322
  }
320
323
  }
321
324
  }
@@ -372,7 +375,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
372
375
  * @param {string} parentName name of the parent def holding the element
373
376
  */
374
377
  function getAnonymousTypeNameInMultiSchema(typeName, parentName) {
375
- let currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
378
+ const currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
376
379
  const newSchemaName = currPrefix || fallBackSchemaName;
377
380
  // new type name without any prefixes
378
381
  const typePlainName = defNameWithoutServiceOrContextName(typeName, newSchemaName);
@@ -403,7 +406,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
403
406
 
404
407
  // Duplicate elements
405
408
  Object.entries(elements).forEach(([elemName, element]) => {
406
- let cloned = cloneCsnNonDict(element, options);
409
+ const cloned = cloneCsnNonDict(element, options);
407
410
  // if this was an anonymous sub element of a key, mark it as not nullable
408
411
  if(isAnonymous && isKey && !cloned.key) {
409
412
  if(cloned.target) {
@@ -58,7 +58,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
58
58
  return { 'cast': [ xpr.cast, { [castKeys[0]]: xpr[castKeys[0]] } ] };
59
59
  }
60
60
  else {
61
- for(let n in xpr) {
61
+ for(const n in xpr) {
62
62
  // xpr could be an array with polluted prototype
63
63
  if (Object.hasOwnProperty.call(xpr, n))
64
64
  xpr[n] = Cast(xpr[n], state)
@@ -214,7 +214,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
214
214
  while(i < e && xpr[i] !== 'and') i++;
215
215
  const a = i < e ? i : -1;
216
216
  if(b >= 0) {
217
- let token = [ 'between' ];
217
+ const token = [ 'between' ];
218
218
  not = (xpr[b-1] === 'not');
219
219
  if(not)
220
220
  token.splice(0,0, 'not');
@@ -312,7 +312,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
312
312
  if (typeof xpr === 'object') {
313
313
  // if(xpr?.func && funkyfuncs.includes(xpr?.func))
314
314
  // return xpr;
315
- for(let n in xpr) {
315
+ for(const n in xpr) {
316
316
  // xpr could be an array with polluted prototype
317
317
  if (!Object.hasOwnProperty.call(xpr, n))
318
318
  continue;
@@ -349,7 +349,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
349
349
  s = p+tl;
350
350
  [tl, p] = findToken(s, e);
351
351
  while(p >= 0) {
352
- let rhs = next(xpr, s, p, state);
352
+ const rhs = next(xpr, s, p, state);
353
353
  naryExpr.push(...op, rhs);
354
354
  if(state.array)
355
355
  lhs = [ lhs, ...op, rhs ];