@sap/cds-compiler 4.4.4 → 4.6.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 (82) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/bin/cdsc.js +18 -11
  3. package/bin/cdsv2m.js +7 -5
  4. package/doc/CHANGELOG_BETA.md +22 -0
  5. package/lib/api/main.js +306 -144
  6. package/lib/api/options.js +18 -6
  7. package/lib/api/validate.js +1 -1
  8. package/lib/base/message-registry.js +45 -10
  9. package/lib/base/messages.js +33 -16
  10. package/lib/base/model.js +4 -0
  11. package/lib/base/optionProcessorHelper.js +45 -176
  12. package/lib/checks/annotationsOData.js +49 -0
  13. package/lib/checks/elements.js +32 -34
  14. package/lib/checks/enricher.js +39 -3
  15. package/lib/checks/validator.js +8 -7
  16. package/lib/compiler/assert-consistency.js +40 -17
  17. package/lib/compiler/builtins.js +30 -53
  18. package/lib/compiler/checks.js +46 -14
  19. package/lib/compiler/cycle-detector.js +1 -4
  20. package/lib/compiler/define.js +35 -10
  21. package/lib/compiler/extend.js +21 -7
  22. package/lib/compiler/generate.js +3 -0
  23. package/lib/compiler/populate.js +5 -1
  24. package/lib/compiler/propagator.js +46 -9
  25. package/lib/compiler/resolve.js +94 -35
  26. package/lib/compiler/shared.js +60 -33
  27. package/lib/compiler/tweak-assocs.js +188 -92
  28. package/lib/compiler/utils.js +11 -1
  29. package/lib/edm/annotations/edmJson.js +41 -66
  30. package/lib/edm/annotations/genericTranslation.js +27 -9
  31. package/lib/edm/annotations/preprocessAnnotations.js +2 -3
  32. package/lib/edm/csn2edm.js +28 -11
  33. package/lib/edm/edmInboundChecks.js +58 -15
  34. package/lib/edm/edmPreprocessor.js +12 -16
  35. package/lib/edm/edmUtils.js +5 -2
  36. package/lib/gen/Dictionary.json +10 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +15 -2
  39. package/lib/gen/language.tokens +1 -0
  40. package/lib/gen/languageParser.js +6557 -5618
  41. package/lib/json/from-csn.js +4 -5
  42. package/lib/json/to-csn.js +29 -4
  43. package/lib/language/antlrParser.js +19 -1
  44. package/lib/language/errorStrategy.js +28 -7
  45. package/lib/language/genericAntlrParser.js +118 -24
  46. package/lib/language/textUtils.js +16 -0
  47. package/lib/main.d.ts +28 -3
  48. package/lib/main.js +3 -0
  49. package/lib/model/csnRefs.js +4 -1
  50. package/lib/model/csnUtils.js +20 -14
  51. package/lib/model/revealInternalProperties.js +5 -2
  52. package/lib/optionProcessor.js +23 -22
  53. package/lib/render/manageConstraints.js +13 -29
  54. package/lib/render/toCdl.js +47 -26
  55. package/lib/render/toHdbcds.js +63 -42
  56. package/lib/render/toRename.js +6 -10
  57. package/lib/render/toSql.js +71 -117
  58. package/lib/render/utils/common.js +41 -6
  59. package/lib/transform/.eslintrc.json +9 -1
  60. package/lib/transform/addTenantFields.js +228 -0
  61. package/lib/transform/db/applyTransformations.js +57 -4
  62. package/lib/transform/db/assertUnique.js +4 -4
  63. package/lib/transform/db/backlinks.js +13 -1
  64. package/lib/transform/db/cdsPersistence.js +1 -1
  65. package/lib/transform/db/expansion.js +24 -3
  66. package/lib/transform/db/flattening.js +70 -71
  67. package/lib/transform/db/killAnnotations.js +37 -0
  68. package/lib/transform/db/rewriteCalculatedElements.js +46 -6
  69. package/lib/transform/db/temporal.js +1 -1
  70. package/lib/transform/draft/db.js +2 -16
  71. package/lib/transform/draft/odata.js +3 -3
  72. package/lib/transform/effective/associations.js +3 -5
  73. package/lib/transform/effective/main.js +6 -9
  74. package/lib/transform/forOdata.js +26 -55
  75. package/lib/transform/forRelationalDB.js +38 -18
  76. package/lib/transform/odata/toFinalBaseType.js +3 -3
  77. package/lib/transform/odata/typesExposure.js +14 -5
  78. package/lib/transform/transformUtils.js +47 -34
  79. package/lib/transform/translateAssocsToJoins.js +45 -11
  80. package/lib/transform/universalCsn/coreComputed.js +1 -1
  81. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  82. package/package.json +7 -6
