@sap/cds-compiler 6.7.3 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bin/cdsc.js +5 -5
  3. package/bin/cdsse.js +1 -1
  4. package/lib/api/main.js +9 -8
  5. package/lib/api/options.js +2 -1
  6. package/lib/api/validate.js +1 -1
  7. package/lib/base/error.js +2 -0
  8. package/lib/base/message-registry.js +7 -2
  9. package/lib/base/messages.js +2 -2
  10. package/lib/{optionProcessor.js → base/optionProcessor.js} +3 -3
  11. package/lib/base/{model.js → specialOptions.js} +16 -39
  12. package/lib/checks/arrayOfs.js +1 -1
  13. package/lib/checks/elements.js +1 -1
  14. package/lib/checks/enricher.js +2 -2
  15. package/lib/checks/featureFlags.js +54 -24
  16. package/lib/checks/foreignKeys.js +1 -1
  17. package/lib/checks/invalidTarget.js +1 -1
  18. package/lib/checks/managedInType.js +1 -1
  19. package/lib/checks/onConditions.js +1 -1
  20. package/lib/checks/queryNoDbArtifacts.js +1 -1
  21. package/lib/checks/validator.js +10 -14
  22. package/lib/compiler/builtins.js +1 -1
  23. package/lib/compiler/checks.js +3 -3
  24. package/lib/compiler/define.js +5 -2
  25. package/lib/{base → compiler}/dictionaries.js +2 -0
  26. package/lib/compiler/extend.js +2 -2
  27. package/lib/compiler/generate.js +2 -2
  28. package/lib/compiler/index.js +11 -3
  29. package/lib/compiler/kick-start.js +1 -1
  30. package/lib/compiler/populate.js +2 -2
  31. package/lib/compiler/resolve.js +4 -2
  32. package/lib/compiler/shared.js +35 -6
  33. package/lib/compiler/utils.js +2 -4
  34. package/lib/compiler/xpr-rewrite.js +1 -1
  35. package/lib/edm/annotations/edmJson.js +2 -4
  36. package/lib/edm/annotations/genericTranslation.js +2 -1
  37. package/lib/edm/csn2edm.js +3 -2
  38. package/lib/edm/edmAnnoPreprocessor.js +1 -1
  39. package/lib/edm/edmInboundChecks.js +2 -1
  40. package/lib/edm/edmPreprocessor.js +3 -3
  41. package/lib/edm/edmUtils.js +2 -2
  42. package/lib/gen/BaseParser.js +1 -12
  43. package/lib/gen/CdlGrammar.checksum +1 -1
  44. package/lib/gen/CdlParser.js +1068 -1067
  45. package/lib/json/from-csn.js +7 -2
  46. package/lib/json/to-csn.js +17 -2
  47. package/lib/main.js +3 -3
  48. package/lib/model/csnUtils.js +2 -2
  49. package/lib/modelCompare/compare.js +1 -1
  50. package/lib/modelCompare/utils/filter.js +1 -0
  51. package/lib/parsers/AstBuildingParser.js +40 -3
  52. package/lib/parsers/index.js +1 -1
  53. package/lib/render/manageConstraints.js +1 -1
  54. package/lib/render/toCdl.js +3 -3
  55. package/lib/render/toHdbcds.js +2 -2
  56. package/lib/render/toSql.js +7 -7
  57. package/lib/render/utils/common.js +9 -2
  58. package/lib/render/utils/sql.js +14 -5
  59. package/lib/render/utils/standardDatabaseFunctions.js +108 -99
  60. package/lib/sql-identifier.js +9 -1
  61. package/lib/{model → tool-lib}/enrichCsn.js +2 -2
  62. package/lib/{model → tool-lib}/revealInternalProperties.js +2 -1
  63. package/lib/transform/addTenantFields.js +1 -1
  64. package/lib/transform/db/applyTransformations.js +1 -1
  65. package/lib/transform/db/assertUnique.js +1 -1
  66. package/lib/transform/db/assocsToQueries/transformExists.js +1 -1
  67. package/lib/transform/db/backlinks.js +2 -2
  68. package/lib/transform/db/expansion.js +2 -2
  69. package/lib/transform/db/flattening.js +3 -4
  70. package/lib/transform/db/killAnnotations.js +1 -0
  71. package/lib/transform/db/processSqlServices.js +2 -1
  72. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  73. package/lib/transform/db/temporal.js +30 -5
  74. package/lib/transform/db/views.js +16 -20
  75. package/lib/transform/draft/db.js +1 -2
  76. package/lib/transform/effective/associations.js +1 -1
  77. package/lib/transform/effective/flattening.js +1 -1
  78. package/lib/transform/effective/main.js +19 -4
  79. package/lib/transform/effective/types.js +1 -1
  80. package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
  81. package/lib/transform/forOdata.js +5 -5
  82. package/lib/transform/forRelationalDB.js +41 -9
  83. package/lib/transform/localized.js +2 -2
  84. package/lib/transform/odata/createForeignKeys.js +1 -1
  85. package/lib/transform/odata/flattening.js +2 -2
  86. package/lib/transform/odata/toFinalBaseType.js +3 -2
  87. package/lib/transform/odata/typesExposure.js +3 -2
  88. package/lib/transform/transformUtils.js +2 -2
  89. package/lib/transform/translateAssocsToJoins.js +2 -1
  90. package/lib/transform/tupleExpansion.js +4 -4
  91. package/lib/transform/universalCsn/universalCsnEnricher.js +7 -3
  92. package/lib/transform/universalCsn/utils.js +1 -1
  93. package/lib/{base → utils}/lazyload.js +9 -0
  94. package/lib/{base → utils}/node-helpers.js +2 -0
  95. package/lib/utils/objectUtils.js +29 -6
  96. package/lib/{base → utils}/optionProcessorHelper.js +16 -6
  97. package/package.json +2 -2
  98. /package/lib/{model → base}/cloneCsn.js +0 -0
  99. /package/lib/{model → base}/csnRefs.js +0 -0
  100. /package/lib/{model/api.js → base/model-api.js} +0 -0
  101. /package/lib/{api → base}/trace.js +0 -0
  102. /package/lib/{model → base}/xprAsTree.js +0 -0
  103. /package/lib/{inspect → tool-lib}/index.js +0 -0
  104. /package/lib/{inspect → tool-lib}/inspectModelStatistics.js +0 -0
  105. /package/lib/{inspect → tool-lib}/inspectPropagation.js +0 -0
  106. /package/lib/{inspect → tool-lib}/inspectUtils.js +0 -0
  107. /package/lib/{base → utils}/shuffle.js +0 -0
