@sap/cds-compiler 6.7.3 → 6.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 (116) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +4 -0
  3. package/bin/cdsc.js +5 -5
  4. package/bin/cdshi.js +1 -0
  5. package/bin/cdsse.js +1 -1
  6. package/lib/api/main.js +17 -9
  7. package/lib/api/options.js +5 -2
  8. package/lib/api/validate.js +1 -1
  9. package/lib/base/builtins.js +13 -9
  10. package/lib/{model → base}/csnRefs.js +8 -10
  11. package/lib/base/error.js +2 -0
  12. package/lib/base/message-registry.js +68 -4
  13. package/lib/base/messages.js +4 -2
  14. package/lib/{optionProcessor.js → base/optionProcessor.js} +5 -3
  15. package/lib/base/{model.js → specialOptions.js} +16 -39
  16. package/lib/checks/arrayOfs.js +1 -1
  17. package/lib/checks/elements.js +1 -1
  18. package/lib/checks/enricher.js +2 -2
  19. package/lib/checks/featureFlags.js +54 -24
  20. package/lib/checks/foreignKeys.js +1 -1
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedInType.js +1 -1
  23. package/lib/checks/onConditions.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +1 -1
  25. package/lib/checks/validator.js +10 -14
  26. package/lib/compiler/assert-consistency.js +11 -9
  27. package/lib/compiler/base.js +5 -1
  28. package/lib/compiler/builtins.js +1 -1
  29. package/lib/compiler/checks.js +3 -3
  30. package/lib/compiler/define.js +6 -3
  31. package/lib/{base → compiler}/dictionaries.js +4 -3
  32. package/lib/compiler/extend.js +121 -21
  33. package/lib/compiler/generate.js +2 -2
  34. package/lib/compiler/index.js +11 -3
  35. package/lib/compiler/kick-start.js +1 -1
  36. package/lib/compiler/lsp-api.js +3 -3
  37. package/lib/compiler/populate.js +6 -7
  38. package/lib/compiler/resolve.js +53 -36
  39. package/lib/compiler/shared.js +68 -18
  40. package/lib/compiler/tweak-assocs.js +2 -2
  41. package/lib/compiler/utils.js +28 -27
  42. package/lib/compiler/xpr-rewrite.js +3 -3
  43. package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
  44. package/lib/edm/annotations/edmJson.js +2 -4
  45. package/lib/edm/annotations/genericTranslation.js +51 -7
  46. package/lib/edm/csn2edm.js +3 -2
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -1
  48. package/lib/edm/edmInboundChecks.js +2 -1
  49. package/lib/edm/edmPreprocessor.js +3 -3
  50. package/lib/edm/edmUtils.js +2 -2
  51. package/lib/gen/BaseParser.js +59 -108
  52. package/lib/gen/CdlGrammar.checksum +1 -1
  53. package/lib/gen/CdlParser.js +2052 -1965
  54. package/lib/gen/Dictionary.json +67 -7
  55. package/lib/json/from-csn.js +14 -14
  56. package/lib/json/to-csn.js +77 -38
  57. package/lib/main.js +3 -3
  58. package/lib/model/csnUtils.js +2 -2
  59. package/lib/modelCompare/compare.js +1 -1
  60. package/lib/modelCompare/utils/filter.js +1 -0
  61. package/lib/parsers/AstBuildingParser.js +83 -33
  62. package/lib/parsers/index.js +1 -1
  63. package/lib/render/manageConstraints.js +1 -1
  64. package/lib/render/toCdl.js +49 -30
  65. package/lib/render/toHdbcds.js +2 -2
  66. package/lib/render/toSql.js +16 -7
  67. package/lib/render/utils/common.js +11 -3
  68. package/lib/render/utils/sql.js +14 -5
  69. package/lib/render/utils/standardDatabaseFunctions.js +108 -99
  70. package/lib/sql-identifier.js +9 -1
  71. package/lib/{model → tool-lib}/enrichCsn.js +3 -2
  72. package/lib/{model → tool-lib}/revealInternalProperties.js +2 -1
  73. package/lib/transform/addTenantFields.js +1 -1
  74. package/lib/transform/db/applyTransformations.js +1 -1
  75. package/lib/transform/db/assertUnique.js +1 -1
  76. package/lib/transform/db/assocsToQueries/transformExists.js +1 -1
  77. package/lib/transform/db/backlinks.js +2 -2
  78. package/lib/transform/db/expansion.js +2 -2
  79. package/lib/transform/db/flattening.js +3 -4
  80. package/lib/transform/db/killAnnotations.js +1 -0
  81. package/lib/transform/db/processSqlServices.js +2 -1
  82. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  83. package/lib/transform/db/temporal.js +30 -5
  84. package/lib/transform/db/views.js +16 -20
  85. package/lib/transform/draft/db.js +1 -2
  86. package/lib/transform/effective/associations.js +1 -1
  87. package/lib/transform/effective/flattening.js +6 -5
  88. package/lib/transform/effective/main.js +24 -4
  89. package/lib/transform/effective/types.js +1 -1
  90. package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
  91. package/lib/transform/forOdata.js +25 -7
  92. package/lib/transform/forRelationalDB.js +48 -12
  93. package/lib/transform/localized.js +2 -2
  94. package/lib/transform/odata/createForeignKeys.js +1 -1
  95. package/lib/transform/odata/flattening.js +2 -2
  96. package/lib/transform/odata/toFinalBaseType.js +3 -2
  97. package/lib/transform/odata/typesExposure.js +3 -2
  98. package/lib/transform/transformUtils.js +2 -2
  99. package/lib/transform/translateAssocsToJoins.js +2 -1
  100. package/lib/transform/tupleExpansion.js +44 -4
  101. package/lib/transform/universalCsn/universalCsnEnricher.js +7 -3
  102. package/lib/transform/universalCsn/utils.js +1 -1
  103. package/lib/{base → utils}/lazyload.js +9 -0
  104. package/lib/{base → utils}/node-helpers.js +2 -0
  105. package/lib/utils/objectUtils.js +29 -6
  106. package/lib/{base → utils}/optionProcessorHelper.js +16 -6
  107. package/package.json +3 -40
  108. /package/lib/{model → base}/cloneCsn.js +0 -0
  109. /package/lib/{model/api.js → base/model-api.js} +0 -0
  110. /package/lib/{api → base}/trace.js +0 -0
  111. /package/lib/{model → base}/xprAsTree.js +0 -0
  112. /package/lib/{inspect → tool-lib}/index.js +0 -0
  113. /package/lib/{inspect → tool-lib}/inspectModelStatistics.js +0 -0
  114. /package/lib/{inspect → tool-lib}/inspectPropagation.js +0 -0
  115. /package/lib/{inspect → tool-lib}/inspectUtils.js +0 -0
  116. /package/lib/{base → utils}/shuffle.js +0 -0
@@ -121,7 +121,7 @@ function fns( model ) {
121
121
  lexical: userBlock,
122
122
  dynamic: modelBuiltinsOrDefinitions,
123
123
  navigation: environment,
124
- notFound: undefinedDefinition,
124
+ notFound: undefinedFromReference,
125
125
  accept: acceptQuerySource,
126
126
  noDep: '', // dependency special for from
127
127
  args: () => 'from-args',
@@ -131,7 +131,7 @@ function fns( model ) {
131
131
  lexical: userBlock,
132
132
  dynamic: modelBuiltinsOrDefinitions,
133
133
  navigation: staticTarget,
134
- notFound: undefinedDefinition,
134
+ notFound: undefinedTypeReference,
135
135
  accept: acceptTypeOrElement,
136
136
  // special `scope`s for CDL parser - TYPE OF (TODO generated?), cds.Association:
137
137
  typeOf: typeOfSemantics,
@@ -629,9 +629,11 @@ function fns( model ) {
629
629
 
630
630
  if (Array.isArray( art ))
631
631
  art = art[0];
632
- if (!art)
633
- return (semantics.dynamic !== modelDefinitions) ? art : pathName( path );
634
-
632
+ if (!art) {
633
+ return (semantics.dynamic !== modelDefinitions && !options.$cdsReplSupport)
634
+ ? art
635
+ : pathName( path );
636
+ }
635
637
  const first = (art.kind === 'using' ? art.extern : art.name).id;
636
638
  return (path.length === 1) ? first : `${ first }.${ pathName( ref.path.slice(1) ) }`;
637
639
  }
@@ -788,6 +790,8 @@ function fns( model ) {
788
790
  return undefined; // parse error
789
791
  if (head._artifact !== undefined)
790
792
  return head._artifact;
793
+ if (user.$extended && user._outer && !semantics.isMainRef)
794
+ user = user._outer;
791
795
  let ruser = user._user || user; // TODO: nicer name if we keep this
792
796
  // TODO: re-think _user link
793
797
  if (ruser._outer && !semantics.isMainRef) {
@@ -1033,13 +1037,23 @@ function fns( model ) {
1033
1037
  return null;
1034
1038
  }
1035
1039
  case 'mixin': {
1036
- // use a source element having that name if in `extend … with columns`:
1037
- const elem = (user._user || user).$extended &&
1038
- art._parent._combined[head.id];
1040
+ // use a source element having that name if in `extend … with` (columns or groupBy):
1041
+ const $extended = user._user?.$extended ?? user.$extended;
1042
+ const elem = $extended && art._parent._combined[head.id];
1039
1043
  if (elem) {
1040
1044
  path.$prefix = elem._parent.name.id; // prepend alias name
1041
- info( 'ref-special-in-extend', [ head.location, user ],
1042
- { '#': 'mixin', id: head.id, art: elem._origin._main } );
1045
+ if ($extended === 'columns') {
1046
+ warning( 'ref-special-in-extend', [ head.location, user ],
1047
+ { '#': 'mixin', id: head.id, art: elem._origin._main } );
1048
+ }
1049
+ else {
1050
+ error( 'ref-unexpected-in-extend', [ head.location, user ], {
1051
+ '#': 'mixin',
1052
+ keyword: $extended,
1053
+ id: head.id,
1054
+ art: elem._origin._main,
1055
+ } );
1056
+ }
1043
1057
  setLink( head, '_navigation', elem );
1044
1058
  return setArtifactLink( head, elem._origin );
1045
1059
  }
@@ -1050,21 +1064,30 @@ function fns( model ) {
1050
1064
  return setArtifactLink( head, art._origin );
1051
1065
  }
1052
1066
  case '$tableAlias': {
1053
- // use a source element having that name if in `extend … with columns`:
1054
- const { $extended } = user._user || user;
1067
+ // use a source element having that name if in `extend … with` (columns or groupBy):
1068
+ const $extended = user._user?.$extended ?? user.$extended;
1055
1069
  // if query source has duplicates, table alias has no elements
1056
1070
  const elem = $extended && art.elements?.[head.id];
1057
1071
  if (elem) {
1058
1072
  path.$prefix = art.name.id; // prepend alias name
1059
- info( 'ref-special-in-extend', [ head.location, user ],
1060
- { '#': 'alias', id: head.id, art: elem._origin._main } );
1073
+ if ($extended === 'columns') {
1074
+ warning( 'ref-special-in-extend', [ head.location, user ],
1075
+ { '#': 'alias', id: head.id, art: elem._origin._main } );
1076
+ }
1077
+ else {
1078
+ error( 'ref-unexpected-in-extend', [ head.location, user ], {
1079
+ '#': 'alias',
1080
+ keyword: $extended,
1081
+ id: head.id,
1082
+ art: elem._origin._main,
1083
+ } );
1084
+ }
1061
1085
  setLink( head, '_navigation', elem );
1062
1086
  return setArtifactLink( head, elem._origin );
1063
1087
  }
1064
1088
  else if ($extended && art.elements) {
1065
- warning( 'ref-deprecated-in-extend', [ head.location, user ], { id: head.id },
1066
- // eslint-disable-next-line @stylistic/max-len
1067
- 'In an added column, do not use the table alias $(ID) to refer to source elements' );
1089
+ warning( 'ref-deprecated-in-extend', [ head.location, user ],
1090
+ { '#': $extended, id: head.id } );
1068
1091
  }
1069
1092
  }
1070
1093
  /* FALLTHROUGH */
@@ -1186,7 +1209,11 @@ function fns( model ) {
1186
1209
  return model.definitions;
1187
1210
  }
1188
1211
  function modelBuiltinsOrDefinitions( user ) {
1189
- return definedViaCdl( user ) ? model.$builtins : model.definitions;
1212
+ if (!definedViaCdl( user ))
1213
+ return model.definitions;
1214
+ if (!options.$cdsReplSupport)
1215
+ return model.$builtins;
1216
+ return Object.assign( { __proto__: null }, model.definitions, model.$builtins );
1190
1217
  }
1191
1218
 
1192
1219
  function artifactParams( user ) {
@@ -1355,6 +1382,21 @@ function fns( model ) {
1355
1382
  // TODO: improve text, use text variant for: "or builtin" or "definitions" or none
1356
1383
  }
1357
1384
 
1385
+ function undefinedFromReference( user, item, valid, dict, prev, path, semantics ) {
1386
+ const base = Functions.effectiveType?.( prev );
1387
+ if (Functions.effectiveType?.( base?.elements?.[item.id] )?.target)
1388
+ signalElementHint( user, item, valid, prev, semantics, path );
1389
+ else
1390
+ undefinedDefinition( user, item, valid, dict, prev, path, semantics );
1391
+ }
1392
+
1393
+ function undefinedTypeReference( user, item, valid, dict, prev, path, semantics ) {
1394
+ if (prev?.elements?.[item.id])
1395
+ signalElementHint( user, item, valid, prev, semantics, path );
1396
+ else
1397
+ undefinedDefinition( user, item, valid, dict, prev, path, semantics );
1398
+ }
1399
+
1358
1400
  function undefinedForAnnotate( user, item, valid, _dict, prev, path ) {
1359
1401
  // in a CSN source, only one env was tested (valid.length 1):
1360
1402
  const name = (prev) ? `${ prev.name.id }.${ item.id }` : item.id;
@@ -1382,6 +1424,14 @@ function fns( model ) {
1382
1424
  }
1383
1425
  }
1384
1426
 
1427
+ function signalElementHint( user, item, valid, prev, semantics, path ) {
1428
+ const art = searchName( prev, item.id, 'absolute' );
1429
+ const addSeparators = p => ( p === item ? ':' : '.' ) + p.id;
1430
+ const code = path[0].id + path.slice( 1 ).map( addSeparators ).join( '' );
1431
+ signalNotFound( 'ref-undefined-def', [ item.location, user ], valid,
1432
+ { '#': 'hint', art, code }, semantics );
1433
+ }
1434
+
1385
1435
  function endsWithSuffix( name, suffix, cond ) {
1386
1436
  if (!name.endsWith( suffix ))
1387
1437
  return false;
@@ -87,7 +87,7 @@ function tweakAssocs( model ) {
87
87
  forEachGeneric( art, 'elements', complainAboutTargetOutsideService );
88
88
 
89
89
  if (art.query) {
90
- traverseQueryPost(art.query, false, (query) => {
90
+ traverseQueryPost(art.query, null, (query) => {
91
91
  forEachGeneric( query, 'elements', handleQueryElements );
92
92
  });
93
93
  }
@@ -103,7 +103,7 @@ function tweakAssocs( model ) {
103
103
  // Check explicit ON / keys with REDIRECTED TO
104
104
  // TODO: run on all queries, but this is potentially incompatible
105
105
  // function rewriteViewCheck( view ) {
106
- // traverseQueryPost( view.query, false, ( query ) => {
106
+ // traverseQueryPost( view.query, null, ( query ) => {
107
107
  // forEachGeneric( query, 'elements', rewriteAssociationCheck );
108
108
  // } );
109
109
  // }
@@ -6,11 +6,9 @@
6
6
  // Please do not add functions “for completeness”, this is not an API file for
7
7
  // others but only by the core compiler.
8
8
 
9
- // TODO: split this file into utils/….js, add some functions from lib/base/model.js
10
-
11
9
  'use strict';
12
10
 
13
- const { dictAdd, dictFirst } = require('../base/dictionaries');
11
+ const { dictAdd, dictFirst } = require('./dictionaries');
14
12
  const { Location, weakLocation } = require('../base/location');
15
13
  const { XsnName, XsnArtifact } = require('./xsn-model');
16
14
 
@@ -294,6 +292,8 @@ const testFunctionPlaceholder = () => true;
294
292
  * Return path step if the path navigates along an association whose final type
295
293
  * satisfies function `test`; "navigates along" = last path item not considered
296
294
  * without truthy optional argument `alsoTestLast`.
295
+ *
296
+ * This function assumes that the reference has already been resolved.
297
297
  */
298
298
  function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
299
299
  for (const item of ref.path || []) {
@@ -419,7 +419,7 @@ function targetMaxNotOne( assoc, item ) {
419
419
  * two entities named `E`, the callback function is called on both.
420
420
  * It is also called on columns with `inline`.
421
421
  *
422
- * See also function forEachDefinition(), currently in lib/base/model.js.
422
+ * See also function forEachDefinition() below.
423
423
  */
424
424
  function forEachUserArtifact( model, prop, callback ) { // not enums
425
425
  forEachUserDict( model, prop, function main( art ) {
@@ -530,6 +530,11 @@ function forEachExprArray( query, array, refContext, exprContext, callback ) {
530
530
  // i.e. is necessary to calculate the elements of the query
531
531
  // except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
532
532
  // NOTE: does not run on non-referred sub queries! Consider using ‹main›.$queries instead!
533
+ //
534
+ // Argument `simpleOnly`:
535
+ // - truthy: only simple queries + sub query in from = do not traverse join and union args
536
+ // - false, '', 0: all, but not right of union/intersect/etc
537
+ // - null, undefined: all, including right side of union/intersect/etc
533
538
  function traverseQueryPost( query, simpleOnly, callback ) {
534
539
  if (!query) // parser error
535
540
  return;
@@ -540,7 +545,7 @@ function traverseQueryPost( query, simpleOnly, callback ) {
540
545
  }
541
546
  if (simpleOnly) {
542
547
  const { from } = query;
543
- if (!from || from.join) // parse error or join
548
+ if (!from || from.join) // parse error or join or union etc
544
549
  return; // ok are: path or simple sub query (!)
545
550
  }
546
551
  if (query.from) { // SELECT
@@ -550,7 +555,7 @@ function traverseQueryPost( query, simpleOnly, callback ) {
550
555
  // console.log('FE:')
551
556
  }
552
557
  else if (query.args) { // JOIN, UNION, INTERSECT
553
- if (!query.join && simpleOnly == null) {
558
+ if (!query.join && simpleOnly != null) {
554
559
  // enough for elements: traverse only first args for UNION/INTERSECT
555
560
  // TODO: we might use this also when we do not rewrite associations
556
561
  // in non-referred sub queries
@@ -578,7 +583,7 @@ function traverseQueryExtra( main, callback ) {
578
583
  if (!main.$queries)
579
584
  return;
580
585
  // with a top-level UNION, $queries[0] is just the left
581
- traverseQueryPost( main.query, false, (q) => { // also with right of UNION (to be compatible)
586
+ traverseQueryPost( main.query, null, (q) => { // also into right of UNION (to be compatible)
582
587
  setLink( q, '_status', 'extra' );
583
588
  callback( q );
584
589
  } );
@@ -586,7 +591,7 @@ function traverseQueryExtra( main, callback ) {
586
591
  if (query._status === 'extra' || query._parent.kind === '$tableAlias')
587
592
  continue; // if parent is alias, query is FROM source -> run by traverseQueryPost
588
593
  // we are now in the top-level (parent is entity) or a non-referred query (parent is query)
589
- traverseQueryPost( query, null, callback );
594
+ traverseQueryPost( query, false, callback ); // not into right of UNION
590
595
  }
591
596
  }
592
597
 
@@ -604,15 +609,18 @@ function viewFromPrimary( view ) {
604
609
  /**
605
610
  * About Helper property $expand for faster the XSN-to-CSN transformation
606
611
  * - null/undefined: artifact, member, items does not contain expanded members
607
- * - 'origin': all expanded (sub) elements have no new target/on and no new annotations
612
+ * - 'origin': all expanded (sub) elements have no associations and no new annotations
608
613
  * that value is only on elements, types, and params -> no other members
609
614
  * when set, only on elem/art with expanded elements
610
- * - 'target': all expanded (sub) elements might only have new target/on, but
615
+ * - 'target': all expanded (sub) elements might only have associations, but
611
616
  * no individual annotations on any (sub) member
612
617
  * when set, traverse all parents where the value has been 'origin' before
618
+ * TODO: only set if `target` or `on` has changed during redirection/rewrite
613
619
  * - 'annotate': at least one inferred (sub) member has an individual annotation,
614
620
  * not counting propagated ones; set up to the definition (main artifact)
615
- * (only set with anno on $inferred elem), annotate beats target
621
+ * (only set with anno on $inferred elem), 'annotate' beats 'target'
622
+ * - 'extend': at least one member has been added, 'extend' beats 'annotate'
623
+ *
616
624
  * Usage according to CSN flavor:
617
625
  * - gensrc: do not render inferred elements (including expanded elements),
618
626
  * collect annotate statements with value 'annotate'
@@ -621,22 +629,16 @@ function viewFromPrimary( view ) {
621
629
  * (might sometimes render the elements unnecessarily, which is not wrong)
622
630
  * - universal: do not render expanded sub elements if $expand = 'origin'
623
631
  */
624
- function setExpandStatus( elem, status ) {
625
- // set on element
626
- while (elem._main) {
627
- elem = elem._parent;
628
- if (status === 'annotate' ? elem.$expand === 'annotate' : elem.$expand !== 'origin')
629
- return;
630
- elem.$expand = status; // meaning: expanded, containing assocs
631
- for (let line = elem.items; line; line = line.items)
632
- line.$expand = status; // to-csn just uses the innermost $expand
633
- }
634
- }
632
+ const expandStatusLevel = {
633
+ origin: 1,
634
+ target: 2,
635
+ annotate: 3,
636
+ extend: 4,
637
+ };
635
638
 
636
- function setExpandStatusAnnotate( elem, status ) {
637
- for (;;) {
638
- if (elem.$expand === status)
639
- return; // already set
639
+ function setExpandStatus( elem, status ) {
640
+ const statusLevel = expandStatusLevel[status];
641
+ while (!elem.$expand || expandStatusLevel[elem.$expand] < statusLevel) {
640
642
  elem.$expand = status; // meaning: expanded, containing annos
641
643
  for (let line = elem.items; line; line = line.items)
642
644
  line.$expand = status; // to-csn just uses the innermost $expand
@@ -864,7 +866,6 @@ module.exports = {
864
866
  traverseQueryExtra,
865
867
  viewFromPrimary,
866
868
  setExpandStatus,
867
- setExpandStatusAnnotate,
868
869
  isDirectComposition,
869
870
  targetCantBeAspect,
870
871
  userQuery,
@@ -148,10 +148,10 @@ const { weakLocation } = require('../base/location');
148
148
  const {
149
149
  setArtifactLink,
150
150
  setLink,
151
- setExpandStatusAnnotate,
151
+ setExpandStatus,
152
152
  } = require('./utils');
153
153
  const { CompilerAssertion } = require('../base/error');
154
- const { isBetaEnabled } = require('../base/model');
154
+ const { isBetaEnabled } = require('../base/specialOptions');
155
155
  const { isSimpleCdlIdentifier } = require('../parsers/identifiers');
156
156
 
157
157
  // Config object passed around all "rewrite" functions.
@@ -550,7 +550,7 @@ function xprRewriteFns( model ) {
550
550
 
551
551
  stripAbsolutePathPrefix( expr, config.expandedRootType );
552
552
  prependRootPath( config.expandedRootType, config.expandedRoot, expr );
553
- setExpandStatusAnnotate( config.destination, 'annotate' );
553
+ setExpandStatus( config.destination, 'annotate' );
554
554
 
555
555
  config.destination[config.anno].$inferred = 'anno-rewrite';
556
556
  // $self-paths via type expansion always need to be rewritten.
@@ -244,7 +244,10 @@ const EdmPrimitiveTypeMap = {
244
244
  exact: 0,
245
245
  desc: 'Abstract meta type',
246
246
  },
247
- // 'Edm.Untyped': { v4: true, desc: 'Abstract void type' },
247
+ 'Edm.Untyped': {
248
+ v4: true,
249
+ desc: 'Abstract void type',
250
+ },
248
251
  };
249
252
  const EdmPathTypeMap = {
250
253
  'Edm.AnnotationPath': 1,
@@ -8,7 +8,7 @@ const {
8
8
  } = require('../EdmPrimitiveTypeDefinitions.js');
9
9
  const { isBuiltinType, isAnnotationExpression } = require('../../base/builtins');
10
10
  const { transformExpression } = require('../../transform/db/applyTransformations.js');
11
- const { conditionAsTree, expressionAsTree } = require('../../model/xprAsTree');
11
+ const { conditionAsTree, expressionAsTree } = require('../../base/xprAsTree');
12
12
 
13
13
  /**
14
14
  * Translate a given token stream expression into an edmJson representation
@@ -390,9 +390,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions, generi
390
390
 
391
391
 
392
392
  transform.ref = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
393
- // until empty filter syntax is introduced for the annotation expressions,
394
- // we ignore the filters in order to generate EDMX
395
- if (xpr.some(ps => ps.args/* || ps.where */)) {
393
+ if (xpr.some(ps => ps.args /* || (ps.where && ps.where.length)) until PR#13752 filters were also forbidden */)) {
396
394
  error('odata-anno-xpr-ref', location, {
397
395
  anno, elemref: parent, '#': 'args',
398
396
  });
@@ -6,7 +6,8 @@ const edmUtils = require('../edmUtils.js');
6
6
  const oDataDictionary = require('../../gen/Dictionary.json');
7
7
  const preprocessAnnotations = require('./preprocessAnnotations.js');
8
8
  const { forEachDefinition } = require('../../model/csnUtils');
9
- const { isBetaEnabled, setProp } = require('../../base/model.js');
9
+ const { isBetaEnabled } = require('../../base/specialOptions.js');
10
+ const { setProp } = require('../../utils/objectUtils.js');
10
11
  const { xpr2edmJson, getEdmJsonHandler } = require('./edmJson.js');
11
12
  const { vocabularyDefinitions } = require('./vocabularyDefinitions.js');
12
13
  const { EdmPathTypeMap } = require('../EdmPrimitiveTypeDefinitions.js');
@@ -890,13 +891,18 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
890
891
  // oTarget: the result object (o: odata)
891
892
  // oTermName: current term
892
893
  // dTypeNameArg: expected type of cAnnoValue according to dictionary, may be null (d: dictionary)
893
- function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg ) {
894
+ // dTypeDef: the whole object definition of the typefrom the dictionary, needed for DerivedTypeConstraints for Edm.Untyped
895
+ // @ts-ignore
896
+ function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg/* , dTypeDef = undefined */ ) {
894
897
  // this function basically only figures out what kind of annotation value we have
895
898
  // (can be: array, expression, enum, pseudo-record, record, simple value),
896
899
  // then calls a more specific function to deal with it and puts
897
900
  // the result into the oTarget object
898
901
 
899
- const [ dTypeName, dTypeIsACollection ] = stripCollection(dTypeNameArg);
902
+ const [ dTypeName, dTypeIsACollection/* , dTypeIsUntyped */ ] = stripCollectionAndgetIsCollectionOrUntyped(dTypeNameArg);
903
+
904
+ // if (dTypeIsUntyped)
905
+ // validateUntypedAnnotationValue(cAnnoValue, typeof dTypeNameArg === 'object' ? dTypeNameArg : dTypeDef, msg);
900
906
 
901
907
  if (Array.isArray(cAnnoValue)) {
902
908
  if (isEnumType(dTypeName)) {
@@ -914,7 +920,12 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
914
920
  if ('=' in cAnnoValue) {
915
921
  if (dTypeIsACollection) {
916
922
  message('odata-anno-value', msg.location,
917
- { anno: msg.anno(), str: 'path', '#': 'incompval' });
923
+ {
924
+ anno: msg.anno(),
925
+ type: dTypeName,
926
+ str: 'path',
927
+ '#': 'incompval',
928
+ });
918
929
  }
919
930
  // expression
920
931
  const res = handleExpression(cAnnoValue['='], dTypeName);
@@ -1080,6 +1091,30 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1080
1091
  // concatenate all the enums to a string, separated by spaces
1081
1092
  return cAnnoValue.filter( x => x['#']).map( x => (forXml ? `${ dTypeName }/` : '') + x['#'] ).join(forXml ? ' ' : ',');
1082
1093
  }
1094
+
1095
+ // --------------------- Edm.Untyped helper function ----------------------
1096
+
1097
+ // // eslint-disable-next-line no-shadow
1098
+ // function validateUntypedAnnotationValue( cAnnoValue, dTypeDef, msg ) {
1099
+ // if (!dTypeDef?.DerivedTypeConstraint)
1100
+ // return;
1101
+ // // if the annotation value is a path, but no path is allowed for this annotation, issue a message,
1102
+ // // for example the UI.DataField.Value annotation
1103
+ // if (cAnnoValue.$edmJson?.$Path/* || cAnnoValue['=']) -> this is handled in handleValue */ &&
1104
+ // !(dTypeDef.DerivedTypeConstraint.some(type => EdmPathTypeMap[type]) ||
1105
+ // dTypeDef.DerivedTypeConstraint.includes('Edm.EntityType') ||
1106
+ // dTypeDef.DerivedTypeConstraint.includes('Edm.ComplexType'))
1107
+ // ) {
1108
+ // message('odata-anno-value', msg.location,
1109
+ // {
1110
+ // anno: msg.anno(),
1111
+ // str: 'path',
1112
+ // type: 'Edm.Untyped',
1113
+ // '#': 'incompval',
1114
+ // });
1115
+ // }
1116
+ // }
1117
+ // ----------------------------------------------------------------------
1083
1118
  }
1084
1119
 
1085
1120
  // found an expression value ("=") "expr"
@@ -1417,7 +1452,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1417
1452
  function generateCollection( annoValue, termName, dTypeName, dTypeIsACollection, msg ) {
1418
1453
  const newCollection = new Edm.Collection(v);
1419
1454
 
1420
- if (dTypeName && !dTypeIsACollection) {
1455
+ if (dTypeName && !dTypeIsACollection && dTypeName !== 'Edm.Untyped') {
1421
1456
  message('odata-anno-value', msg.location,
1422
1457
  {
1423
1458
  anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'incompval',
@@ -1720,14 +1755,23 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1720
1755
  return dTypeName;
1721
1756
  }
1722
1757
 
1723
- function stripCollection( typeName ) {
1724
- if (typeName) {
1758
+ function stripCollectionAndgetIsCollectionOrUntyped( typeName ) {
1759
+ if (typeName && typeof typeName === 'string') {
1725
1760
  const match = typeName.match(/^Collection\((.+)\)/);
1726
1761
  if (match)
1727
1762
  return [ match[1], true ];
1763
+ // return [ match[1], true, match[1] === 'Edm.Untyped' ];
1728
1764
  }
1729
1765
 
1766
+ // in the Dictionary.json, if a certain element or type has type 'Edm.Untyped', we also store
1767
+ // a 'DerivedTypeConstraint' property containing the possible types for this element or type.
1768
+ // Therefore, the type is not just a string but an object: { Type: 'Edm.Untyped', DerivedTypeConstraint: [...] }
1769
+ if (typeName && typeof typeName === 'object')
1770
+ return [ typeName.Type, typeName.DerivedTypeConstraint.every(type => type.startsWith('Collection(')) ];
1771
+ // return [ typeName.Type, typeName.DerivedTypeConstraint.some(type => type.startsWith('Collection(')), true ];
1772
+
1730
1773
  return [ typeName, false ];
1774
+ // return [ typeName, false, typeName === 'Edm.Untyped' ];
1731
1775
  }
1732
1776
 
1733
1777
  function isPrimitiveType( typeName ) {
@@ -9,7 +9,8 @@ const VALUELIST_NAVPROP_PREFIX = '';
9
9
  const edmUtils = require('./edmUtils.js');
10
10
  const { initializeModel } = require('./edmPreprocessor.js');
11
11
  const translate = require('./annotations/genericTranslation.js');
12
- const { setProp, isBetaEnabled } = require('../base/model');
12
+ const { setProp } = require('../utils/objectUtils');
13
+ const { isBetaEnabled } = require('../base/specialOptions');
13
14
  const {
14
15
  isEdmPropertyRendered, getUtils, findAnnotationExpression,
15
16
  } = require('../model/csnUtils');
@@ -21,7 +22,7 @@ const {
21
22
  EdmPrimitiveTypeMap,
22
23
  } = require('./EdmPrimitiveTypeDefinitions.js');
23
24
  const { getEdm } = require('./edm.js');
24
- const { cloneFullCsn } = require('../model/cloneCsn');
25
+ const { cloneFullCsn } = require('../base/cloneCsn');
25
26
  const { forEach, forEachValue } = require('../utils/objectUtils.js');
26
27
  /*
27
28
  OData V2 spec 06/01/2017 PDF version is available here:
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const edmUtils = require('./edmUtils.js');
4
- const { setProp } = require('../base/model');
4
+ const { setProp } = require('../utils/objectUtils');
5
5
  const { forEachGeneric } = require('../model/csnUtils');
6
6
 
7
7
  /*
@@ -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 {
5
6
  forEachDefinition, forEachMemberRecursively, getUtils,
6
7
  transformAnnotationExpression,
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /* eslint max-statements-per-line:off */
4
- const { setProp, isBetaEnabled } = require('../base/model');
4
+ const { isBetaEnabled } = require('../base/specialOptions');
5
5
  const {
6
6
  forEachDefinition, forEachGeneric, forEachMemberRecursively,
7
7
  isEdmPropertyRendered, getUtils,
@@ -15,8 +15,8 @@ const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
15
15
  const { inboundQualificationChecks } = require('./edmInboundChecks.js');
16
16
  const typesExposure = require('../transform/odata/typesExposure');
17
17
  const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
18
- const { cloneCsnNonDict, cloneAnnotationValue } = require('../model/cloneCsn');
19
- const { forEach, forEachKey } = require('../utils/objectUtils.js');
18
+ const { cloneCsnNonDict, cloneAnnotationValue } = require('../base/cloneCsn');
19
+ const { setProp, forEach, forEachKey } = require('../utils/objectUtils.js');
20
20
 
21
21
  const NavResAnno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
22
22
 
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const { setProp } = require('../base/model');
3
+ const { setProp } = require('../utils/objectUtils');
4
4
  const {
5
5
  isEdmPropertyRendered, applyTransformations,
6
6
  } = require('../model/csnUtils');
7
7
  const { isBuiltinType } = require('../base/builtins');
8
8
  const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
9
9
  const { CompilerAssertion } = require('../base/error');
10
- const { cloneAnnotationValue } = require('../model/cloneCsn');
10
+ const { cloneAnnotationValue } = require('../base/cloneCsn');
11
11
 
12
12
  /* eslint max-statements-per-line:off */
13
13
  function validateOptions( _options ) {