@@ -9,15 +9,15 @@ const transformUtils = require('../transformUtils');
9
9
  const { csnRefs } = require('../../model/csnRefs');
10
10
  const { setProp, isBetaEnabled } = require('../../base/model');
11
11
  const { forEach } = require('../../utils/objectUtils');
12
- const { cardinality2str } = require('../../model/csnUtils');
13
-
12
+ const { cardinality2str, isAnnotationExpression } = require('../../model/csnUtils');
13
+ const { transformExpression } = require('./applyTransformations');
14
14
  /**
15
15
  * Strip off leading $self from refs where applicable
16
16
  *
17
17
  * @param {CSN.Model} csn
18
18
  */
19
19
  function removeLeadingSelf( csn ) {
20
- const magicVars = [ '$now', '$self', '$projection', '$user', '$session', '$at' ];
20
+ const magicVars = [ '$now', '$self', '$projection', '$user', '$tenant', '$session', '$at' ];
21
21
  applyTransformations(csn, {
22
22
  elements: (parent, prop, elements) => {
23
23
  for (const [ elementName, element ] of Object.entries(elements)) {
@@ -42,11 +42,12 @@ function removeLeadingSelf( csn ) {
42
42
  *
43
43
  * @param {CSN.Model} csn
44
44
  * @param {CSN.Options} options
45
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
45
46
  * @param {WeakMap} resolved Cache for resolved refs
46
47
  * @param {string} pathDelimiter
47
48
  * @param {object} iterateOptions
48
49
  */
49
- function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOptions = {} ) {
50
+ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDelimiter, iterateOptions = {} ) {
50
51
  /**
51
52
  * Remove .localized from the element and any sub-elements
52
53
  *
@@ -67,7 +68,7 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
67
68
  stack.push(...Object.values(current.elements));
68
69
  }
69
70
  }
70
- const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
71
+ const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
71
72
  const { getServiceName, getFinalTypeInfo } = csnUtils;
72
73
 
73
74
  // We don't want to iterate over actions
@@ -95,18 +96,17 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
95
96
 
96
97
  // structured types might not have the child-types replaced.
97
98
  // Drill down to ensure this.
98
- let nextElements = node.elements || node.items?.elements;
99
- if (nextElements) {
100
- const stack = [ nextElements ];
101
- while (stack.length > 0) {
102
- const elements = stack.pop();
103
- for (const e of Object.values(elements)) {
104
- if (e.type && !isBuiltinType(e.type))
105
- toFinalBaseType(e, resolved, true);
106
- nextElements = e.elements || e.items?.elements;
107
- if (nextElements)
108
- stack.push(nextElements);
109
- }
99
+ const nextElements = node.elements || node.items?.elements;
100
+ const stack = nextElements ? [ nextElements ] : [];
101
+ while (stack.length > 0) {
102
+ const elements = stack.pop();
103
+ for (const e of Object.values(elements)) {
104
+ toFinalBaseType(e, resolved, true);
105
+ if (!options.toOdata && e.items) // items could have unresolved types
106
+ toFinalBaseType(e.items, resolved, true);
107
+ const next = e.elements || e.items?.elements;
108
+ if (next)
109
+ stack.push(next);
110
110
  }
111
111
  }
112
112
 
@@ -164,27 +164,28 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
164
164
  */
165
165
  function isODataItems( typeName ) {
166
166
  const typeDef = csn.definitions[typeName];
167
- return !!(options.toOdata && typeDef && typeDef.items);
167
+ return !!(options.toOdata && typeDef && typeDef?.items);
168
168
  }
169
169
  }
170
170
 
171
171
  /**
172
172
  * @param {CSN.Model} csn
173
173
  * @param {CSN.Options} options
174
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
174
175
  * @param {WeakMap} resolved Cache for resolved refs
175
176
  * @param {string} pathDelimiter
176
177
  * @param {object} iterateOptions
177
178
  */
178
- function flattenAllStructStepsInRefs( csn, options, resolved, pathDelimiter, iterateOptions = {} ) {
179
+ function flattenAllStructStepsInRefs( csn, options, messageFunctions, resolved, pathDelimiter, iterateOptions = {} ) {
179
180
  const { inspectRef, effectiveType } = csnRefs(csn);
180
- const { flattenStructStepsInRef } = transformUtils.getTransformers(csn, options, pathDelimiter);
181
+ const { flattenStructStepsInRef } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
181
182
  const adaptRefs = [];
182
183
 
183
184
  /**
184
185
  * For each step of the links, check if there is a type reference.
185
186
  * If there is, resolve it and store the result in a WeakMap.
186
187
  *
187
- * @param {Array} [links=[]]
188
+ * @param {Array} [links]
188
189
  * @todo seems too hacky
189
190
  * @returns {WeakMap} A WeakMap where a link is the key and the type is the value
190
191
  */
@@ -258,12 +259,13 @@ function flattenAllStructStepsInRefs( csn, options, resolved, pathDelimiter, ite
258
259
  /**
259
260
  * @param {CSN.Model} csn
260
261
  * @param {CSN.Options} options
262
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
261
263
  * @param {string} pathDelimiter
262
- * @param {Function} error
263
264
  * @param {object} iterateOptions
264
265
  */
265
- function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {} ) {
266
- const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
266
+ function flattenElements( csn, options, messageFunctions, pathDelimiter, iterateOptions = {} ) {
267
+ const { error } = messageFunctions;
268
+ const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
267
269
  const { isAssocOrComposition, effectiveType } = csnUtils;
268
270
  const transformers = {
269
271
  elements: flatten,
@@ -309,33 +311,28 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
309
311
  if (flatElement.notNull !== false && !branch.some(s => !s.notNull))
310
312
  flatElement.notNull = true;
311
313
 
312
-
313
314
  if (flatElement.type && isAssocOrComposition(flatElement) && flatElement.on) {
314
315
  // unmanaged relations can't be primary key
315
316
  delete flatElement.key;
316
- // Make refs resolvable by fixing the first ref step
317
- for (const onPart of flatElement.on) {
318
- if (onPart.ref) {
319
- const firstRef = onPart.ref[0];
320
-
321
- /*
322
- when element is defined in the current name resolution scope, like
323
- entity E {
324
- key x: Integer;
325
- s : {
326
- y : Integer;
327
- a3 : association to E on a3.x = y;
328
- }
329
- }
330
- We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
331
- */
317
+ transformExpression(flatElement, 'on', {
318
+ ref: (_parent, _prop, xpr) => {
332
319
  const prefix = flatElement._flatElementNameWithDots.split('.').slice(0, -1).join(pathDelimiter);
333
- const possibleFlatName = prefix + pathDelimiter + firstRef;
334
-
320
+ const possibleFlatName = prefix + pathDelimiter + xpr[0];
321
+ /*
322
+ when element is defined in the current name resolution scope, like
323
+ entity E {
324
+ key x: Integer;
325
+ s : {
326
+ y : Integer;
327
+ a3 : association to E on a3.x = y;
328
+ }
329
+ }
330
+ We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
331
+ */
335
332
  if (flatElems[possibleFlatName])
336
- onPart.ref[0] = possibleFlatName;
337
- }
338
- }
333
+ xpr[0] = possibleFlatName;
334
+ },
335
+ });
339
336
  }
340
337
  parent[prop].$orderedElements.push([ flatElemName, flatElement ]);
341
338
  // Still add them - otherwise we might not detect collisions between generated elements.
@@ -434,16 +431,16 @@ function linkForeignKeyAnnotationExtensionsToAssociation( csn, options ) {
434
431
  /**
435
432
  * @param {CSN.Model} csn
436
433
  * @param {CSN.Options} options
437
- * @param {Function} error
438
- * @param {Function} warning
434
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
439
435
  * @param {string} pathDelimiter
440
436
  * @param {boolean} flattenKeyRefs
441
437
  * @param {object} csnUtils
442
438
  * @param {object} iterateOptions
443
439
  */
444
- function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, warning, pathDelimiter, flattenKeyRefs, csnUtils, iterateOptions = {} ) {
440
+ function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFunctions, pathDelimiter, flattenKeyRefs, csnUtils, iterateOptions = {} ) {
441
+ const { error, warning } = messageFunctions;
445
442
  const { isManagedAssociation, inspectRef, isStructured } = csnUtils;
446
- const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, pathDelimiter);
443
+ const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
447
444
  if (flattenKeyRefs) {
448
445
  applyTransformations(csn, {
449
446
  elements: (parent, prop, elements, path) => {
@@ -814,6 +811,29 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
814
811
  return [ [ prefix, newFk ] ];
815
812
  }
816
813
 
814
+ fks.forEach((fk) => {
815
+ // prepend current prefix
816
+ fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
817
+ // if this is the entry association, decorate the final foreign keys with the association props
818
+ if (lvl === 0) {
819
+ if (options.transformation !== 'effective')
820
+ fk[1]['@odata.foreignKey4'] = prefix;
821
+ if (options.transformation === 'odata' || options.transformation === 'effective') {
822
+ const validAnnoNames = Object.keys(element).filter(pn => pn[0] === '@' && !isAnnotationExpression(element[pn]));
823
+ copyAnnotations(element, fk[1], true, {}, validAnnoNames);
824
+ }
825
+ // propagate not null to final foreign key
826
+ for (const prop of [ 'notNull', 'key' ]) {
827
+ if (element[prop] !== undefined)
828
+ fk[1][prop] = element[prop];
829
+ }
830
+ if (element.$location)
831
+ setProp(fk[1], '$location', element.$location);
832
+ }
833
+ });
834
+ return fks;
835
+
836
+
817
837
  /**
818
838
  * Get the path to continue resolving references
819
839
  *
@@ -834,27 +854,6 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
834
854
  return [ path, ...additions ];
835
855
  return [ ...path, ...additions ];
836
856
  }
837
-
838
- fks.forEach((fk) => {
839
- // prepend current prefix
840
- fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
841
- // if this is the entry association, decorate the final foreign keys with the association props
842
- if (lvl === 0) {
843
- if (options.transformation !== 'effective')
844
- fk[1]['@odata.foreignKey4'] = prefix;
845
- if (options.transformation === 'odata' || options.transformation === 'effective')
846
- copyAnnotations(element, fk[1], true);
847
-
848
- // propagate not null to final foreign key
849
- for (const prop of [ 'notNull', 'key' ]) {
850
- if (element[prop] !== undefined)
851
- fk[1][prop] = element[prop];
852
- }
853
- if (element.$location)
854
- setProp(fk[1], '$location', element.$location);
855
- }
856
- });
857
- return fks;
858
857
  }
859
858
 
860
859
  module.exports = {
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ const requiredAnnos = {
4
+ '@cds.persistence.skip': true,
5
+ '@cds.persistence.exists': true,
6
+ '@cds.persistence.table': true,
7
+ '@cds.persistence.journal': true, // Build checks on it
8
+ '@sql.append': true,
9
+ '@sql.prepend': true,
10
+ '@sql.replace': true, // We do a check on this, no real function
11
+ '@assert.unique': true, // We do a check on this, no real function
12
+ '@assert.integrity': true,
13
+ '@cds.valid.from': true,
14
+ '@cds.valid.to': true,
15
+ '@cds.valid.key': true,
16
+ '@odata.draft.enabled': true,
17
+ '@fiori.draft.enabled': true,
18
+ '@cds.persistence.calcview': true,
19
+ '@cds.persistence.udf': true,
20
+ '@cds.autoexpose': true,
21
+ '@cds.autoexposed': true,
22
+ '@cds.redirection.target': true,
23
+ '@Core.Computed': true,
24
+ };
25
+
26
+ /**
27
+ *
28
+ * @param {object} carrier
29
+ * @param {string} annoKey
30
+ */
31
+ function killNonrequiredAnno( carrier, annoKey ) {
32
+ if (!requiredAnnos[annoKey] && !annoKey.startsWith('@assert.unique.'))
33
+ delete carrier[annoKey];
34
+ }
35
+
36
+
37
+ module.exports = { killNonrequiredAnno };
@@ -149,8 +149,11 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
149
149
  art, env, links, scope,
150
150
  } = getRefInfo(parent, p);
151
151
 
152
+ // calc element publishes association, treat as regular
153
+ // unmanaged association
154
+ const calcElementIsAssoc = art?.value && art.target;
152
155
  // TODO: Calculated elements on-write
153
- if (art?.value && !art.value.stored) {
156
+ if (art?.value && !art.value.stored && !calcElementIsAssoc) {
154
157
  const alias = parent.as || implicitAs(parent.ref);
155
158
  // TODO: What about other scopes? expand/inline?
156
159
  const value = (scope !== 'ref-target') ? absolutifyPaths(env, art, ref, links, name).value : keepAssocStepsInRef(ref, links, art).value;
@@ -446,11 +449,15 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
446
449
  unfoldingMap[name] = [ true, [ ...columns ] ];
447
450
  }
448
451
  }
449
- else if (!columnMap[name] && hasStar) { // Via * - just append
450
- unfoldingMap[name] = [ true, [ { ref: [ name ] } ] ];
451
- }
452
- else { // just a random column - keep
453
- unfoldingMap[name] = [ false, [ columnMap[name] ] ];
452
+ else {
453
+ if (usesCalcOnRead(branches))
454
+ containsCalcOnRead = true;
455
+ if (!columnMap[name] && hasStar) { // Via * - just append
456
+ unfoldingMap[name] = [ true, [ { ref: [ name ] } ] ];
457
+ }
458
+ else { // just a random column - keep
459
+ unfoldingMap[name] = [ false, [ columnMap[name] ] ];
460
+ }
454
461
  }
455
462
  }
456
463
  }
@@ -491,6 +498,39 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
491
498
  return false;
492
499
  }
493
500
 
501
+ /**
502
+ * Returns true if the branch/column uses a calc-on-read,
503
+ * for example in a filter.
504
+ *
505
+ * TODO: Enable calculated elements next to nested projections
506
+ *
507
+ * @param {object} branches
508
+ * @returns {boolean}
509
+ */
510
+ function usesCalcOnRead( branches ) {
511
+ let returnValue = false;
512
+ for (const branchName in branches) {
513
+ const column = branches[branchName]?.steps[0]?._column;
514
+ if (column) {
515
+ applyTransformationsOnNonDictionary({ column }, 'column', {
516
+ // eslint-disable-next-line no-loop-func
517
+ ref: (parent) => {
518
+ if (hasOnReadValue(parent))
519
+ returnValue = true;
520
+ },
521
+ }, {
522
+ drillRef: true,
523
+ // skip subqueries and nested projections
524
+ // calculated elements and nested projections
525
+ // only conflict on same level
526
+ skipStandard: [ 'SELECT', 'expand', 'inline' ],
527
+ });
528
+ }
529
+ }
530
+
531
+ return returnValue;
532
+ }
533
+
494
534
  /**
495
535
  * A leaf can reference a column which in turn references a real element - that might have a .value.
496
536
  * Find such cases.
@@ -171,7 +171,7 @@ function getAnnotationHandler( csn, options, pathDelimiter, messageFunctions ) {
171
171
  const { error } = messageFunctions;
172
172
  const {
173
173
  extractValidFromToKeyElement, checkAssignment, checkMultipleAssignments, recurseElements,
174
- } = getTransformers(csn, options, pathDelimiter);
174
+ } = getTransformers(csn, options, messageFunctions, pathDelimiter);
175
175
 
176
176
  return handleTemporalAnnotations;
177
177
  /**
@@ -26,7 +26,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
26
26
  const {
27
27
  createForeignKeyElement, createAndAddDraftAdminDataProjection, createScalarElement, createAssociationElement,
28
28
  addElement, copyAndAddElement, createAssociationPathComparison, csnUtils,
29
- } = getTransformers(csn, options, pathDelimiter);
29
+ } = getTransformers(csn, options, messageFunctions, pathDelimiter);
30
30
  const { getCsnDef, isComposition } = csnUtils;
31
31
  const { error, warning } = messageFunctions;
32
32
  const generatedArtifacts = Object.create(null);
@@ -122,26 +122,12 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
122
122
 
123
123
  generatedArtifacts[draftsArtifactName] = true;
124
124
 
125
- // extract keys for UUID inspection
126
- const keys = [];
125
+ // TODO: Do we really need this? Is this possibly done by a validator earlier?
127
126
  forEachMemberRecursively(artifact, (elt, name, prop, path) => {
128
127
  if (!elt.elements && !elt.type && !elt.virtual) // only check leafs
129
128
  error(null, path, 'Expecting element to have a type when used in a draft-enabled artifact');
130
- if (elt.key && elt.key === true && !elt.virtual)
131
- keys.push(elt);
132
129
  }, [ 'definitions', artifactName ], false, { elementsOnly: true });
133
130
 
134
- // In contrast to EDM, the DB entity may have more than one technical keys but should have ideally exactly one key of type cds.UUID
135
- if (keys.length !== 1) {
136
- warning(null, [ 'definitions', artifactName ], { count: keys.length },
137
- 'Entity annotated with “@odata.draft.enabled” should have exactly one key element, but found $(COUNT)');
138
- }
139
- else {
140
- const uuidCount = keys.reduce((acc, k) => ((k.type === 'cds.UUID' || k.type === 'cds.String' && k.$renamed === 'cds.UUID' && k.length === 36) ? acc + 1 : acc), 0);
141
- if (uuidCount === 0)
142
- warning(null, [ 'definitions', artifactName ], 'Entity annotated with “@odata.draft.enabled” should have one key element of type “cds.UUID”');
143
- }
144
-
145
131
  // Ignore boolean return value. We know that we're inside a service or else we wouldn't have reached this code.
146
132
  const matchingService = getMatchingService(artifactName) || '';
147
133
  // Generate the DraftAdministrativeData projection into the service, unless there is already one
@@ -27,21 +27,21 @@ const { makeMessageFunction } = require('../../base/messages');
27
27
  * @todo check if needed at all: Remove '$projection' from paths in the element's ON-condition
28
28
  */
29
29
  function generateDrafts( csn, options, services ) {
30
+ const messageFunctions = makeMessageFunction(csn, options, 'for.odata');
31
+ const { error, info } = messageFunctions;
30
32
  const {
31
33
  createAndAddDraftAdminDataProjection, createScalarElement,
32
34
  createAssociationElement, createAssociationPathComparison,
33
35
  addElement, createAction, assignAction,
34
36
  resetAnnotation,
35
37
  csnUtils,
36
- } = getTransformers(csn, options);
38
+ } = getTransformers(csn, options, messageFunctions);
37
39
  const {
38
40
  getServiceName,
39
41
  hasAnnotationValue,
40
42
  getFinalTypeInfo,
41
43
  } = csnUtils;
42
44
 
43
- const { error, info } = makeMessageFunction(csn, options, 'for.odata');
44
-
45
45
  if (!services)
46
46
  services = getServiceNames(csn);
47
47
 
@@ -17,14 +17,12 @@ const backlinks = require('../db/backlinks');
17
17
  * @param {CSN.Model} csn Input CSN - will not be transformed
18
18
  * @param {CSN.Options} options
19
19
  * @param {object} csnUtils
20
- * @param {object} messageFunctions
21
- * @param {Function} messageFunctions.error
22
- * @param {Function} messageFunctions.warning
20
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
23
21
  * @todo Remove .keys afterwards
24
22
  * @todo Add created foreign keys into .columns in case of a query?
25
23
  * @returns {CSN.Model}
26
24
  */
27
- function turnAssociationsIntoUnmanaged( csn, options, csnUtils, { error, warning } ) {
25
+ function turnAssociationsIntoUnmanaged( csn, options, csnUtils, messageFunctions ) {
28
26
  // TODO: Do we really need this?
29
27
  forEachDefinition(csn, (artifact, artifactName) => {
30
28
  setProp(artifact, '$path', [ 'definitions', artifactName ]);
@@ -33,7 +31,7 @@ function turnAssociationsIntoUnmanaged( csn, options, csnUtils, { error, warning
33
31
  }, [ 'definitions', artifactName ]);
34
32
  });
35
33
  // Flatten out the fks and create the corresponding elements
36
- flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, error, warning, '_', true, csnUtils, { allowArtifact: () => true, skipDict: {} });
34
+ flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_', true, csnUtils, { allowArtifact: () => true, skipDict: {} });
37
35
 
38
36
  // Add the foreign keys also to the columns if the association itself was explicitly selected
39
37
  // TODO: Extend the expansion to also expand managed to their foreign
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { isBetaEnabled } = require('../../base/model');
4
4
  const { CompilerAssertion } = require('../../base/error');
5
- const { makeMessageFunction } = require('../../base/messages');
6
5
  const { cloneCsnNonDict, getUtils, isAspect } = require('../../model/csnUtils');
7
6
  const transformUtils = require('../transformUtils');
8
7
  const flattening = require('../db/flattening');
@@ -26,21 +25,21 @@ const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities }
26
25
  * @private
27
26
  * @param {CSN.Model} model Input CSN - will not be transformed
28
27
  * @param {CSN.Options} options
28
+ * @param {object} messageFunctions
29
29
  * @returns {CSN.Model}
30
30
  */
31
- function effectiveCsn( model, options ) {
31
+ function effectiveCsn( model, options, messageFunctions ) {
32
32
  if (!isBetaEnabled(options, 'effectiveCsn'))
33
33
  throw new CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
34
34
 
35
35
  const csn = cloneCsnNonDict(model, options);
36
-
37
36
  delete csn.vocabularies; // must not be set for effective CSN
37
+ messageFunctions.setModel(csn);
38
38
 
39
- const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, '_');
39
+ const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, messageFunctions, '_');
40
40
  queries.projectionToSELECTAndAddColumns(csn);
41
41
 
42
42
  let csnUtils = getUtils(csn, 'init-all');
43
- const messageFunctions = makeMessageFunction(csn, options, 'for.effective');
44
43
 
45
44
  // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
46
45
  const cleanup = validate.forRelationalDB(csn, {
@@ -65,13 +64,11 @@ function effectiveCsn( model, options ) {
65
64
 
66
65
  const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
67
66
 
68
- csnUtils = getUtils(csn, 'init-all');
69
-
70
67
  // Remove properties attached by validator - they do not "grow" as the model grows.
71
68
  cleanup();
72
69
 
73
- flattening.flattenAllStructStepsInRefs(csn, options, new WeakMap(), '_');
74
- flattening.flattenElements(csn, options, '_', messageFunctions.error);
70
+ flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, new WeakMap(), '_');
71
+ flattening.flattenElements(csn, options, messageFunctions, '_');
75
72
 
76
73
  resolveTypesInActionsAfterFlattening();
77
74