@@ -57,18 +57,21 @@ const sqlDialects = {
57
57
  reservedWords: keywords.postgres,
58
58
  effectiveName: name => name.toLowerCase(),
59
59
  asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
60
+ maxIdentifierLength: 63,
60
61
  },
61
62
  hana: {
62
63
  regularRegex: /^[A-Za-z_][A-Za-z_$#0-9]*$/,
63
64
  reservedWords: keywords.hana,
64
65
  effectiveName: name => name.toUpperCase(),
65
66
  asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
67
+ maxIdentifierLength: 127,
66
68
  },
67
69
  hdbcds: {
68
70
  regularRegex: /^[A-Za-z_][A-Za-z_0-9]*$/,
69
71
  reservedWords: keywords.hdbcds,
70
72
  effectiveName: name => name,
71
73
  asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
74
+ maxIdentifierLength: 127,
72
75
  },
73
76
  };
74
77
 
@@ -98,4 +101,9 @@ function delimitedId( name, dialect ) {
98
101
  return s.asDelimitedId( name );
99
102
  }
100
103
 
101
- module.exports = { smartId, smartFuncId, delimitedId };
104
+ module.exports = {
105
+ smartId,
106
+ smartFuncId,
107
+ delimitedId,
108
+ sqlDialects,
109
+ };
@@ -39,11 +39,11 @@
39
39
 
40
40
  'use strict';
41
41
 
42
- const { csnRefs, artifactProperties } = require('./csnRefs');
42
+ const { csnRefs, artifactProperties } = require('../base/csnRefs');
43
43
  const { locationString } = require('../base/location');
44
44
  const { CompilerAssertion } = require('../base/error');
45
45
  const { isAnnotationExpression } = require('../base/builtins');
46
- const shuffleGen = require('../base/shuffle');
46
+ const shuffleGen = require('../utils/shuffle');
47
47
 
