@sap/cds-compiler 4.7.6 → 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 (129) hide show
  1. package/CHANGELOG.md +63 -3
  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 +28 -1
  6. package/bin/cdshi.js +13 -3
  7. package/doc/CHANGELOG_BETA.md +24 -1
  8. package/lib/api/main.js +119 -46
  9. package/lib/api/options.js +51 -0
  10. package/lib/api/validate.js +1 -5
  11. package/lib/base/builtins.js +116 -0
  12. package/lib/base/keywords.js +5 -1
  13. package/lib/base/location.js +91 -14
  14. package/lib/base/message-registry.js +76 -46
  15. package/lib/base/messages.js +121 -35
  16. package/lib/base/model.js +4 -7
  17. package/lib/checks/actionsFunctions.js +3 -3
  18. package/lib/checks/annotationsOData.js +3 -0
  19. package/lib/checks/defaultValues.js +5 -2
  20. package/lib/checks/elements.js +2 -1
  21. package/lib/checks/enricher.js +2 -2
  22. package/lib/checks/queryNoDbArtifacts.js +5 -3
  23. package/lib/checks/utils.js +1 -1
  24. package/lib/checks/validator.js +8 -56
  25. package/lib/compiler/assert-consistency.js +11 -7
  26. package/lib/compiler/builtins.js +0 -74
  27. package/lib/compiler/checks.js +105 -29
  28. package/lib/compiler/define.js +37 -25
  29. package/lib/compiler/extend.js +35 -12
  30. package/lib/compiler/index.js +9 -10
  31. package/lib/compiler/lsp-api.js +5 -0
  32. package/lib/compiler/populate.js +13 -5
  33. package/lib/compiler/propagator.js +24 -18
  34. package/lib/compiler/resolve.js +47 -45
  35. package/lib/compiler/shared.js +61 -21
  36. package/lib/compiler/tweak-assocs.js +15 -90
  37. package/lib/compiler/utils.js +3 -3
  38. package/lib/compiler/xpr-rewrite.js +689 -0
  39. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  40. package/lib/edm/annotations/edmJson.js +7 -5
  41. package/lib/edm/annotations/genericTranslation.js +149 -71
  42. package/lib/edm/csn2edm.js +25 -9
  43. package/lib/edm/edm.js +7 -7
  44. package/lib/edm/edmInboundChecks.js +57 -5
  45. package/lib/edm/edmPreprocessor.js +54 -25
  46. package/lib/edm/edmUtils.js +3 -16
  47. package/lib/gen/Dictionary.json +138 -14
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +1 -1
  50. package/lib/gen/languageParser.js +2085 -1989
  51. package/lib/json/csnVersion.js +7 -4
  52. package/lib/json/from-csn.js +21 -11
  53. package/lib/json/to-csn.js +8 -4
  54. package/lib/language/antlrParser.js +1 -1
  55. package/lib/language/genericAntlrParser.js +23 -16
  56. package/lib/language/multiLineStringParser.js +2 -2
  57. package/lib/language/textUtils.js +1 -1
  58. package/lib/main.d.ts +90 -14
  59. package/lib/main.js +9 -1
  60. package/lib/model/cloneCsn.js +21 -9
  61. package/lib/model/csnRefs.js +153 -42
  62. package/lib/model/csnUtils.js +14 -11
  63. package/lib/model/enrichCsn.js +4 -2
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/model/sortViews.js +14 -6
  66. package/lib/modelCompare/compare.js +135 -122
  67. package/lib/optionProcessor.js +49 -2
  68. package/lib/render/DuplicateChecker.js +6 -6
  69. package/lib/render/manageConstraints.js +1 -0
  70. package/lib/render/toCdl.js +6 -3
  71. package/lib/render/toHdbcds.js +4 -48
  72. package/lib/render/toSql.js +6 -3
  73. package/lib/transform/addTenantFields.js +58 -35
  74. package/lib/transform/db/applyTransformations.js +34 -1
  75. package/lib/transform/db/constraints.js +1 -1
  76. package/lib/transform/db/expansion.js +11 -3
  77. package/lib/transform/db/flattening.js +71 -46
  78. package/lib/transform/db/groupByOrderBy.js +2 -2
  79. package/lib/transform/db/temporal.js +6 -3
  80. package/lib/transform/db/transformExists.js +2 -2
  81. package/lib/transform/db/views.js +1 -4
  82. package/lib/transform/effective/annotations.js +194 -0
  83. package/lib/transform/effective/main.js +11 -10
  84. package/lib/transform/effective/misc.js +45 -14
  85. package/lib/transform/effective/types.js +4 -3
  86. package/lib/transform/forOdata.js +29 -12
  87. package/lib/transform/forRelationalDB.js +104 -113
  88. package/lib/transform/localized.js +7 -6
  89. package/lib/transform/odata/flattening.js +228 -107
  90. package/lib/transform/odata/toFinalBaseType.js +10 -26
  91. package/lib/transform/odata/typesExposure.js +41 -25
  92. package/lib/transform/parseExpr.js +4 -7
  93. package/lib/transform/transformUtils.js +50 -43
  94. package/lib/transform/translateAssocsToJoins.js +48 -48
  95. package/lib/transform/universalCsn/coreComputed.js +2 -1
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  97. package/package.json +2 -2
  98. package/share/messages/README.md +4 -0
  99. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  100. package/share/messages/anno-missing-rewrite.md +45 -0
  101. package/share/messages/check-proper-type-of.md +1 -1
  102. package/share/messages/def-duplicate-autoexposed.md +1 -1
  103. package/share/messages/extend-repeated-intralayer.md +3 -16
  104. package/share/messages/extend-unrelated-layer.md +1 -1
  105. package/share/messages/message-explanations.json +2 -0
  106. package/share/messages/redirected-to-ambiguous.md +1 -1
  107. package/share/messages/redirected-to-complex.md +1 -1
  108. package/share/messages/redirected-to-unrelated.md +1 -1
  109. package/share/messages/rewrite-not-supported.md +1 -1
  110. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  111. package/share/messages/type-missing-enum-value.md +59 -0
  112. package/share/messages/wildcard-excluding-one.md +1 -1
  113. package/bin/.eslintrc.json +0 -17
  114. package/lib/api/.eslintrc.json +0 -37
  115. package/lib/checks/.eslintrc.json +0 -31
  116. package/lib/compiler/.eslintrc.json +0 -8
  117. package/lib/edm/.eslintrc.json +0 -46
  118. package/lib/inspect/.eslintrc.json +0 -4
  119. package/lib/json/.eslintrc.json +0 -4
  120. package/lib/language/.eslintrc.json +0 -4
  121. package/lib/model/.eslintrc.json +0 -13
  122. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  123. package/lib/render/.eslintrc.json +0 -22
  124. package/lib/transform/.eslintrc.json +0 -13
  125. package/lib/transform/db/.eslintrc.json +0 -41
  126. package/lib/transform/draft/.eslintrc.json +0 -4
  127. package/lib/transform/effective/.eslintrc.json +0 -4
  128. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  129. package/lib/utils/.eslintrc.json +0 -7
