@sap/cds-compiler 6.6.2 → 6.7.1

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 (44) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/bin/cdsc.js +2 -0
  3. package/bin/cdsse.js +1 -1
  4. package/lib/base/message-registry.js +6 -7
  5. package/lib/base/model.js +0 -72
  6. package/lib/checks/elements.js +1 -1
  7. package/lib/checks/featureFlags.js +2 -2
  8. package/lib/compiler/assert-consistency.js +3 -4
  9. package/lib/compiler/base.js +8 -0
  10. package/lib/compiler/builtins.js +8 -9
  11. package/lib/compiler/checks.js +27 -6
  12. package/lib/compiler/cycle-detector.js +4 -4
  13. package/lib/compiler/define.js +65 -83
  14. package/lib/compiler/extend.js +357 -325
  15. package/lib/compiler/finalize-parse-cdl.js +3 -4
  16. package/lib/compiler/generate.js +205 -203
  17. package/lib/compiler/kick-start.js +34 -49
  18. package/lib/compiler/populate.js +95 -28
  19. package/lib/compiler/propagator.js +3 -5
  20. package/lib/compiler/resolve.js +17 -13
  21. package/lib/compiler/shared.js +47 -19
  22. package/lib/compiler/tweak-assocs.js +2 -4
  23. package/lib/compiler/utils.js +84 -31
  24. package/lib/gen/BaseParser.js +924 -1055
  25. package/lib/gen/CdlGrammar.checksum +1 -1
  26. package/lib/gen/CdlParser.js +5 -2
  27. package/lib/json/from-csn.js +25 -16
  28. package/lib/main.d.ts +13 -0
  29. package/lib/model/revealInternalProperties.js +18 -0
  30. package/lib/parsers/AstBuildingParser.js +22 -5
  31. package/lib/render/toHdbcds.js +2 -2
  32. package/lib/render/utils/sql.js +2 -2
  33. package/lib/render/utils/standardDatabaseFunctions.js +2 -2
  34. package/lib/transform/db/constraints.js +3 -4
  35. package/lib/transform/db/killAnnotations.js +1 -1
  36. package/lib/transform/db/processSqlServices.js +10 -11
  37. package/lib/transform/forOdata.js +7 -124
  38. package/lib/transform/odata/fioriTreeViews.js +173 -0
  39. package/lib/transform/odata/flattening.js +2 -2
  40. package/lib/transform/translateAssocsToJoins.js +7 -4
  41. package/package.json +1 -1
  42. package/share/messages/message-explanations.json +0 -2
  43. package/share/messages/type-unexpected-foreign-keys.md +0 -52
  44. package/share/messages/type-unexpected-on-condition.md +0 -52
@@ -1 +1 @@
1
- 2af41f8c84ca1fa40a50b8c58903dadc
1
+ f0320d5264a6107c245a82427b4f6338
@@ -1,6 +1,7 @@
1
- // Parser generated by redepage v0.3.1
1
+ // Parser generated by redepage v0.3.2
2
2
  'use strict;'
3
3
  const { XsnSource, XsnArtifact, XsnName } = require( '../compiler/xsn-model' )
4
+ const { isValidEnumValue } = require( '../compiler/base' )
4
5
  const AstBuildingParser = require('../parsers/AstBuildingParser')
5
6
  const keywords={
6
7
  abstract:0,
@@ -2717,7 +2718,9 @@ case'@':this.annoAssignStd({art},336);continue
2717
2718
  default:this.s=337;continue
2718
2719
  }
2719
2720
  case 337:switch(this.lk()){
2720
- case'Id':case'@':case'key':this.elementDef({outer:$.outer,art},0);continue
2721
+ case'Id':case'@':case'key':if(this.elementDef({outer:$.outer,art},0)){ if (![ 'virtual', 'key', 'masked', '$syntax', 'elements', 'type', 'items' ]
2722
+ .some( p => art[p] ) && isValidEnumValue( art.value ))
2723
+ art.$syntax = 'or-enum'; }continue
2721
2724
  case'extend':this.ckP(338,['Id']);continue
2722
2725
  default:this.ei();continue
2723
2726
  }
@@ -121,6 +121,7 @@ const { isAnnotationExpression } = require('../base/builtins');
121
121
  const { CompilerAssertion } = require('../base/error');
122
122
  const { Location } = require('../base/location');
123
123
  const { XsnSource } = require('../compiler/xsn-model');
124
+ const { isValidEnumValue } = require( '../compiler/base' );
124
125
  const { xsnAsTree, splitClauses } = require('../parsers/XprTree');
125
126
 
126
127
  const $location = Symbol.for('cds.$location');
@@ -734,7 +735,7 @@ const schema = compileSchema( {
734
735
  class: 'expression', // calculated elements
735
736
  vZeroFor: 'val', // CSN v0.1.0 property for `val` in enum def
736
737
  // type: annoValue,
737
- inKind: [ 'element', 'enum' ], // TODO: Remove "enum" again; currently for extensions
738
+ inKind: [ 'element' ],
738
739
  optional: exprProperties.concat([ 'stored' ]),
739
740
  },
740
741
  stored: {
@@ -993,21 +994,15 @@ function definition( def, spec, xsn, csn, name ) {
993
994
  }
994
995
  pushLocation( def );
995
996
  const savedInExtensions = inExtensions;
996
- let kind = calculateKind( def, spec ); // might set inExtensions
997
- const r = (kind === '$column') ? { location: location() } : { location: location(), kind };
997
+ const r = { location: location(), kind: undefined };
998
+ const kind = calculateKind( def, spec, r ); // might set inExtensions
999
+ if (kind !== '$column')
1000
+ r.kind ??= kind;
998
1001
  const xor = {};
999
1002
  const { prop } = spec;
1000
1003
  const kind0 = (spec.validKinds.length || spec.prop === 'extensions') && kind;
1001
1004
  const csnProps = Object.keys( def );
1002
1005
 
1003
- // For compatibility, extension property `elements` could actually be an `enum`:
1004
- if (savedInExtensions === '' && prop === 'elements' && // in extend property `elements`
1005
- !Object.keys( def ).some( couldNotBeEnumProperty )) {
1006
- r.$syntax = 'enum'; // could be an enum
1007
- if (def.val !== undefined || def['#'] !== undefined)
1008
- kind = 'enum'; // for function expected(), i.e. allow property `val`/`#`
1009
- }
1010
-
1011
1006
  if (csnProps.length) {
1012
1007
  const valueName = (prop === 'keys' || prop === 'foreignKeys' ? 'targetElement' : 'value');
1013
1008
  // the next is basically object() + the inValue handling
@@ -1057,17 +1052,19 @@ function namespace( ref, spec ) {
1057
1052
  return ns ? { kind: 'namespace', name: ns } : null;
1058
1053
  }
1059
1054
 
1060
- function couldNotBeEnumProperty( prop ) {
1061
- // returns true for `value` (which we allow with warning when extending an enum with `elements`)
1055
+ function couldBeEnumProperty( prop ) {
1062
1056
  const inKind = schema[prop]?.inKind; // undefined for annotations, $location, …
1063
1057
  // inKind for annotation assignments is function -> can be for enum
1064
- return Array.isArray( inKind ) && inKind.includes( 'element' );
1058
+ return inKind && // `value` handled extra
1059
+ (!Array.isArray( inKind ) || prop === 'value' || inKind.includes( 'enum' ));
1065
1060
  }
1066
1061
 
1067
1062
  function actions( def, spec, xsn, csn, name ) {
1068
1063
  if (def.kind === 'extend' && (def.elements || def.enum)) {
1069
1064
  // TODO: Handle this case in extend.js; already done for `returns`
1070
1065
  // See message ext-expecting-returns
1066
+ // TODO: the location would also bad here
1067
+ // (should be at the property, not containing object - but we remove this anyway)
1071
1068
  error( 'syntax-unexpected-property', location(true), {
1072
1069
  '#': def.kind,
1073
1070
  prop: def.enum ? 'enum' : 'elements',
@@ -1873,7 +1870,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1873
1870
  return { type: ignore };
1874
1871
  }
1875
1872
 
1876
- function calculateKind( def, spec ) {
1873
+ function calculateKind( def, spec, xsn ) {
1877
1874
  if (inExtensions) {
1878
1875
  inExtensions = spec.defaultKind;
1879
1876
  return 'annotate';
@@ -1883,9 +1880,21 @@ function calculateKind( def, spec ) {
1883
1880
  return (def.extend) ? 'extend' : 'annotate';
1884
1881
  }
1885
1882
  const kind = (def.kind === 'view') ? 'entity' : def.kind; // 'view' is CSN v0.1.0
1886
- if (kind === 'extend' && inExtensions === '') // valid extend -> keep inExtensions
1883
+ const inExtend = inExtensions === '';
1884
+ if (inExtend && kind === 'extend') // extend in extend -> keep inExtensions
1887
1885
  return 'extend';
1888
1886
  inExtensions = null;
1887
+ // Compatibilitity: object rendered in `elements` of extend might be enum:
1888
+ if (inExtend && !def.kind && spec.defaultKind === 'element' &&
1889
+ Object.keys( def ).every( couldBeEnumProperty )) {
1890
+ if (isValidEnumValue( def.value ))
1891
+ xsn.$syntax = 'or-enum'; // could be an enum
1892
+ xsn.kind = 'element';
1893
+ // We also allow a `val` which is not embedded in a `value` also on an element
1894
+ // def inside extensions, because that is how compiler v3 has written it:
1895
+ if (def.val && !def.value)
1896
+ return 'enum'; // use schema of enum (for `val`)
1897
+ }
1889
1898
  if (!spec.validKinds.includes( kind ))
1890
1899
  return spec.defaultKind;
1891
1900
  return (def.abstract || def.$syntax === 'aspect')
package/lib/main.d.ts CHANGED
@@ -313,6 +313,19 @@ declare namespace compiler {
313
313
  * @private
314
314
  */
315
315
  edm4OpenAPI?: boolean
316
+ /**
317
+ * Enable the draft-generated artifacts to handle state messages.
318
+ *
319
+ * @since v6.1.0
320
+ */
321
+ draftMessages?: boolean
322
+ /**
323
+ * Add `CreatedByUserDescription`, `LastChangedByUserDescription` and
324
+ * `InProcessByUserDescription` fields to the `DraftAdministrativeData` entity.
325
+ *
326
+ * @since v6.6.0
327
+ */
328
+ draftUserDescription?: boolean
316
329
  }
317
330
 
318
331
  /**
@@ -8,6 +8,8 @@
8
8
  // This function should return a meaningful result in all circumstances:
9
9
  // * with --parse-only, with both CDL and CSN input,
10
10
  // * for the core compiler output and all transformations working on the XSN.
11
+ //
12
+ // TODO: make sure that all extend statements are listed)
11
13
 
12
14
  'use strict';
13
15
 
@@ -95,6 +97,7 @@ function revealInternalProperties( model, nameOrPath ) {
95
97
  _layerExtends: layerExtends,
96
98
  _origin: origin,
97
99
  $compositionTargets: d => d, // dictionary( boolean )
100
+ _extensions: revealExtensions,
98
101
  _extend: reveal,
99
102
  _annotate: reveal,
100
103
  _annotateS: artifactIdentifier,
@@ -270,6 +273,21 @@ function revealInternalProperties( model, nameOrPath ) {
270
273
  return artifactDictionary( node, parent );
271
274
  }
272
275
 
276
+ function revealExtensions( node ) {
277
+ if (Array.isArray( node ))
278
+ return array( node, revealSingleExtension );
279
+ const r = {};
280
+ for (const prop in node)
281
+ r[prop] = revealExtensions( node[prop] );
282
+ return r;
283
+ }
284
+
285
+ function revealSingleExtension( node, parent ) {
286
+ return (node.kind && node.__unique_id__ == null && node.$effectiveSeqNo == null && !node.builtin)
287
+ ? reveal( node, parent )
288
+ : artifactIdentifier( node, parent );
289
+ }
290
+
273
291
  function reveal( node, parent, name ) {
274
292
  if (node == null || typeof node !== 'object' )
275
293
  return node;
@@ -39,6 +39,17 @@ const valueTokens = {
39
39
  true: true,
40
40
  };
41
41
  const valueTokensLength = Object.values( valueTokens ).length;
42
+ // likewise, if expectedArray contains all the following tokens, replace them by 'Literal'
43
+ const literalTokens = {
44
+ Number: true,
45
+ QuotedLiteral: true,
46
+ String: true,
47
+ '#': true,
48
+ true: true,
49
+ false: true,
50
+ null: true,
51
+ };
52
+ const literalTokensLength = Object.values( literalTokens ).length;
42
53
 
43
54
  const extensionDicts = {
44
55
  elements: true, enum: true, params: true, returns: true,
@@ -107,9 +118,15 @@ class AstBuildingParser extends BaseParser {
107
118
  let array = this.expectingArray_();
108
119
  this.s = savedState;
109
120
  // Avoid clutter in messages: replace all value tokens by ‹Value› if all are there:
110
- const reduced = raw ? array : array.filter( tok => valueTokens[tok] !== true );
111
- if (reduced.length + valueTokensLength === array.length)
112
- array = [ 'Value', ...reduced ];
121
+ const withoutValueTokens = raw ? array : array.filter( tok => valueTokens[tok] !== true );
122
+ if (withoutValueTokens.length + valueTokensLength === array.length) {
123
+ array = [ 'Value', ...withoutValueTokens ];
124
+ }
125
+ else {
126
+ const withoutLiteralTokens = raw ? array : array.filter( tok => literalTokens[tok] !== true );
127
+ if (withoutLiteralTokens.length + literalTokensLength === array.length)
128
+ array = [ 'Literal', ...withoutLiteralTokens ];
129
+ }
113
130
  return array.map( tok => this.antlrName( tok ) )
114
131
  .sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
115
132
  }
@@ -153,7 +170,7 @@ class AstBuildingParser extends BaseParser {
153
170
  this.conditionStackLength == null) // after error recovery
154
171
  return false;
155
172
  if (this.constructor.tracingParser)
156
- this._traceSubPush( 'queryOnLeft' );
173
+ this._traceSubPush?.( 'queryOnLeft' );
157
174
  return this.queryOnLeft( 'table', mode );
158
175
  }
159
176
 
@@ -175,7 +192,7 @@ class AstBuildingParser extends BaseParser {
175
192
  if (this.inSameRule_( this.s, this.stack.at( -1 ).followState ))
176
193
  return false;
177
194
  this.dynamic_.parenthesesCtx = [ null ];
178
- this._tracePush( 'Parentheses()' );
195
+ this._tracePush?.( 'Parentheses()' );
179
196
  }
180
197
  const { parenthesesCtx } = this.dynamic_;
181
198
  const noQuery = parenthesesCtx?.[0];
@@ -4,7 +4,7 @@ const {
4
4
  getLastPartOf, getLastPartOfRef,
5
5
  hasValidSkipOrExists, getNormalizedQuery,
6
6
  getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement,
7
- pathName, hasPersistenceSkipAnnotation, implicitAs,
7
+ pathName, hasPersistenceSkipAnnotation, implicitAs, forEachDefinition,
8
8
  } = require('../model/csnUtils');
9
9
  const { isBuiltinType, isMagicVariable } = require('../base/builtins');
10
10
  const keywords = require('../base/keywords');
@@ -18,7 +18,7 @@ const {
18
18
  renderReferentialConstraint,
19
19
  } = require('./utils/sql');
20
20
  const DuplicateChecker = require('./DuplicateChecker');
21
- const { forEachDefinition, isDeprecatedEnabled } = require('../base/model');
21
+ const { isDeprecatedEnabled } = require('../base/model');
22
22
  const { checkCSNVersion } = require('../json/csnVersion');
23
23
  const { timetrace } = require('../utils/timetrace');
24
24
 
@@ -5,7 +5,7 @@
5
5
  const { getResultingName } = require('../../model/csnUtils');
6
6
  const { smartId, delimitedId } = require('../../sql-identifier');
7
7
  const { ModelError } = require('../../base/error');
8
- const { isBetaEnabled, setProp } = require('../../base/model');
8
+ const { setProp } = require('../../base/model');
9
9
 
10
10
  /**
11
11
  * Render a given referential constraint as part of a SQL CREATE TABLE statement, or as .hdbconstraint artefact.
@@ -161,7 +161,7 @@ const allowedHdbProjectionViewProperties = {
161
161
  * @returns {boolean} `true` if the artifact is a projection view, otherwise `false`.
162
162
  */
163
163
  function isProjectionView( csn, artifact, options ) {
164
- if (!isBetaEnabled(options, 'projectionViews') || !artifact.projection || options.sqlDialect !== 'hana' || artifact.params || !artifact.$dataProductService)
164
+ if (!artifact.projection || options.sqlDialect !== 'hana' || artifact.params || !artifact.$dataProductService)
165
165
  return false;
166
166
 
167
167
  if (artifact.$isProjectionView !== undefined)
@@ -666,7 +666,7 @@ const hanaFunctions = {
666
666
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
667
667
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
668
668
  // make sure to cast NUMERIC to BIGINT (corresponds to cds.Int64)
669
- return `(EXTRACT(EPOCH FROM (${ y }) - (${ x })) * 10000000)::BIGINT`;
669
+ return `(EXTRACT(EPOCH FROM (${ y })::TIMESTAMP - (${ x })::TIMESTAMP) * 10000000)::BIGINT`;
670
670
  },
671
671
  seconds_between(signature) {
672
672
  const { args } = signature;
@@ -674,7 +674,7 @@ const hanaFunctions = {
674
674
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
675
675
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
676
676
 
677
- return `EXTRACT(EPOCH FROM (${ y }) - (${ x }))::BIGINT`;
677
+ return `EXTRACT(EPOCH FROM (${ y })::TIMESTAMP - (${ x })::TIMESTAMP)::BIGINT`;
678
678
  },
679
679
  days_between(signature) {
680
680
  const { args } = signature;
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { forEachDefinition } = require('../../base/model');
4
3
  const { applyTransformations, getResultingName, hasPersistenceSkipAnnotation } = require('../../model/csnUtils');
5
4
  const { forEach, forEachKey } = require('../../utils/objectUtils');
6
5
  const { CompilerAssertion } = require('../../base/error');
@@ -92,7 +91,7 @@ function createReferentialConstraints( csn, options ) {
92
91
  associations.forEach(association => association.fn());
93
92
 
94
93
  // Step III: Create the final referential constraints from all dependent key <-> parent key pairs stemming from the same $sourceAssociation
95
- forEachDefinition(csn, collectAndAttachReferentialConstraints);
94
+ forEach(csn.definitions, collectAndAttachReferentialConstraints);
96
95
 
97
96
  /**
98
97
  * Retrieve the <up_> link of an `cds.Composition` used in an on-condition like `$self = <comp>.<up_>`
@@ -501,10 +500,10 @@ function createReferentialConstraints( csn, options ) {
501
500
  * - Find all other elements in artifact with the same sourceAssociation
502
501
  * - Create constraints with the information supplied by $parentKey, $parentTable and $onDelete
503
502
  *
504
- * @param {CSN.Artifact} artifact
505
503
  * @param {string} artifactName
504
+ * @param {CSN.Artifact} artifact
506
505
  */
507
- function collectAndAttachReferentialConstraints( artifact, artifactName ) {
506
+ function collectAndAttachReferentialConstraints( artifactName, artifact ) {
508
507
  const referentialConstraints = Object.create(null);
509
508
 
510
509
  // tenant foreign keys may have multiple parent keys
@@ -26,7 +26,7 @@ const requiredAnnos = {
26
26
  '@Core.Computed': true,
27
27
  [sqlServiceAnnotation]: true,
28
28
  '@cds.external': true, // for external ABAP SQL services and data products for now
29
- '@DataIntegration.dataproduct.type': true, // for data product production
29
+ '@data.product': true, // for data product production
30
30
  };
31
31
 
32
32
  /**
@@ -18,13 +18,13 @@ function processSqlServices(csn, options) {
18
18
  setProp(csn, '$dummyServiceEntities', Object.create(null));
19
19
  setProp(csn, '$dataProductEntities', Object.create(null));
20
20
  return function findAndMarkSqlServiceArtifacts(artifact, artifactName) {
21
- const { sqlServiceName, dummyServiceName, dataProductServiceName } = isEntityInSqlService(artifact, artifactName, csn, options);
21
+ const { sqlServiceName, dummyServiceName, dataProductProductionServiceName } = isEntityInSqlService(artifact, artifactName, csn, options);
22
22
  if (sqlServiceName?.length > 0)
23
23
  setProp(artifact, '$sqlService', sqlServiceName);
24
24
  if (dummyServiceName?.length > 0)
25
25
  setProp(artifact, '$dummyService', dummyServiceName);
26
- if (dataProductServiceName?.length > 0)
27
- setProp(artifact, '$dataProductService', dataProductServiceName);
26
+ if (dataProductProductionServiceName?.length > 0)
27
+ setProp(artifact, '$dataProductService', dataProductProductionServiceName);
28
28
  };
29
29
  }
30
30
 
@@ -58,7 +58,7 @@ function isDummyService(artifact, options) {
58
58
  * @returns {object} An object containing the names of the SQL service and external ABAP SQL service, if found.
59
59
  */
60
60
  function isEntityInSqlService(artifact, artifactName, csn, options) {
61
- const result = { sqlServiceName: undefined, dummyServiceName: undefined, dataProductServiceName: undefined };
61
+ const result = { sqlServiceName: undefined, dummyServiceName: undefined, dataProductProductionServiceName: undefined };
62
62
  if (artifact.kind !== 'entity' || !artifactName.includes('.') || hasPersistenceSkipAnnotation(artifact))
63
63
  return result;
64
64
 
@@ -75,8 +75,8 @@ function isEntityInSqlService(artifact, artifactName, csn, options) {
75
75
  if (isDummyService(definition, options))
76
76
  result.dummyServiceName = possibleServiceName;
77
77
 
78
- if (isDataProductService(definition, options))
79
- result.dataProductServiceName = possibleServiceName;
78
+ if (isDataProductProductionService(definition))
79
+ result.dataProductProductionServiceName = possibleServiceName;
80
80
 
81
81
  // We don't allow nested services/contexts - if we find one, we don't need to keep searching
82
82
  if (definition.kind === 'service' || definition.kind === 'context')
@@ -115,21 +115,20 @@ function createServiceDummy(artifact, artifactName, csn, { error }) {
115
115
  }
116
116
 
117
117
  /**
118
- * Determines if the given artifact is a data product service.
118
+ * Determines if the given artifact is a data product production service.
119
119
  *
120
120
  * @param {object} artifact - The artifact to evaluate.
121
- * @param {object} options - The options object containing feature flags.
122
121
  * @returns {boolean} - Returns `true` if the artifact is a data product service, otherwise `false`.
123
122
  */
124
- function isDataProductService(artifact, options) {
125
- return isBetaEnabled(options, 'projectionViews') && artifact.kind === 'service' && artifact['@DataIntegration.dataproduct.type'] === 'primary';
123
+ function isDataProductProductionService(artifact) {
124
+ return artifact.kind === 'service' && artifact['@data.product'] && !artifact['@cds.external'];
126
125
  }
127
126
 
128
127
  module.exports = {
129
128
  processSqlServices,
130
129
  isSqlService,
131
130
  isDummyService,
132
- isDataProductService,
131
+ isDataProductProductionService,
133
132
  sqlServiceAnnotation,
134
133
  createServiceDummy,
135
134
  };
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const { isBetaEnabled } = require('../base/model');
4
- const { isBuiltinType } = require('../base/builtins');
5
4
  const transformUtils = require('./transformUtils');
6
5
  const {
7
6
  forEachDefinition,
@@ -16,12 +15,14 @@ const {
16
15
  } = require('../model/csnUtils');
17
16
  const { checkCSNVersion } = require('../json/csnVersion');
18
17
  const validate = require('../checks/validator');
19
- const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./odata/utils');
20
- const expandToFinalBaseType = require('./odata/toFinalBaseType');
21
18
  const { timetrace } = require('../utils/timetrace');
22
19
  const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
20
+
21
+ const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./odata/utils');
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
26
  const associations = require('./db/associations');
26
27
  const expansion = require('./db/expansion');
27
28
  const generateDrafts = require('./draft/odata');
@@ -92,8 +93,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
92
93
 
93
94
  const transformers = transformUtils.getTransformers(csn, options, messageFunctions, '_');
94
95
  const {
95
- addDefaultTypeFacets, addElement,
96
- checkMultipleAssignments, createScalarElement,
96
+ addDefaultTypeFacets, checkMultipleAssignments,
97
97
  recurseElements, setAnnotation,
98
98
  renameAnnotation, expandStructsInExpression,
99
99
  csnUtils,
@@ -296,7 +296,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
296
296
  renameShorthandAnnotations(def);
297
297
 
298
298
  // Generate annotations and fields needed for the Fiori Tree Views out of the @hierarchy annotation
299
- generateFioriTreeViewAnnotationsAndFields(def, defName);
299
+ if (def['@hierarchy'])
300
+ generateFioriTreeViewAnnotationsAndFields(def, defName, messageFunctions, csnUtils, transformers);
300
301
 
301
302
  // Annotate artifacts with their DB names if requested.
302
303
  // Skip artifacts that have no DB equivalent anyway
@@ -365,124 +366,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
365
366
  //--------------------------------------------------------------------
366
367
  // HELPER SECTION STARTS HERE
367
368
 
368
- // =====================================================================
369
- // Generate annotations and fields needed for the Fiori Tree Views out of the @hierarchy annotation
370
- function generateFioriTreeViewAnnotationsAndFields(def, defName) {
371
- const re = new RegExp('^@hierarchy#.+$');
372
- if (Object.keys(def).some(key => re.test(key)))
373
- error(null, [ 'definitions', defName ], {}, 'Assigning qualifier with the @hierarchy annotation is not supported');
374
-
375
- if (!def['@hierarchy'])
376
- return;
377
- if (!(def.projection || def.query))
378
- return;
379
-
380
- const defKeys = [];
381
- const assocs = [];
382
- Object.entries(def.elements).forEach(([ name, elem ]) => {
383
- if (elem.key)
384
- defKeys.push([ name, elem ]);
385
- if (isAssociation(elem) && elem.target === defName)
386
- assocs.push([ name, elem ]);
387
- });
388
-
389
- // the definition must have exactly one scalar key field
390
- if (defKeys.length !== 1 && !isBuiltinType(defKeys[0].type))
391
- return;
392
- const defKeyName = defKeys[0][0];
393
-
394
- // if the @hierarchy annotation has a true value, we need to make sure the definition has exacly one
395
- // association pointing to self and has the single key field as a foreign key
396
- if (typeof def['@hierarchy'] === 'boolean' && def['@hierarchy'] === true) {
397
- if (assocs.length !== 1)
398
- return;
399
- const assocName = assocs[0][0];
400
- const assoc = assocs[0][1];
401
- if (!isValidHierarchyAssociation(assoc, defKeyName))
402
- return;
403
- addHierarchyAnnotations(def, defName, defKeyName, assocName);
404
- addHierarchyFields(def, defName);
405
- }
406
- else if (typeof def['@hierarchy'] === 'object' && def['@hierarchy']['=']) {
407
- const assocName = def['@hierarchy']['='];
408
- if (!isValidHierarchyAssociation(assocs.find(a => a[0] === assocName)[1], defKeyName))
409
- return;
410
- addHierarchyAnnotations(def, defName, defKeyName, assocName);
411
- addHierarchyFields(def, defName);
412
- }
413
- }
414
-
415
- // returns true if assoc has one key and that one key is the corresponding 'keyName'
416
- function isValidHierarchyAssociation(assoc, keyName) {
417
- return assoc.keys.length === 1 && assoc.keys[0].ref.join() === keyName;
418
- }
419
-
420
- function addHierarchyAnnotations(def, defName, defKeyName, assocName) {
421
- const qualifier = `${ defName.split('.').pop() }Hierarchy`;
422
-
423
- [ `@Aggregation.RecursiveHierarchy#${ qualifier }`,
424
- `@Aggregation.RecursiveHierarchy#${ qualifier }.NodeProperty`,
425
- `@Aggregation.RecursiveHierarchy#${ qualifier }.ParentNavigationProperty`,
426
- `@Hierarchy.RecursiveHierarchy#${ qualifier }`,
427
- `@Hierarchy.RecursiveHierarchy#${ qualifier }.LimitedDescendantCount`,
428
- `@Hierarchy.RecursiveHierarchy#${ qualifier }.DistanceFromRoot`,
429
- `@Hierarchy.RecursiveHierarchy#${ qualifier }.DrillState`,
430
- `@Hierarchy.RecursiveHierarchy#${ qualifier }.LimitedRank` ].forEach(anno => checkAndReportErrorWhenAnnotationExists(def, defName, anno));
431
-
432
- setAnnotation(def, `@Aggregation.RecursiveHierarchy#${ qualifier }.NodeProperty`, { '=': defKeyName });
433
- setAnnotation(def, `@Aggregation.RecursiveHierarchy#${ qualifier }.ParentNavigationProperty`, { '=': assocName });
434
- setAnnotation(def, `@Hierarchy.RecursiveHierarchy#${ qualifier }.LimitedDescendantCount`, { '=': 'LimitedDescendantCount' });
435
- setAnnotation(def, `@Hierarchy.RecursiveHierarchy#${ qualifier }.DistanceFromRoot`, { '=': 'DistanceFromRoot' });
436
- setAnnotation(def, `@Hierarchy.RecursiveHierarchy#${ qualifier }.DrillState`, { '=': 'DrillState' });
437
- setAnnotation(def, `@Hierarchy.RecursiveHierarchy#${ qualifier }.LimitedRank`, { '=': 'LimitedRank' });
438
-
439
- // if @Capabilities.FilterRestrictions.NonFilterableProperties or @Capabilities.SortRestrictions.NonSortableProperties
440
- // are already defined, we append to the existing value the created elements
441
- if (def['@Capabilities.FilterRestrictions.NonFilterableProperties'] && Array.isArray(def['@Capabilities.FilterRestrictions.NonFilterableProperties'])) {
442
- def['@Capabilities.FilterRestrictions.NonFilterableProperties'].push('LimitedDescendantCount', 'DistanceFromRoot', 'DrillState', 'LimitedRank');
443
- }
444
- else {
445
- setAnnotation(def, '@Capabilities.FilterRestrictions.NonFilterableProperties',
446
- [ 'LimitedDescendantCount', 'DistanceFromRoot', 'DrillState', 'LimitedRank' ]);
447
- }
448
-
449
- if (def['@Capabilities.SortRestrictions.NonSortableProperties'] && Array.isArray(def['@Capabilities.SortRestrictions.NonSortableProperties'])) {
450
- def['@Capabilities.FilterRestrictions.NonFilterableProperties'].push('LimitedDescendantCount', 'DistanceFromRoot', 'DrillState', 'LimitedRank');
451
- }
452
- else {
453
- setAnnotation(def, '@Capabilities.SortRestrictions.NonSortableProperties',
454
- [ 'LimitedDescendantCount', 'DistanceFromRoot', 'DrillState', 'LimitedRank' ]);
455
- }
456
- }
457
-
458
- function addHierarchyFields(def, defName) {
459
- const limitedDescendantCount = createScalarElement('LimitedDescendantCount', 'cds.Integer');
460
- limitedDescendantCount.LimitedDescendantCount['@Core.Computed'] = true;
461
- limitedDescendantCount.LimitedDescendantCount.$calc = { val: null };
462
- addElement(limitedDescendantCount, def, defName);
463
-
464
- const distanceFromRoot = createScalarElement('DistanceFromRoot', 'cds.Integer');
465
- distanceFromRoot.DistanceFromRoot['@Core.Computed'] = true;
466
- distanceFromRoot.DistanceFromRoot.$calc = { val: null };
467
- addElement(distanceFromRoot, def, defName);
468
-
469
- const drillState = createScalarElement('DrillState', 'cds.String');
470
- drillState.DrillState['@Core.Computed'] = true;
471
- drillState.DrillState.$calc = { val: null };
472
- addElement(drillState, def, defName);
473
-
474
- const limitedRank = createScalarElement('LimitedRank', 'cds.Integer');
475
- limitedRank.LimitedRank['@Core.Computed'] = true;
476
- limitedRank.LimitedRank.$calc = { val: null };
477
- addElement(limitedRank, def, defName);
478
- }
479
-
480
- function checkAndReportErrorWhenAnnotationExists(def, defName, anno) {
481
- if (def[anno])
482
- error(null, [ 'definitions', defName ], { anno, name: defName }, 'Annotation $(ANNO) is already defined on the hierarchy entity $(NAME)');
483
- }
484
- // =====================================================================
485
-
486
369
  // Transform @readonly/@mandatory/@disabled into @Common.FieldControl annotation
487
370
  // with a when/then/else expression consisting of the input from the annotations.
488
371
  function processDynamicFieldControlAnnotations(node) {