48
48
  function enrichCsn( csn, options = {} ) {
49
49
  const transformers = {
@@ -283,7 +283,8 @@ function revealInternalProperties( model, nameOrPath ) {
283
283
  }
284
284
 
285
285
  function revealSingleExtension( node, parent ) {
286
- return (node.kind && node.__unique_id__ == null && node.$effectiveSeqNo == null && !node.builtin)
286
+ return (node.kind && node.__unique_id__ == null &&
287
+ node.$effectiveSeqNo == null && !node.builtin)
287
288
  ? reveal( node, parent )
288
289
  : artifactIdentifier( node, parent );
289
290
  }
@@ -23,7 +23,7 @@ const {
23
23
  traverseQuery,
24
24
  implicitAs,
25
25
  pathId,
26
- } = require( '../model/csnRefs' );
26
+ } = require( '../base/csnRefs' );
27
27
 
28
28
  const annoTenantIndep = '@cds.tenant.independent';
29
29
 
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
 
15
- const { setProp } = require('../../base/model');
15
+ const { setProp } = require('../../utils/objectUtils');
16
16
  const { isAnnotationExpression } = require('../../base/builtins');
17
17
 
18
18
 
@@ -78,7 +78,7 @@ function processAssertUnique( csn, options, messageFunctions ) {
78
78
  // constraintKey is the concatenation of all flattened paths (order is important)
79
79
  let constraintKey = '';
80
80
  flattenedPathObjects.forEach((p) => {
81
- const pstr = p.ref.map(path => path.id).join('.');
81
+ const pstr = p.ref.map(path => path.id || path).join('.');
82
82
  constraintKey += pstr;
83
83
  if (!pathxrefs[pstr])
84
84
  pathxrefs[pstr] = 1;
@@ -3,7 +3,7 @@
3
3
  const {
4
4
  forAllQueries, forEachDefinition, walkCsnPath, transformExpression,
5
5
  } = require('../../../model/csnUtils');
6
- const { setProp } = require('../../../base/model');
6
+ const { setProp } = require('../../../utils/objectUtils');
7
7
  const { getHelpers } = require('./utils');
8
8
 
9
9
  const normalizeFromLeaf = require('./normalizeFrom');
@@ -4,9 +4,9 @@ const {
4
4
  applyTransformationsOnNonDictionary, isAssociationOperand, isDollarSelfOrProjectionOperand,
5
5
  } = require('../../model/csnUtils');
6
6
 
7
- const { setProp } = require('../../base/model');
7
+ const { setProp } = require('../../utils/objectUtils');
8
8
  const { forEach } = require('../../utils/objectUtils');
9
- const { cloneCsnNonDict } = require('../../model/cloneCsn');
9
+ const { cloneCsnNonDict } = require('../../base/cloneCsn');
10
10
  const { ModelError } = require('../../base/error');
11
11
 
12
12
  /**
@@ -6,8 +6,8 @@ const {
6
6
  getUtils,
7
7
  forEachDefinition,
8
8
  } = require('../../model/csnUtils');
9
- const { implicitAs, columnAlias, pathId } = require('../../model/csnRefs');
10
- const { setProp } = require('../../base/model');
9
+ const { implicitAs, columnAlias, pathId } = require('../../base/csnRefs');
10
+ const { setProp } = require('../../utils/objectUtils');
11
11
  const { killNonrequiredAnno } = require('./killAnnotations');
12
12
  const { featureFlags } = require('../featureFlags');
13
13
  const { applyTransformationsOnNonDictionary } = require('./applyTransformations');
@@ -9,11 +9,10 @@ const {
9
9
  } = require('../../model/csnUtils');
10
10
  const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
11
11
  const transformUtils = require('../transformUtils');
12
- const { csnRefs } = require('../../model/csnRefs');
13
- const { setProp } = require('../../base/model');
14
- const { forEach } = require('../../utils/objectUtils');
12
+ const { csnRefs } = require('../../base/csnRefs');
13
+ const { setProp, forEach } = require('../../utils/objectUtils');
15
14
  const { transformExpression } = require('./applyTransformations');
16
- const { cloneCsnNonDict } = require('../../model/cloneCsn');
15
+ const { cloneCsnNonDict } = require('../../base/cloneCsn');
17
16
  const { EdmTypeFacetNames } = require('../../edm/EdmPrimitiveTypeDefinitions');
18
17
  const { adaptAnnotationsRefs } = require('../odata/adaptAnnotationRefs');
19
18
 
@@ -27,6 +27,7 @@ const requiredAnnos = {
27
27
  [sqlServiceAnnotation]: true,
28
28
  '@cds.external': true, // for external ABAP SQL services and data products for now
29
29
  '@data.product': true, // for data product production
30
+ '@hierarchy': true, // for Fiori Tree Views
30
31
  };
31
32
 
32
33
  /**
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { setProp, isBetaEnabled } = require('../../base/model');
3
+ const { setProp } = require('../../utils/objectUtils');
4
+ const { isBetaEnabled } = require('../../base/specialOptions');
4
5
  const { hasPersistenceSkipAnnotation } = require('../../model/csnUtils');
5
6
 
6
7
  const sqlServiceAnnotation = '@protocol';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { setProp } = require('../../base/model');
3
+ const { setProp } = require('../../utils/objectUtils');
4
4
  const { CompilerAssertion } = require('../../base/error');
5
5
  const {
6
6
  forEachDefinition,
@@ -9,7 +9,7 @@ const {
9
9
  implicitAs,
10
10
  } = require('../../model/csnUtils');
11
11
  const { getBranches } = require('./flattening');
12
- const { cloneCsnNonDict } = require('../../model/cloneCsn');
12
+ const { cloneCsnNonDict } = require('../../base/cloneCsn');
13
13
 
14
14
  const cloneCsnOptions = { hiddenPropertiesToClone: [ '_art', '_links', '$env', '$scope' ] };
15
15
 
@@ -3,8 +3,9 @@
3
3
  const {
4
4
  getNormalizedQuery, forEachMember,
5
5
  } = require('../../model/csnUtils');
6
- const { implicitAs } = require('../../model/csnRefs');
7
- const { setProp, isBetaEnabled } = require('../../base/model');
6
+ const { implicitAs } = require('../../base/csnRefs');
7
+ const { setProp } = require('../../utils/objectUtils');
8
+ const { isBetaEnabled } = require('../../base/specialOptions');
8
9
  const { getTransformers } = require('../transformUtils');
9
10
 
10
11
  const validToString = '@cds.valid.to';
@@ -206,9 +207,16 @@ function getAnnotationHandler( csn, options, pathDelimiter, messageFunctions ) {
206
207
  'Expecting $(NAME) and $(ID) if $(ANNO) is used');
207
208
  }
208
209
 
209
- forEachMember(artifact, (member) => {
210
+ // Collect paths for unique constraint (original keys + validFrom)
211
+ const uniquePaths = [];
212
+ const addedMembers = new Set();
213
+
214
+ forEachMember(artifact, (member, memberName) => {
210
215
  if (member.key) {
211
- member.unique = true;
216
+ // Add to unique constraint paths - use the same ref format as assertUnique.prepare does
217
+ // this can then be used by assertUnique.rewrite to process
218
+ uniquePaths.push({ ref: [ { id: memberName } ], _art: member });
219
+ addedMembers.add(memberName);
212
220
  delete member.key;
213
221
  // Remember that this element was a key in the original artifact.
214
222
  // This is needed for localized convenience view generation.
@@ -220,8 +228,25 @@ function getAnnotationHandler( csn, options, pathDelimiter, messageFunctions ) {
220
228
  });
221
229
 
222
230
  validFrom.forEach((member) => {
223
- member.element.unique = true;
231
+ // Add validFrom to unique constraint paths (if not already added as a key)
232
+ const memberName = member.path[member.path.length - 1];
233
+ if (!addedMembers.has(memberName))
234
+ uniquePaths.push({ ref: [ { id: memberName } ], _art: member.element });
224
235
  });
236
+
237
+ // Create $tableConstraints.unique entry - will be rewritten by assertUnique.rewrite()
238
+ if (uniquePaths.length > 0) {
239
+ if (!artifact.$tableConstraints)
240
+ artifact.$tableConstraints = Object.create(null);
241
+ if (!artifact.$tableConstraints.unique)
242
+ artifact.$tableConstraints.unique = Object.create(null);
243
+
244
+ // Use a named constraint
245
+ artifact.$tableConstraints.unique.__c_cds_valid = {
246
+ paths: uniquePaths,
247
+ parentTable: artifactName,
248
+ };
249
+ }
225
250
  }
226
251
  else {
227
252
  validFrom.forEach((member) => {
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- getUtils, applyTransformationsOnNonDictionary, forEachDefinition,
4
+ getUtils, applyTransformationsOnNonDictionary,
5
5
  } = require('../../model/csnUtils');
6
- const { implicitAs, columnAlias, pathId } = require('../../model/csnRefs');
6
+ const { implicitAs, columnAlias, pathId } = require('../../base/csnRefs');
7
7
  const { ModelError } = require('../../base/error');
8
- const { setProp } = require('../../base/model');
9
- const { cloneCsnNonDict } = require('../../model/cloneCsn');
8
+ const { setProp } = require('../../utils/objectUtils');
9
+ const { cloneCsnNonDict } = require('../../base/cloneCsn');
10
10
 
11
11
  /**
12
12
  * If a mixin association is published, return the mixin association.
@@ -472,7 +472,7 @@ function getColumnMap( query, csnUtils ) {
472
472
 
473
473
 
474
474
  /**
475
- * Ensure that each column in the CSN has a name. A column does not have
475
+ * Ensure that a single query's columns have names. A column does not have
476
476
  * a name if the column is an expression and there is no explicit alias.
477
477
  * In that case an internal alias (from csnRefs()) is used and made explicit
478
478
  * via non-enumerable `as`.
@@ -485,24 +485,20 @@ function getColumnMap( query, csnUtils ) {
485
485
  * - We can't use e.g. `$as`, as csnRefs() does not use that property, and it must not
486
486
  * invent another name for the column (could happen after flattening).
487
487
  *
488
- * @param {CSN.Model} csn
489
- * @param {CSN.Options} options
488
+ * @param {object} query
490
489
  * @param {object} csnUtils
490
+ * @param {CSN.Options} options
491
491
  */
492
- function ensureColumnNames( csn, options, csnUtils ) {
493
- forEachDefinition(csn, (def) => {
494
- csnUtils.initDefinition(def);
495
- for (const query of csnUtils.$getQueries(def) || []) {
496
- for (const col of query._select.columns || []) {
497
- if (col !== '*' && !columnAlias(col)) {
498
- if (options.transformation === 'hdbcds')
499
- col.as = csnUtils.getColumnName(col);
500
- else
501
- setProp(col, 'as', csnUtils.getColumnName(col));
502
- }
503
- }
492
+ function ensureColumnNames( query, csnUtils, options ) {
493
+ const select = query.SELECT || query.projection;
494
+ for (const col of select?.columns || []) {
495
+ if (col !== '*' && !columnAlias(col)) {
496
+ if (options.transformation === 'hdbcds')
497
+ col.as = csnUtils.getColumnName(col);
498
+ else
499
+ setProp(col, 'as', csnUtils.getColumnName(col));
504
500
  }
505
- });
501
+ }
506
502
  }
507
503
 
508
504
  module.exports = {
@@ -4,10 +4,9 @@ const {
4
4
  getServiceNames, forEachDefinition,
5
5
  getResultingName, forEachMemberRecursively, applyAnnotationsFromExtensions,
6
6
  } = require('../../model/csnUtils');
7
- const { setProp } = require('../../base/model');
8
7
  const { getTransformers } = require('../transformUtils');
9
8
  const { ModelError } = require('../../base/error');
10
- const { forEach } = require('../../utils/objectUtils');
9
+ const { setProp, forEach } = require('../../utils/objectUtils');
11
10
  const draftAnnotation = '@odata.draft.enabled';
12
11
  const booleanBuiltin = 'cds.Boolean';
13
12
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { setProp } = require('../../base/model');
3
+ const { setProp } = require('../../utils/objectUtils');
4
4
  const flattening = require('../db/flattening');
5
5
  const {
6
6
  applyTransformations, forEachDefinition, forEachMemberRecursively, forEachMember, applyTransformationsOnNonDictionary,
@@ -4,7 +4,7 @@ const {
4
4
  forEachDefinition, forEachMemberRecursively, applyTransformationsOnNonDictionary, transformExpression, transformAnnotationExpression,
5
5
  } = require('../../model/csnUtils');
6
6
  const { getStructStepsFlattener } = require('../db/flattening');
7
- const { setProp } = require('../../base/model');
7
+ const { setProp } = require('../../utils/objectUtils');
8
8
  const { forEach } = require('../../utils/objectUtils');
9
9
 
10
10
 
@@ -16,11 +16,13 @@ const handleExists = require('../db/assocsToQueries/transformExists');
16
16
  const misc = require('./misc');
17
17
  const annotations = require('./annotations');
18
18
  const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
19
- const { cloneFullCsn } = require('../../model/cloneCsn');
19
+ const { cloneFullCsn } = require('../../base/cloneCsn');
20
20
  const { featureFlags, removeFeatureFlags } = require('../featureFlags');
21
21
  const getServiceFilterFunction = require('./service');
22
- const { traverseQuery } = require('../../model/csnRefs');
22
+ const { traverseQuery } = require('../../base/csnRefs');
23
23
  const { expandWildcard } = require('../db/expansion');
24
+ const { getQueryFeatureFlagSetter, getDefinitionFeatureFlagSetter } = require('../../checks/featureFlags');
25
+ const shuffleGen = require('../../utils/shuffle');
24
26
 
25
27
  /**
26
28
  * This is just a PoC for now!
@@ -40,6 +42,10 @@ function effectiveCsn( model, options, messageFunctions ) {
40
42
  delete csn.vocabularies; // must not be set for effective CSN
41
43
  messageFunctions.setModel(csn);
42
44
 
45
+ const { shuffleDict } = shuffleGen(options.testMode);
46
+ if (options.testMode && csn.definitions)
47
+ csn.definitions = shuffleDict(csn.definitions);
48
+
43
49
  const transformerUtils = transformUtils.getTransformers(csn, options, messageFunctions, '_');
44
50
  const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
45
51
 
@@ -48,9 +54,17 @@ function effectiveCsn( model, options, messageFunctions ) {
48
54
  let { csnUtils } = transformerUtils;
49
55
  csnUtils.initAllDefinitions();
50
56
 
57
+ const setQueryFeatureFlags = getQueryFeatureFlagSetter(csn);
58
+ const setDefinitionFeatureFlags = getDefinitionFeatureFlagSetter(csn, options);
59
+
51
60
  forEachDefinition(csn, (def) => {
52
- if (def.query || def.projection)
53
- traverseQuery(def.query || def, null, null, query => expandWildcard(query, csnUtils, options));
61
+ if (def.query || def.projection) {
62
+ traverseQuery(def.query || def, null, null, (query) => {
63
+ expandWildcard(query, csnUtils, options);
64
+ setQueryFeatureFlags(query);
65
+ });
66
+ }
67
+ setDefinitionFeatureFlags(def);
54
68
  });
55
69
 
56
70
  // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
@@ -94,6 +108,7 @@ function effectiveCsn( model, options, messageFunctions ) {
94
108
  transformerUtils.csnUtils = getUtils(csn, 'init-all');
95
109
  associations.managedToUnmanaged(csn, options, transformerUtils, messageFunctions);
96
110
  associations.transformBacklinks(csn, options, transformerUtils, messageFunctions);
111
+
97
112
  const transformers = mergeTransformers([
98
113
  options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {},
99
114
  misc.removeDefinitionsAndProperties(csn, options),
@@ -6,7 +6,7 @@ const {
6
6
  applyTransformationsOnDictionary,
7
7
  } = require('../../model/csnUtils');
8
8
  const { forEachKey } = require('../../utils/objectUtils');
9
- const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
9
+ const { cloneCsnDict, cloneCsnNonDict } = require('../../base/cloneCsn');
10
10
  const propertiesToSkipForCasts = { enum: true };
11
11
 
12
12
  /**
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { isBuiltinType } = require('../../base/builtins');
3
+ const { isBuiltinType } = require('../base/builtins');
4
4
  /**
5
5
  * Processing the `@hierarchy` annotation on projections/views and generates:
6
6
  * - the OData annotations: `@Aggregation.RecursiveHierarchy`, `@Hierarchy.RecursiveHierarchy`
@@ -16,17 +16,22 @@ const { isBuiltinType } = require('../../base/builtins');
16
16
  * - the foreign key is of scalar type
17
17
  * Otherwise, appropriate warnings/errors are raised.
18
18
  */
19
- function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers) {
19
+ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers, options) {
20
20
  const { error, warning } = messageFunctions;
21
- const { isManagedAssociation, isAssociation } = csnUtils;
21
+ const { isAssociation } = csnUtils;
22
22
  const { setAnnotation, addElement, createScalarElement } = transformers;
23
+ const isODataTransformation = options.transformation === 'odata';
23
24
 
24
- if (Object.keys(def).some(key => key.startsWith('@hierarchy#')))
25
- error(null, [ 'definitions', defName ], {}, 'Assigning qualifier with the @hierarchy annotation is not supported');
25
+ if (Object.keys(def).some(key => key.startsWith('@hierarchy#'))) {
26
+ if (isODataTransformation)
27
+ error(null, [ 'definitions', defName ], {}, 'Assigning qualifier with the @hierarchy annotation is not supported');
28
+ return;
29
+ }
26
30
 
27
31
  // supported only on projections or views
28
32
  if (!(def.projection || def.query)) {
29
- warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy is only supported on projections and views');
33
+ if (isODataTransformation)
34
+ warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy is only supported on projections and views');
30
35
  return;
31
36
  }
32
37
 
@@ -34,7 +39,8 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
34
39
  const mngAssocsToSelf = [];
35
40
  const unmgAssocsToSelf = [];
36
41
  Object.entries(def.elements).forEach(([ name, elem ]) => {
37
- if (isManagedAssociation(elem) && elem.target === defName && ![ 'DraftAdministrativeData' ].includes(name))
42
+ // if (isManagedAssociation(elem) && elem.target === defName && ![ 'DraftAdministrativeData' ].includes(name))
43
+ if (isAssociation(elem) && elem.keys && elem.target === defName && ![ 'DraftAdministrativeData' ].includes(name))
38
44
  mngAssocsToSelf.push([ name, elem ]);
39
45
  else if (isAssociation(elem) && elem.on &&
40
46
  elem.target === defName && ![ 'SiblingEntity' ].includes(name))
@@ -45,19 +51,19 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
45
51
  // association pointing to self and has a single foreign key that is of scalar type
46
52
  if (typeof def['@hierarchy'] === 'boolean' && def['@hierarchy'] === true) {
47
53
  if (mngAssocsToSelf.length > 1) {
48
- warning(null, [ 'definitions', defName ], {},
49
- 'Annotation @hierarchy with value true is ignored as multiple managed associations to self exist');
54
+ if (isODataTransformation)
55
+ warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy with value true is ignored as multiple managed associations to self exist');
50
56
  return;
51
57
  }
52
58
  else if (mngAssocsToSelf.length === 0) {
53
59
  if (unmgAssocsToSelf.length === 1) {
54
- error(null, unmgAssocsToSelf[0][1].$path || [ 'definitions', defName, 'elements', unmgAssocsToSelf[0][0] ],
55
- { name: unmgAssocsToSelf[0][0] }, 'Annotation @hierarchy is not supported for unmanaged association $(NAME)');
56
- }
57
- else {
58
- warning(null, [ 'definitions', defName ], {},
59
- 'Annotation @hierarchy with value true is ignored as no managed association to self exists');
60
+ if (isODataTransformation) {
61
+ error(null, unmgAssocsToSelf[0][1].$path || [ 'definitions', defName, 'elements', unmgAssocsToSelf[0][0] ],
62
+ { name: unmgAssocsToSelf[0][0] }, 'Annotation @hierarchy is not supported for unmanaged association $(NAME)');
63
+ }
60
64
  }
65
+ if (isODataTransformation)
66
+ warning(null, [ 'definitions', defName ], {}, 'Annotation @hierarchy with value true is ignored as no managed association to self exists');
61
67
  return;
62
68
  }
63
69
 
@@ -65,7 +71,8 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
65
71
  const assocName = mngAssocsToSelf[0][0];
66
72
 
67
73
  if (isValidHierarchyAssociation(assoc, assocName)) {
68
- addHierarchyAnnotations(def, defName, assoc.keys[0].ref.join(), assocName);
74
+ if (isODataTransformation)
75
+ addHierarchyAnnotations(def, defName, assoc.keys[0].ref.join(), assocName);
69
76
  addHierarchyFields(def, defName);
70
77
  }
71
78
  }
@@ -73,19 +80,22 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
73
80
  const assocName = def['@hierarchy']['='];
74
81
  const unmngAssoc = unmgAssocsToSelf.find(a => a[0] === assocName);
75
82
  if (unmngAssoc) {
76
- error(null, unmngAssoc[1].$path || [ 'definitions', defName, 'elements', unmngAssoc[0] ],
77
- { name: unmngAssoc[0] }, 'Annotation @hierarchy is not supported for unmanaged association $(NAME)');
83
+ if (isODataTransformation) {
84
+ error(null, unmngAssoc[1].$path || [ 'definitions', defName, 'elements', unmngAssoc[0] ],
85
+ { name: unmngAssoc[0] }, 'Annotation @hierarchy is not supported for unmanaged association $(NAME)');
86
+ }
78
87
  return;
79
88
  }
80
89
 
81
90
  const assoc = mngAssocsToSelf.find(a => a[0] === assocName);
82
91
  if (!assoc) {
83
- warning(null, [ 'definitions', defName ], { name: assocName },
84
- 'Annotation @hierarchy refers to a non-existing managed association $(NAME)');
92
+ if (isODataTransformation)
93
+ warning(null, [ 'definitions', defName ], { name: assocName }, 'Annotation @hierarchy refers to a non-existing managed association $(NAME)');
85
94
  return;
86
95
  }
87
96
  if (isValidHierarchyAssociation(assoc[1], assocName)) {
88
- addHierarchyAnnotations(def, defName, assoc[1].keys[0].ref.join(), assocName);
97
+ if (isODataTransformation)
98
+ addHierarchyAnnotations(def, defName, assoc[1].keys[0].ref.join(), assocName);
89
99
  addHierarchyFields(def, defName);
90
100
  }
91
101
  }
@@ -94,16 +104,20 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
94
104
  function isValidHierarchyAssociation(assoc, assocName) {
95
105
  // the association must have exactly one scalar foreign key
96
106
  if (assoc.keys.length > 1) {
97
- warning(null, assoc.$path || [ 'definitions', defName, 'elements', assocName ], { name: assocName },
98
- 'Annotation @hierarchy is ignored as the managed association $(NAME) has multiple foreign keys');
107
+ if (isODataTransformation) {
108
+ warning(null, assoc.$path || [ 'definitions', defName, 'elements', assocName ], { name: assocName },
109
+ 'Annotation @hierarchy is ignored as the managed association $(NAME) has multiple foreign keys');
110
+ }
99
111
  return false;
100
112
  }
101
113
 
102
114
  const fkName = assoc.keys[0].ref.join();
103
115
  const defKey = Object.entries(def.elements).find(([ name, elem ]) => elem.key && name === fkName);
104
116
  if (defKey && !isBuiltinType(defKey[1].type)) {
105
- warning(null, assoc.$path || [ 'definitions', defName, 'elements', assocName ], { name: assocName },
106
- 'Annotation @hierarchy is ignored as the foreign key of the managed association $(NAME) is not of a scalar type');
117
+ if (isODataTransformation) {
118
+ warning(null, assoc.$path || [ 'definitions', defName, 'elements', assocName ], { name: assocName },
119
+ 'Annotation @hierarchy is ignored as the foreign key of the managed association $(NAME) is not of a scalar type');
120
+ }
107
121
  return false;
108
122
  }
109
123
  return true;
@@ -143,6 +157,7 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
143
157
  }
144
158
 
145
159
  function addHierarchyFields(def, defName) {
160
+ // add elements in elements dictionary
146
161
  const limitedDescendantCount = createScalarElement('LimitedDescendantCount', 'cds.Integer');
147
162
  limitedDescendantCount.LimitedDescendantCount['@Core.Computed'] = true;
148
163
  limitedDescendantCount.LimitedDescendantCount.$calc = { val: null };
@@ -162,6 +177,14 @@ function generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunction
162
177
  limitedRank.LimitedRank['@Core.Computed'] = true;
163
178
  limitedRank.LimitedRank.$calc = { val: null };
164
179
  addElement(limitedRank, def, defName);
180
+
181
+ // add fields in the query columns
182
+ if (def.query && def.query.SELECT && def.query.SELECT.columns) {
183
+ def.query.SELECT.columns.push({ val: null, as: 'LimitedDescendantCount', cast: { type: 'cds.Integer' } });
184
+ def.query.SELECT.columns.push({ val: null, as: 'DistanceFromRoot', cast: { type: 'cds.Integer' } });
185
+ def.query.SELECT.columns.push({ val: null, as: 'DrillState', cast: { type: 'cds.String' } });
186
+ def.query.SELECT.columns.push({ val: null, as: 'LimitedRank', cast: { type: 'cds.Integer' } });
187
+ }
165
188
  }
166
189
 
167
190
  function checkAndReportErrorWhenAnnotationExists(def, defName, anno) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { isBetaEnabled } = require('../base/model');
3
+ const { isBetaEnabled } = require('../base/specialOptions');
4
4
  const transformUtils = require('./transformUtils');
5
5
  const {
6
6
  forEachDefinition,
@@ -22,15 +22,15 @@ const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./oda
22
22
  const expandToFinalBaseType = require('./odata/toFinalBaseType');
23
23
  const flattening = require('./odata/flattening');
24
24
  const createForeignKeyElements = require('./odata/createForeignKeys');
25
- const generateFioriTreeViewAnnotationsAndFields = require('./odata/fioriTreeViews');
25
+ const generateFioriTreeViewAnnotationsAndFields = require('./fioriTreeViews');
26
26
  const associations = require('./db/associations');
27
27
  const expansion = require('./db/expansion');
28
28
  const generateDrafts = require('./draft/odata');
29
29
 
30
30
  const { addTenantFields } = require('./addTenantFields');
31
31
  const { addLocalizationViews } = require('./localized');
32
- const { cloneFullCsn } = require('../model/cloneCsn');
33
- const { csnRefs } = require('../model/csnRefs');
32
+ const { cloneFullCsn } = require('../base/cloneCsn');
33
+ const { csnRefs } = require('../base/csnRefs');
34
34
  const replaceForeignKeyRefsInExpressionAnnotations = require('./odata/foreignKeyRefsInXprAnnos');
35
35
  const { isAnnotationExpression, xprInAnnoProperties } = require('../base/builtins');
36
36
 
@@ -297,7 +297,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
297
297
 
298
298
  // Generate annotations and fields needed for the Fiori Tree Views out of the @hierarchy annotation
299
299
  if (def['@hierarchy'])
300
- generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers);
300
+ generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers, options);
301
301
 
302
302
  // Annotate artifacts with their DB names if requested.
303
303
  // Skip artifacts that have no DB equivalent anyway