@@ -616,10 +616,10 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
616
616
  else if (typeof xpr.$env === 'number') {
617
617
  if (xpr.$scope === 'mixin')
618
618
  return '';
619
- return error(null, xpr.$path, '$env with number is not handled yet - please report this error!');
619
+ return error(null, xpr.$path, '$env with number is not handled yet - report this error!');
620
620
  }
621
621
 
622
- return error(null, xpr.$path, 'Boolean $env is not handled yet - please report this error!');
622
+ return error(null, xpr.$path, 'Boolean $env is not handled yet - report this error!');
623
623
  }
624
624
  else if (xpr.ref) {
625
625
  throw new ModelError('Missing $env and missing leading artifact ref - throwing to trigger recompilation!');
@@ -62,10 +62,9 @@ function usesMixinAssociation( query, association, associationName ) {
62
62
  * @param {CSN.Model} csn
63
63
  * @param {CSN.Options} options
64
64
  * @param {{error: Function, info: Function}} messageFunctions
65
- * @param {Function} transformCommon For the time being: Pass from outside
66
65
  * @returns {(query: CSN.Query, artifact: CSN.Artifact, artName: string, path: CSN.Path) => void} Transformer function for views
67
66
  */
68
- function getViewTransformer( csn, options, messageFunctions, transformCommon ) {
67
+ function getViewTransformer( csn, options, messageFunctions ) {
69
68
  const csnUtils = getUtils(csn);
70
69
  const {
71
70
  get$combined, isAssocOrComposition,
@@ -237,8 +236,6 @@ function getViewTransformer( csn, options, messageFunctions, transformCommon ) {
237
236
  // Copy the association element to the MIXIN clause under its alias name
238
237
  // Needs to be a deep copy, as we transform the on-condition
239
238
  const mixinElem = cloneCsnNonDict(elem, options);
240
- // Perform common transformations on the newly generated MIXIN element (won't be reached otherwise)
241
- transformCommon(mixinElem, mixinElemName);
242
239
 
243
240
  if (query.SELECT && !query.SELECT.mixin)
244
241
  query.SELECT.mixin = Object.create(null);
@@ -0,0 +1,194 @@
1
+ 'use strict';
2
+
3
+ const { CompilerAssertion } = require('../../base/error');
4
+
5
+ const directMappings = {
6
+ '@Common.IsDayOfCalendarMonth': replace('@Semantics.calendar.dayOfMonth'),
7
+ '@Common.IsDayOfCalendarYear': replace('@Semantics.calendar.dayOfYear'),
8
+ '@Common.IsCalendarWeek': replace('@Semantics.calendar.week'),
9
+ '@Common.IsCalendarMonth': replace('@Semantics.calendar.month'),
10
+ '@Common.IsCalendarQuarter': replace('@Semantics.calendar.quarter'),
11
+ '@Common.IsCalendarHalfyear': replace('@Semantics.calendar.halfyear'),
12
+ '@Common.IsCalendarYear': replace('@Semantics.calendar.year'),
13
+ '@Common.IsCalendarYearWeek': replace('@Semantics.calendar.yearWeek'),
14
+ '@Common.IsCalendarYearMonth': replace('@Semantics.calendar.yearMonth'),
15
+ '@Common.IsCalendarYearQuarter': replace('@Semantics.calendar.yearQuarter'),
16
+ '@Common.IsCalendarYearHalfyear': replace('@Semantics.calendar.yearHalfyear'),
17
+ '@Common.IsCalendarDate': replace('@Semantics.date'),
18
+ '@Common.IsFiscalYearVariant': replace('@Semantics.yearVariant'),
19
+ '@Common.IsFiscalPeriod': replace('@Semantics.period'),
20
+ '@Common.IsFiscalYear': replace('@Semantics.year'),
21
+ '@Common.IsFiscalYearPeriod': replace('@Semantics.yearPeriod'),
22
+ '@Common.IsFiscalQuarter': replace('@Semantics.quarter'),
23
+ '@Common.IsFiscalYearQuarter': replace('@Semantics.yearQuarter'),
24
+ '@Common.IsFiscalWeek': replace('@Semantics.week'),
25
+ '@Common.IsFiscalYearWeek': replace('@Semantics.yearWeek'),
26
+ '@Common.IsDayOfFiscalYear': replace('@Semantics.dayOfYear'),
27
+ '@Measures.ISOCurrency': (csn, artifact, element, oldAnno) => {
28
+ const { targetElement } = getAnnoRefTarget(csn, artifact, element[oldAnno]);
29
+ if (refPointsToThisArtifact(csn, artifact, element, oldAnno)) {
30
+ replace('@Semantics.amount.currencyCode')(csn, artifact, element, oldAnno);
31
+ if (targetElement && targetElement['@Semantics.currencyCode'] === undefined)
32
+ targetElement['@Semantics.currencyCode'] = true;
33
+ }
34
+ },
35
+ '@Measures.Unit': (csn, artifact, element, oldAnno) => {
36
+ const { targetElement } = getAnnoRefTarget(csn, artifact, element[oldAnno]);
37
+ if (refPointsToThisArtifact(csn, artifact, element, oldAnno)) {
38
+ replace('@Semantics.quantity.unitOfMeasure')(csn, artifact, element, oldAnno);
39
+ if (targetElement && targetElement['@Semantics.unitOfMeasure'] === undefined)
40
+ targetElement['@Semantics.unitOfMeasure'] = true;
41
+ }
42
+ },
43
+ '@UI.IsImageURL': replace('@Semantics.imageUrl'),
44
+ '@Common.ValueList.CollectionPath': (csn, artifact, element) => {
45
+ if (!element.target && element['@Consumption.valueHelpDefinition'] === undefined) {
46
+ if (element['@Common.ValueList.Parameters'] && Array.isArray(element['@Common.ValueList.Parameters'])) {
47
+ const InOutParameters = element['@Common.ValueList.Parameters'].filter(param => param.$Type === 'Common.ValueListParameterInOut');
48
+
49
+ if (InOutParameters.length === 1) {
50
+ element['@Consumption.valueHelpDefinition'] = [ {
51
+ name: element['@Common.ValueList.CollectionPath'],
52
+ } ];
53
+
54
+ delete element['@Common.ValueList.CollectionPath'];
55
+ delete element['@Common.ValueList.Label'];
56
+
57
+ element['@Consumption.valueHelpDefinition'][0].element = element['@Common.ValueList.Parameters'][0].ValueListProperty;
58
+ delete element['@Common.ValueList.Parameters'];
59
+ }
60
+ }
61
+ }
62
+ },
63
+ '@Common.TextFor': replace('@Semantics.text', true),
64
+ '@Common.IsLanguageIdentifier': replaceIf('@Semantics.language', true, (csn, artifact, element, anno) => !!element[anno]),
65
+ // We need to set two different annos here, depending on the value -> need a custom replacer
66
+ '@Common.Text': (csn, artifact, element, oldAnno) => {
67
+ const { targetArtifact, targetElement } = getAnnoRefTarget(csn, artifact, element[oldAnno]);
68
+ if (targetArtifact === artifact && !element['@ObjectModel.text.element'] && !targetElement['@Semantics.text']) {
69
+ element['@ObjectModel.text.element'] = element[oldAnno];
70
+ if (targetElement['@Semantics.text'] === undefined)
71
+ targetElement['@Semantics.text'] = true;
72
+ delete element['@Common.Text'];
73
+ }
74
+ else if (targetArtifact && targetElement && !element['@ObjectModel.text.association'] && !targetElement['@Semantics.text']) {
75
+ element['@ObjectModel.text.association'] = element[oldAnno];
76
+ if (targetElement['@Semantics.text'] === undefined)
77
+ targetElement['@Semantics.text'] = true;
78
+ delete element['@Common.Text'];
79
+ }
80
+ },
81
+ };
82
+
83
+ /**
84
+ *
85
+ * @param {CSN.Model} csn
86
+ * @param {CSN.Artifact} artifact
87
+ * @param {CSN.Element} element
88
+ * @param {object} anno
89
+ * @returns {boolean}
90
+ */
91
+ function refPointsToThisArtifact( csn, artifact, element, anno ) {
92
+ const { targetArtifact } = getAnnoRefTarget(csn, artifact, element[anno]);
93
+ return targetArtifact && targetArtifact === artifact;
94
+ }
95
+
96
+ /**
97
+ * Walk the possible annotation ref and return the artifact and element it points to
98
+ *
99
+ * @param {CSN.Model} csn
100
+ * @param {CSN.Artifact} startArtifact
101
+ * @param {object} annoValue
102
+ * @returns {object}
103
+ */
104
+ function getAnnoRefTarget( csn, startArtifact, annoValue ) {
105
+ if (!annoValue || !annoValue['='])
106
+ return { targetArtifact: undefined, targetElement: undefined };
107
+
108
+ const steps = annoValue['='].split('.');
109
+ let base = startArtifact;
110
+ let element;
111
+ for (const step of steps) {
112
+ if (!base.elements)
113
+ return { targetArtifact: undefined, targetElement: undefined };
114
+ element = base.elements[step];
115
+ if (!element)
116
+ return { targetArtifact: undefined, targetElement: undefined };
117
+ if (element.target)
118
+ base = csn.definitions[element.target];
119
+ }
120
+
121
+ return { targetArtifact: base, targetElement: element };
122
+ }
123
+
124
+ /**
125
+ * Get the function to replace oldAnno with newAnno on carrier.
126
+ *
127
+ * - If available, use "replacement" as value.
128
+ * - Only do replacement if "condition" returns true
129
+ * - Possibly set additional annotations via "additional"
130
+ * @param {string} newAnno
131
+ * @param {any} replacement
132
+ * @param {Function} [condition]
133
+ * @param {Function} [additional]
134
+ * @returns {Function}
135
+ */
136
+ function replace( newAnno, replacement, condition = () => true, additional = () => true ) {
137
+ return function replaceAnnotationPrefix(csn, artifact, carrier, oldAnno) {
138
+ if (carrier[newAnno] === undefined && condition(csn, artifact, carrier, oldAnno, newAnno)) {
139
+ carrier[newAnno] = replacement || carrier[oldAnno];
140
+ additional(carrier, oldAnno, newAnno);
141
+ delete carrier[oldAnno];
142
+ }
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Get the function to replace oldAnno with newAnno on carrier.
148
+ *
149
+ * - If available, use "replacement" as value.
150
+ * - Only do replacement if "condition" returns true
151
+ *
152
+ * @param {string} newAnno
153
+ * @param {any} replacement
154
+ * @param {Function} condition
155
+ * @returns {Function}
156
+ */
157
+ function replaceIf( newAnno, replacement, condition ) {
158
+ return replace( newAnno, replacement, condition );
159
+ }
160
+
161
+ /**
162
+ *
163
+ * @param {CSN.Model} csn
164
+ * @returns {object} Transfomer object for applyTransformations
165
+ */
166
+ function remapODataAnnotations( csn ) {
167
+ /**
168
+ *
169
+ * @param {CSN.Artifact} artifact
170
+ * @param {CSN.Element} element Element to process
171
+ */
172
+ function remapAnnotationsOnElement( artifact, element ) {
173
+ if (element.elements && !element.$ignore) // We expect to only be called on flattened CSN - error if we encounter .elements!
174
+ throw new CompilerAssertion(`Expected a flat model. Found element with subelements: ${JSON.stringify(element)}`);
175
+ for (const prop in element) {
176
+ if (directMappings[prop])
177
+ directMappings[prop](csn, artifact, element, prop);
178
+ }
179
+ }
180
+
181
+ return {
182
+ elements: (parent, prop, elements, path) => {
183
+ const artifact = csn.definitions[path[1]];
184
+ if (artifact?.kind === 'entity') {
185
+ for (const elementName in elements)
186
+ remapAnnotationsOnElement(artifact, elements[elementName]);
187
+ }
188
+ },
189
+ };
190
+ }
191
+
192
+ module.exports = {
193
+ remapODataAnnotations,
194
+ };
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const { isBetaEnabled } = require('../../base/model');
4
- const { CompilerAssertion } = require('../../base/error');
5
- const { getUtils, isAspect } = require('../../model/csnUtils');
3
+ const {
4
+ getUtils, isAspect, mergeTransformers, applyTransformations,
5
+ } = require('../../model/csnUtils');
6
6
  const transformUtils = require('../transformUtils');
7
7
  const flattening = require('../db/flattening');
8
8
  const types = require('./types');
@@ -14,6 +14,7 @@ const associations = require('./associations');
14
14
  const generateDrafts = require('../draft/db');
15
15
  const handleExists = require('../db/transformExists');
16
16
  const misc = require('./misc');
17
+ const annotations = require('./annotations');
17
18
  const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
18
19
  const { cloneFullCsn } = require('../../model/cloneCsn');
19
20
 
@@ -30,15 +31,12 @@ const { cloneFullCsn } = require('../../model/cloneCsn');
30
31
  * @returns {CSN.Model}
31
32
  */
32
33
  function effectiveCsn( model, options, messageFunctions ) {
33
- if (!isBetaEnabled(options, 'effectiveCsn'))
34
- throw new CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
35
-
36
34
  const csn = cloneFullCsn(model, options);
37
35
  delete csn.vocabularies; // must not be set for effective CSN
38
36
  messageFunctions.setModel(csn);
39
37
 
40
38
  const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, messageFunctions, '_');
41
- queries.projectionToSELECTAndAddColumns(csn);
39
+ const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
42
40
 
43
41
  let csnUtils = getUtils(csn, 'init-all');
44
42
 
@@ -63,7 +61,7 @@ function effectiveCsn( model, options, messageFunctions ) {
63
61
  // Expand a structured thing in: keys, columns, order by, group by
64
62
  expansion.expandStructureReferences(csn, options, '_', messageFunctions, csnUtils);
65
63
 
66
- const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
64
+ const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils, options);
67
65
 
68
66
  // Remove properties attached by validator - they do not "grow" as the model grows.
69
67
  cleanup();
@@ -80,8 +78,11 @@ function effectiveCsn( model, options, messageFunctions ) {
80
78
  associations.managedToUnmanaged(csn, options, csnUtils, messageFunctions);
81
79
  associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
82
80
  generateDrafts(csn, options, '_', messageFunctions);
83
- misc.attachPersistenceName(csn, options, csnUtils);
84
- misc.removeDefinitionsAndProperties(csn);
81
+ const transformers = mergeTransformers([ misc.attachPersistenceName(csn, options, csnUtils), options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options) ], null);
82
+ applyTransformations(csn, transformers, [], { skipIgnore: false });
83
+
84
+ if (!options.resolveProjections)
85
+ redoProjections.forEach(fn => fn());
85
86
 
86
87
  messageFunctions.throwWithError();
87
88
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- forEachDefinition, forEachMemberRecursively, getArtifactDatabaseNameOf, getElementDatabaseNameOf, applyTransformations,
4
+ getArtifactDatabaseNameOf, getElementDatabaseNameOf,
5
5
  } = require('../../model/csnUtils');
6
6
  /**
7
7
  * Attach @cds.persistence.name to all artifacts and "things".
@@ -9,15 +9,34 @@ const {
9
9
  * @param {CSN.Model} csn
10
10
  * @param {CSN.Options} options
11
11
  * @param {object} csnUtils
12
+ * @returns {object}
12
13
  */
13
14
  function attachPersistenceName( csn, options, csnUtils ) {
14
15
  const { addStringAnnotationTo } = csnUtils;
15
16
 
16
- forEachDefinition(csn, (artifact, artifactName) => {
17
- addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
17
+ /**
18
+ *
19
+ * @param {object} parent
20
+ * @param {string} prop
21
+ * @param {object} dict
22
+ * @param {CSN.Path} path
23
+ */
24
+ function addToEachMember( parent, prop, dict, path ) {
25
+ const artifact = csn.definitions[path[1]];
26
+ if (artifact?.kind === 'entity') {
27
+ for (const memberName in dict)
28
+ addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), dict[memberName]);
29
+ }
30
+ }
18
31
 
19
- forEachMemberRecursively(artifact, (member, memberName) => addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member), [ 'definitions', artifactName ]);
20
- });
32
+ return {
33
+ kind: (parent, prop, kind, path) => {
34
+ if (kind === 'entity')
35
+ addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(path[1], options.sqlMapping, csn, options.sqlDialect), parent);
36
+ },
37
+ elements: addToEachMember,
38
+ params: addToEachMember,
39
+ };
21
40
  }
22
41
 
23
42
  /**
@@ -39,20 +58,29 @@ function killProp( parent, prop ) {
39
58
  * - includes
40
59
  * - localized
41
60
  * @param {CSN.Model} csn
61
+ * @param {CSN.Options} options
62
+ * @returns {object}
42
63
  * @todo Callback-like architecture and merge with persistence name?
43
64
  */
44
- function _removeDefinitionsAndProperties( csn ) {
45
- const killers = {
65
+ function _removeDefinitionsAndProperties( csn, options ) {
66
+ const transformers = {
46
67
  $ignore: (a, b, c, path, parentParent) => {
47
68
  const tail = path[path.length - 1];
48
69
  delete parentParent[tail];
49
70
  },
50
71
  kind: (artifact, a, b, path) => {
51
- if (artifact.kind === 'aspect' || artifact.kind === 'type')
52
- delete csn.definitions[path[1]];
53
-
54
- else if (artifact['@cds.persistence.skip'] === 'if-unused')
55
- artifact['@cds.persistence.skip'] = false;
72
+ if (artifact.kind === 'aspect' || artifact.kind === 'type') {
73
+ if (artifact.elements || artifact.items || options.resolveSimpleTypes)
74
+ delete csn.definitions[path[1]];
75
+ }
76
+ else {
77
+ if (artifact.kind === 'event') {
78
+ delete artifact.projection;
79
+ delete artifact.query;
80
+ }
81
+ if (artifact['@cds.persistence.skip'] === 'if-unused')
82
+ artifact['@cds.persistence.skip'] = false;
83
+ }
56
84
  },
57
85
  // Still used in flattenStructuredElements - in db/flattening.js
58
86
  _flatElementNameWithDots: killProp,
@@ -64,13 +92,16 @@ function _removeDefinitionsAndProperties( csn ) {
64
92
  // Set when we remove .key from temporal things, used in localized.js
65
93
  $key: killProp,
66
94
  includes: killProp,
67
- localized: killProp,
68
95
  enum: killProp,
69
96
  keys: killProp,
70
97
  excluding: killProp, // * is resolved, so has no effect anymore
98
+ targetAspect: killProp,
71
99
  };
72
100
 
73
- applyTransformations(csn, killers, [], { skipIgnore: false });
101
+ if (!options.keepLocalized)
102
+ transformers.localized = killProp;
103
+
104
+ return transformers;
74
105
  }
75
106
 
76
107
 
@@ -17,10 +17,11 @@ const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
17
17
  * @todo What about annotations on the type?
18
18
  * @param {CSN.Model} csn will be transformed
19
19
  * @param {object} csnUtils
20
+ * @param {CSN.Options} options
20
21
  * @returns {Function} Callback to resolve things (actions and their returns) later - as for them, $self would lead to unresolvable constructs at this point
21
22
  * so we can call this callback after flattening is done - then we can safely resolve their types.
22
23
  */
23
- function resolveTypes( csn, csnUtils ) {
24
+ function resolveTypes( csn, csnUtils, options ) {
24
25
  const { getFinalTypeInfo } = csnUtils;
25
26
  const later = [];
26
27
  applyTransformations(csn, {
@@ -51,7 +52,7 @@ function resolveTypes( csn, csnUtils ) {
51
52
  *
52
53
  * Drill down into .elements and .items
53
54
  *
54
- * @param {object} parent Object with a .type propertie
55
+ * @param {object} parent Object with a .type property
55
56
  */
56
57
  function resolveType( parent ) {
57
58
  // TODO: I assume there can be cases with a type ref but still having .elements already? Subelement anno?
@@ -67,7 +68,7 @@ function resolveTypes( csn, csnUtils ) {
67
68
  parent.items = cloneCsnNonDict(final.items);
68
69
  delete parent.type;
69
70
  }
70
- else if (final?.type) {
71
+ else if (final?.type && (options.resolveSimpleTypes || parent.type.ref?.length > 1)) {
71
72
  forEachKey(final, (key) => { // copy `type` + properties (default, etc.)
72
73
  if (parent[key] === undefined || key === 'type')
73
74
  parent[key] = final[key];
@@ -73,7 +73,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
73
73
  timetrace.start('OData transformation');
74
74
 
75
75
  // copy the model as we don't want to change the input model
76
- let csn = cloneFullCsn(inputModel, options);
76
+ const csn = cloneFullCsn(inputModel, options);
77
77
  messageFunctions.setModel(csn);
78
78
 
79
79
  const { message, error, warning, info, throwWithAnyError } = messageFunctions;
@@ -119,6 +119,11 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
119
119
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
120
120
  enrichUniversalCsn(csn, options);
121
121
 
122
+ // - Generate artificial draft fields on a structured CSN if requested, flattening and struct
123
+ // expansion do their magic including foreign key generation and annotation propagation.
124
+ // Tenantenizer has to decorate the DraftAdministrativeData, so draft decoration must be done before.
125
+ generateDrafts(csn, options, services, messageFunctions);
126
+
122
127
  if (options.tenantDiscriminator)
123
128
  addTenantFields(csn, options);
124
129
 
@@ -171,9 +176,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
171
176
  // rendering which may has to publish external definitions
172
177
  expandToFinalBaseType(csn, transformers, csnUtils, services, options, error);
173
178
 
174
- // - Generate artificial draft fields on a structured CSN if requested, flattening and struct
175
- // expansion do their magic including foreign key generation and annotation propagation.
176
- generateDrafts(csn, options, services, messageFunctions);
177
179
 
178
180
  // Check if structured elements and managed associations are compared in an expression
179
181
  // and expand these structured elements. This tuple expansion allows all other
@@ -182,15 +184,19 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
182
184
  expandStructsInExpression(csn, { skipArtifact: isExternalServiceMember, drillRef: true });
183
185
 
184
186
  if (!structuredOData) {
185
- expansion.expandStructureReferences(csn, options, '_', { error, info, throwWithAnyError }, csnUtils, { skipArtifact: isExternalServiceMember });
187
+ expansion.expandStructureReferences(csn, options, '_',
188
+ { error, info, throwWithAnyError }, csnUtils,
189
+ { skipArtifact: isExternalServiceMember });
186
190
  const resolved = new WeakMap();
187
191
 
188
192
  const { inspectRef, effectiveType } = csnRefs(csn);
189
- const { adaptRefs, transformer: refFlattener } = flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
190
-
191
- flattening.allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options);
193
+ const { adaptRefs, transformer: refFlattener } =
194
+ flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
192
195
 
196
+ flattening.allInOneFlattening(csn, refFlattener, adaptRefs,
197
+ inspectRef, isExternalServiceMember, error, csnUtils, options);
193
198
  flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs,
199
+ inspectRef, effectiveType, csnUtils, error, options,
194
200
  { //skip: ['action', 'aspect', 'event', 'function', 'type'],
195
201
  skipArtifact: isExternalServiceMember,
196
202
  });
@@ -199,20 +205,30 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
199
205
  // rewritten path expressions
200
206
  forEachDefinition(csn, (def) => {
201
207
  ['elements', 'params'].forEach(dictName => {
202
- if(def[`$${dictName}`])
203
- def[dictName] = def[`$${dictName}`];
208
+ if(def[`$flat${dictName}`])
209
+ def[dictName] = def[`$flat${dictName}`];
204
210
  })
205
211
  if(def.$flatAnnotations) {
206
212
  Object.entries(def.$flatAnnotations).forEach(([an, av]) => {
207
213
  def[an] = av;
208
214
  })
209
215
  }
216
+ if(def.actions) {
217
+ Object.values(def.actions).forEach((action) => {
218
+ if(action.$flatAnnotations) {
219
+ Object.entries(action.$flatAnnotations).forEach(([an, av]) => {
220
+ action[an] = av;
221
+ });
222
+ }
223
+ });
224
+ }
210
225
  });
211
226
  }
212
227
 
213
228
  // TODO: add the generated foreign keys to the columns when we are in a view
214
229
  // see db/views.js::addForeignKeysToColumns
215
- flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_', !structuredOData, csnUtils,{ skipArtifact: isExternalServiceMember });
230
+ flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_',
231
+ !structuredOData, csnUtils,{ skipArtifact: isExternalServiceMember });
216
232
 
217
233
  // Allow using managed associations as steps in on-conditions to access their fks
218
234
  // To be done after handleManagedAssociationsAndCreateForeignKeys,
@@ -251,7 +267,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
251
267
  // Annotate artifacts with their DB names if requested.
252
268
  // Skip artifacts that have no DB equivalent anyway
253
269
  if (options.sqlMapping && !(def.kind in skipPersNameKinds))
254
- def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
270
+ // hana to allow naming mode "hdbcds"
271
+ def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana');
255
272
 
256
273
  forEachMemberRecursively(def, (member, memberName, propertyName) => {
257
274
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested