@sap/cds-compiler 4.8.0 → 4.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +38 -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 +30 -17
  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 +38 -21
  12. package/lib/base/messages.js +51 -20
  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 +10 -3
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +38 -30
  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/populate.js +0 -2
  26. package/lib/compiler/propagator.js +23 -19
  27. package/lib/compiler/resolve.js +48 -29
  28. package/lib/compiler/shared.js +60 -20
  29. package/lib/compiler/tweak-assocs.js +72 -116
  30. package/lib/compiler/xpr-rewrite.js +762 -0
  31. package/lib/edm/annotations/edmJson.js +24 -7
  32. package/lib/edm/annotations/genericTranslation.js +81 -61
  33. package/lib/edm/edm.js +4 -4
  34. package/lib/edm/edmInboundChecks.js +33 -0
  35. package/lib/edm/edmPreprocessor.js +9 -6
  36. package/lib/gen/Dictionary.json +129 -14
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +1 -1
  39. package/lib/gen/languageParser.js +1523 -1518
  40. package/lib/json/from-csn.js +13 -4
  41. package/lib/json/to-csn.js +12 -12
  42. package/lib/language/genericAntlrParser.js +14 -6
  43. package/lib/main.d.ts +67 -14
  44. package/lib/main.js +1 -0
  45. package/lib/model/cloneCsn.js +6 -3
  46. package/lib/model/csnRefs.js +23 -11
  47. package/lib/model/csnUtils.js +13 -7
  48. package/lib/model/enrichCsn.js +3 -1
  49. package/lib/model/revealInternalProperties.js +2 -1
  50. package/lib/model/sortViews.js +14 -6
  51. package/lib/modelCompare/compare.js +33 -34
  52. package/lib/optionProcessor.js +27 -2
  53. package/lib/render/DuplicateChecker.js +6 -6
  54. package/lib/render/manageConstraints.js +1 -0
  55. package/lib/render/toCdl.js +3 -1
  56. package/lib/transform/db/applyTransformations.js +33 -0
  57. package/lib/transform/db/constraints.js +75 -28
  58. package/lib/transform/db/expansion.js +8 -3
  59. package/lib/transform/db/flattening.js +2 -2
  60. package/lib/transform/db/groupByOrderBy.js +2 -2
  61. package/lib/transform/db/temporal.js +6 -3
  62. package/lib/transform/db/transformExists.js +2 -2
  63. package/lib/transform/effective/annotations.js +194 -0
  64. package/lib/transform/effective/main.js +6 -8
  65. package/lib/transform/effective/misc.js +31 -10
  66. package/lib/transform/forOdata.js +23 -7
  67. package/lib/transform/forRelationalDB.js +3 -3
  68. package/lib/transform/localized.js +7 -6
  69. package/lib/transform/odata/flattening.js +221 -124
  70. package/lib/transform/odata/toFinalBaseType.js +1 -1
  71. package/lib/transform/odata/typesExposure.js +15 -12
  72. package/lib/transform/parseExpr.js +4 -4
  73. package/lib/transform/transformUtils.js +47 -42
  74. package/lib/transform/translateAssocsToJoins.js +47 -47
  75. package/lib/transform/universalCsn/universalCsnEnricher.js +16 -19
  76. package/package.json +1 -1
  77. package/share/messages/anno-missing-rewrite.md +45 -0
  78. package/share/messages/message-explanations.json +1 -0
  79. package/bin/.eslintrc.json +0 -17
  80. package/lib/api/.eslintrc.json +0 -37
  81. package/lib/checks/.eslintrc.json +0 -31
  82. package/lib/compiler/.eslintrc.json +0 -8
  83. package/lib/edm/.eslintrc.json +0 -46
  84. package/lib/inspect/.eslintrc.json +0 -4
  85. package/lib/json/.eslintrc.json +0 -4
  86. package/lib/language/.eslintrc.json +0 -4
  87. package/lib/model/.eslintrc.json +0 -13
  88. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  89. package/lib/render/.eslintrc.json +0 -22
  90. package/lib/transform/.eslintrc.json +0 -13
  91. package/lib/transform/db/.eslintrc.json +0 -41
  92. package/lib/transform/draft/.eslintrc.json +0 -4
  93. package/lib/transform/effective/.eslintrc.json +0 -4
  94. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  95. 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
  }
@@ -239,66 +290,87 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
239
290
  // Later, the query can/must be rewritten as long as a flat OData CSN is published
240
291
  // but this then operates on the entity/view which has all struct infos available
241
292
  function flattenAndPrefixExprPaths(carrier, propNames, csnPath, rootPrefix, typeIdx, refParentIsItems = false) {
293
+
242
294
  const refCheck = {
243
295
  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' });
296
+ const { art, scope } = inspectRef(path);
297
+ if (scope !== '$magic' && art) {
298
+ const ft = csnUtils.getFinalTypeInfo(art.type);
299
+ if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
300
+ error('odata-anno-xpr-ref', path,
301
+ {
302
+ anno: refCheck.anno,
303
+ elemref,
304
+ name: refCheck.eltLocationStr,
305
+ '#': 'flatten_builtin'
306
+ });
307
+ }
248
308
  }
249
309
  }
250
310
  }
251
311
 
252
312
  refCheck.eltLocationStr = (function() {
253
313
  const [head, ...tail ] = rootPrefix;
254
- return `${head}:${tail.join('.')}`;
314
+ if(tail.length)
315
+ return `${head}:${tail.join('.')}`;
316
+ else
317
+ return `${head}`;
255
318
  })();
256
319
 
320
+ let refChanged = false;
257
321
  const absolutifier = {
258
322
  ref : (parent, prop, xpr) => {
259
323
  const head = xpr[0].id || xpr[0];
260
- if(typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
261
- 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
+ let isPrefixed = false;
325
+ if(!isMagicVariable(head)) {
326
+ if (head === '$self' && typeIdx < rootPrefix.length) {
327
+ isPrefixed = true;
328
+ const [xprHead, ...xprTail] = xpr.slice(1, xpr.length);
329
+ if(xprHead) {
330
+ if (xprHead.id) {
331
+ xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
332
+ parent[prop] = [ xprHead, ...xprTail ];
333
+ }
334
+ else
335
+ parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
336
+ }
265
337
  }
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
- }
273
- else if(rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
274
- const [xprHead, ...xprTail] = xpr;
275
- if(!refParentIsItems) {
276
- if(xprHead.id) {
277
- xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
278
- parent[prop] = [ xprHead, ...xprTail ];
338
+ else if (head !== '$self' && !parent.param && rootPrefix.length > 2) {
339
+ isPrefixed = true;
340
+ const [xprHead, ...xprTail] = xpr;
341
+ if (!refParentIsItems) {
342
+ if (xprHead.id) {
343
+ xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
344
+ parent[prop] = [ xprHead, ...xprTail ];
345
+ }
346
+ else
347
+ parent[prop] = [ rootPrefix.slice(1, -1).concat(xprHead).join('_'), ...xprTail];
279
348
  }
280
349
  else
281
- parent[prop] = [ rootPrefix.slice(1, -1).concat(xprHead).join('_'), ...xprTail];
350
+ parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
351
+ }
352
+ if(isPrefixed) {
353
+ if (carrier.$scope === 'params')
354
+ parent.param = true;
355
+ else
356
+ parent[prop].unshift('$self');
282
357
  }
283
- else
284
- parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
285
- if(carrier.$scope === 'params')
286
- parent.param = true;
287
- else
288
- parent[prop].unshift('$self');
289
358
  }
359
+ if(isPrefixed)
360
+ refChanged = isPrefixed;
290
361
  }
291
362
  }
292
-
293
363
  propNames.forEach(pn => {
364
+ refChanged = false;
294
365
  refCheck.anno = pn;
295
366
  transformExpression(carrier, pn, [ refCheck, refFlattener ], csnPath);
296
- });
297
- adaptRefs.forEach(fn => fn(refParentIsItems));
298
- adaptRefs.length = 0;
299
- propNames.forEach(pn => {
367
+ adaptRefs.forEach(fn =>
368
+ { if( fn(refParentIsItems)) refChanged = true });
369
+ adaptRefs.length = 0;
300
370
  transformExpression(carrier, pn, absolutifier, csnPath)
301
- })
371
+ if(refChanged && carrier[pn]['='])
372
+ carrier[pn]['='] = true;
373
+ });
302
374
  }
303
375
 
304
376
  // TODO: This should be part of the generic path rewriting algorithm
@@ -320,57 +392,70 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
320
392
  }
321
393
  }
322
394
 
323
- function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType, csnUtils, error, options, iterateOptions = {} ) {
395
+ function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
396
+ csnUtils, error, options, iterateOptions = {} ) {
324
397
 
325
398
  // All anno path flattening is already done, don't do it on locations where we don't want it!
326
399
  const typeNames = [];
327
400
  forEachDefinition(csn, (def, defName) => {
328
- if(def.kind === 'entity') {
401
+ if (def.kind === 'entity') {
329
402
  ['query', 'projection'].forEach(dictName => {
330
- applyTransformationsOnNonDictionary(csn.definitions[defName], dictName, refFlattener, iterateOptions, [ 'definitions', defName ]);
403
+ applyTransformationsOnNonDictionary(csn.definitions[defName],
404
+ dictName, refFlattener, iterateOptions,
405
+ [ 'definitions', defName ]);
331
406
  })
332
- if(csn.definitions[defName].actions)
333
- applyTransformationsOnDictionary(csn.definitions[defName].actions, refFlattener, iterateOptions, [ 'definitions', defName, 'actions' ]);
407
+ if (csn.definitions[defName].actions)
408
+ applyTransformationsOnDictionary(csn.definitions[defName].actions,
409
+ refFlattener, iterateOptions,
410
+ [ 'definitions', defName, 'actions' ]);
334
411
  }
335
412
 
336
- if(['type'].includes(def.kind)) {
413
+ if (['type'].includes(def.kind)) {
337
414
  typeNames.push(defName);
338
- applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, iterateOptions, [ 'definitions' ]);
415
+ applyTransformationsOnNonDictionary(csn.definitions,
416
+ defName, refFlattener, iterateOptions, [ 'definitions' ]);
339
417
  }
340
418
  });
341
419
  adaptRefs.forEach(fn => fn());
342
420
  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
421
 
348
- let i = links.length-2;
349
- const getProp = (propName) =>
350
- (links[i].art?.[propName] ||
351
- effectiveType(links[i].art)[propName]);
352
-
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
- }
422
+ const refCheck = {
423
+ ref: (elemref, prop, xpr, path) => {
424
+ const { links, art } = (elemref._links && elemref._art
425
+ ? { links: elemref._links, art: elemref._art }
426
+ : inspectRef(path) );
427
+
428
+ let i = links.length-2;
429
+ const getProp = (propName) =>
430
+ (links[i].art?.[propName] ||
431
+ effectiveType(links[i].art)[propName]);
432
+
433
+ const ft = csnUtils.getFinalTypeInfo(art?.type);
434
+ let target = undefined;
435
+ for(; i >= 0 && !getProp('items') && !target; i--) {
436
+ target = getProp('target');
437
+ }
438
+ if (target && csn.definitions[target].$flatelements
439
+ && !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
440
+ error('odata-anno-xpr-ref', path,
441
+ { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
361
442
  }
362
443
  }
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
444
  }
445
+ typeNames.forEach(tn => {
446
+ forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
447
+ Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
448
+ let refChanged = false;
449
+ refCheck.anno = pn;
450
+ transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
451
+ adaptRefs.forEach(fn => {
452
+ if (fn(true, 1)) refChanged = true });
453
+ adaptRefs.length = 0;
454
+ if(refChanged && member[pn]['='])
455
+ member[pn]['='] = true;
456
+ });
457
+ }, [ 'definitions', tn ]);
458
+ })
374
459
  }
375
460
 
376
461
  function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, pathDelimiter) {
@@ -395,32 +480,39 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
395
480
  const adaptRefs = [];
396
481
  const transformer = {
397
482
  ref: (parent, prop, ref, path) => {
483
+ let refChanged = false;
398
484
  const { links, art, scope } = inspectRef(path);
399
485
  const resolvedLinkTypes = resolveLinkTypes(links);
400
486
  setProp(parent, '$path', [ ...path ]);
401
487
  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;
488
+ const fn = (suspend = false, suspendPos = 0,
489
+ refFilter = (_parent) => true) => {
490
+ if (refFilter(parent)) {
491
+ const scopedPath = [ ...parent.$path ];
492
+ // TODO: If foreign key annotations should be assigned via
493
+ // full path into target, uncomment this line and
494
+ // comment/remove setProp in expansion.js
495
+ // setProp(parent, '$structRef', parent.ref);
496
+ [ parent.ref, refChanged ] = flattenStructStepsInRef(ref,
497
+ scopedPath, links, scope, resolvedLinkTypes,
498
+ suspend, suspendPos, parent.$bparam);
499
+ resolved.set(parent, { links, art, scope });
500
+ // Explicitly set implicit alias for things that are now flattened - but only in columns
501
+ // TODO: Can this be done elegantly during expand phase already?
502
+ if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
503
+ if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
504
+ delete parent.as;
505
+ delete parent.$implicitAlias;
506
+ }
507
+ // To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
508
+ else if (parent.ref[parent.ref.length - 1] !== lastRef &&
509
+ (insideColumns(scopedPath) || insideKeys(scopedPath)) &&
510
+ !parent.as) {
511
+ parent.as = lastRef;
512
+ }
513
+ return refChanged;
422
514
  }
423
-
515
+ return false;
424
516
  /**
425
517
  * Return true if the path points inside columns
426
518
  *
@@ -428,7 +520,10 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
428
520
  * @returns {boolean}
429
521
  */
430
522
  function insideColumns( path ) {
431
- return path.length >= 3 && (path[path.length - 3] === 'SELECT' || path[path.length - 3] === 'projection') && path[path.length - 2] === 'columns';
523
+ return path.length >= 3
524
+ && (path[path.length - 3] === 'SELECT'
525
+ || path[path.length - 3] === 'projection')
526
+ && path[path.length - 2] === 'columns';
432
527
  }
433
528
  /**
434
529
  * Return true if the path points inside keys
@@ -437,7 +532,9 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
437
532
  * @returns {boolean}
438
533
  */
439
534
  function insideKeys( path ) {
440
- return path.length >= 3 && path[path.length - 2] === 'keys' && typeof path[path.length - 1] === 'number';
535
+ return path.length >= 3
536
+ && path[path.length - 2] === 'keys'
537
+ && typeof path[path.length - 1] === 'number';
441
538
  }
442
539
  };
443
540
  // 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) {