@sap/cds-compiler 3.9.4 → 4.0.2

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 (95) hide show
  1. package/CHANGELOG.md +107 -4
  2. package/README.md +0 -1
  3. package/bin/cdsc.js +11 -23
  4. package/bin/cdsse.js +3 -3
  5. package/doc/API.md +5 -0
  6. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  7. package/doc/CHANGELOG_BETA.md +17 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +28 -0
  9. package/lib/api/.eslintrc.json +1 -1
  10. package/lib/api/main.js +55 -9
  11. package/lib/api/options.js +2 -0
  12. package/lib/base/error.js +2 -0
  13. package/lib/base/message-registry.js +143 -64
  14. package/lib/base/messages.js +213 -107
  15. package/lib/base/model.js +11 -11
  16. package/lib/checks/.eslintrc.json +1 -1
  17. package/lib/checks/annotationsOData.js +2 -2
  18. package/lib/checks/elements.js +1 -1
  19. package/lib/checks/enricher.js +26 -3
  20. package/lib/checks/onConditions.js +67 -12
  21. package/lib/checks/queryNoDbArtifacts.js +106 -105
  22. package/lib/checks/sql-snippets.js +2 -0
  23. package/lib/checks/types.js +12 -6
  24. package/lib/checks/validator.js +2 -2
  25. package/lib/compiler/assert-consistency.js +10 -8
  26. package/lib/compiler/builtins.js +8 -2
  27. package/lib/compiler/checks.js +52 -35
  28. package/lib/compiler/define.js +31 -26
  29. package/lib/compiler/extend.js +120 -65
  30. package/lib/compiler/finalize-parse-cdl.js +12 -43
  31. package/lib/compiler/generate.js +16 -5
  32. package/lib/compiler/index.js +8 -5
  33. package/lib/compiler/kick-start.js +4 -3
  34. package/lib/compiler/populate.js +96 -95
  35. package/lib/compiler/propagator.js +7 -8
  36. package/lib/compiler/resolve.js +377 -103
  37. package/lib/compiler/shared.js +794 -517
  38. package/lib/compiler/tweak-assocs.js +8 -6
  39. package/lib/compiler/utils.js +44 -0
  40. package/lib/edm/annotations/genericTranslation.js +41 -5
  41. package/lib/edm/csn2edm.js +34 -32
  42. package/lib/edm/edm.js +34 -31
  43. package/lib/edm/edmAnnoPreprocessor.js +0 -23
  44. package/lib/edm/edmInboundChecks.js +7 -2
  45. package/lib/edm/edmPreprocessor.js +25 -18
  46. package/lib/edm/edmUtils.js +8 -4
  47. package/lib/gen/Dictionary.json +18 -0
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +4 -2
  50. package/lib/gen/languageParser.js +5006 -4582
  51. package/lib/json/from-csn.js +157 -112
  52. package/lib/json/to-csn.js +60 -89
  53. package/lib/language/antlrParser.js +17 -13
  54. package/lib/language/docCommentParser.js +11 -1
  55. package/lib/language/genericAntlrParser.js +13 -10
  56. package/lib/language/language.g4 +168 -97
  57. package/lib/main.d.ts +128 -36
  58. package/lib/main.js +1 -1
  59. package/lib/model/csnRefs.js +24 -5
  60. package/lib/model/csnUtils.js +9 -8
  61. package/lib/model/revealInternalProperties.js +7 -12
  62. package/lib/model/sortViews.js +4 -2
  63. package/lib/modelCompare/compare.js +1 -1
  64. package/lib/modelCompare/utils/filter.js +40 -2
  65. package/lib/optionProcessor.js +0 -3
  66. package/lib/render/toCdl.js +247 -214
  67. package/lib/render/toHdbcds.js +197 -181
  68. package/lib/render/toSql.js +325 -289
  69. package/lib/render/utils/common.js +42 -4
  70. package/lib/render/utils/delta.js +1 -1
  71. package/lib/render/utils/sql.js +3 -3
  72. package/lib/transform/braceExpression.js +2 -2
  73. package/lib/transform/db/.eslintrc.json +1 -1
  74. package/lib/transform/db/applyTransformations.js +3 -3
  75. package/lib/transform/db/associations.js +24 -12
  76. package/lib/transform/db/expansion.js +17 -18
  77. package/lib/transform/db/flattening.js +17 -21
  78. package/lib/transform/db/rewriteCalculatedElements.js +171 -64
  79. package/lib/transform/db/views.js +3 -4
  80. package/lib/transform/draft/db.js +21 -12
  81. package/lib/transform/draft/odata.js +4 -0
  82. package/lib/transform/forOdataNew.js +62 -47
  83. package/lib/transform/forRelationalDB.js +12 -7
  84. package/lib/transform/localized.js +4 -2
  85. package/lib/transform/odata/toFinalBaseType.js +5 -5
  86. package/lib/transform/odata/typesExposure.js +3 -3
  87. package/lib/transform/parseExpr.js +3 -0
  88. package/lib/transform/transformUtilsNew.js +43 -23
  89. package/lib/transform/translateAssocsToJoins.js +7 -6
  90. package/lib/transform/universalCsn/.eslintrc.json +1 -1
  91. package/lib/transform/universalCsn/coreComputed.js +7 -5
  92. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
  93. package/package.json +2 -2
  94. package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
  95. package/share/messages/message-explanations.json +1 -1
@@ -38,7 +38,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
38
38
 
39
39
  // proxies are merged into the final model after all proxy elements are collected
40
40
  const proxyCache = [];
41
- // iterarte only over those definitions that need to be preprocessed
41
+ // iterate only over those definitions that need to be preprocessed
42
42
  // instead of mangling through the whole model each time
43
43
  // preprocess steps removing adding to the model must co-modify this map
44
44
  const reqDefs = { definitions: Object.create(null) };
@@ -398,7 +398,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
398
398
  // non-containment rendering. If containment rendering is active, the containee has no
399
399
  // entity set. Instead try to rewrite the annotation in such a way that it is effective
400
400
  // on the containment navigation property.
401
- // $containeeAssoications stores the containees (children/outbound edges)
401
+ // $containeeAssociations stores the containees (children/outbound edges)
402
402
  // $containerNames stores the containers (parents/inbound edges)
403
403
 
404
404
  function initContainments(container) {
@@ -410,7 +410,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
410
410
  }
411
411
 
412
412
  function initContainments(elt, _memberName, _prop, path) {
413
- if(elt.target && elt['@odata.contained'] && !elt._ignore) {
413
+ if(elt.target && elt['@odata.contained']) {
414
414
  // store all containment associations, required to create the containment paths later on
415
415
  container.$containeeAssociations.push( { assoc: elt, path });
416
416
  // Let the containee know its container
@@ -495,7 +495,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
495
495
 
496
496
  // create the Parameter Definition
497
497
  const parameterCsn = createParameterEntity(entityCsn, entityName, false);
498
-
498
+ setProp(parameterCsn, '_origin', entityCsn);
499
499
  // create the Type Definition
500
500
  // modify the original parameter entity with backlink and new name
501
501
  if(csn.definitions[typeEntityName])
@@ -609,6 +609,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
609
609
  [ 'definitions', parameterEntityName, 'elements', parameterToTypeAssocName ] );
610
610
  }
611
611
 
612
+ [ '@odata.singleton', '@odata.singleton.nullable' ].forEach(a => {
613
+ if(entityCsn[a] != null)
614
+ parameterCsn[a] = entityCsn[a];
615
+ delete entityCsn[a];
616
+ });
617
+
612
618
  // initialize containment
613
619
  // propagate containment information, if containment is recursive, use parameterCsn.name as $containerNames
614
620
  if(entityCsn.$containerNames) {
@@ -829,7 +835,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
829
835
  }
830
836
  }
831
837
  // Only in containment:
832
- // Ignore this (foreign key) elment if renderForeignKeys is false
838
+ // Ignore this (foreign key) element if renderForeignKeys is false
833
839
  if(options.odataContainment && element['@odata.containment.ignore']) {
834
840
  if(!options.renderForeignKeys)
835
841
  edmUtils.assignAnnotation(element, '@cds.api.ignore', true);
@@ -859,7 +865,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
859
865
  forEachMemberRecursively(def.items || def, finalizeConstraintsOnAssoc, [], true, { elementsOnly: true });
860
866
  }
861
867
  function finalizeConstraintsOnAssoc(element) {
862
- if (element.target && !element._ignore && element._constraints) {
868
+ if (element.target && element._constraints) {
863
869
  edmUtils.finalizeReferentialConstraints(csn, element, options, info);
864
870
 
865
871
  if(element._constraints?._partnerCsn) {
@@ -958,14 +964,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
958
964
  // if this artifact is a service member check its associations
959
965
  if(globalSchemaPrefix) {
960
966
  forEachGeneric(struct.items || struct, 'elements', element => {
961
- if(!element.target || element['@odata.navigable'] === false)
967
+ if(!edmUtils.isNavigable(element))
962
968
  return;
963
969
  /*
964
970
  * Consider everything @cds.autoexpose: falsy to be a proxy candidate for now
965
971
  */
966
972
  /*
967
973
  if(element._target['@cds.autoexpose'] === false) {
968
- // :TODO: Also _ignore foreign keys to association?
974
+ // :TODO: Also ignore foreign keys to association?
969
975
  edmUtils.foreach(struct.elements,
970
976
  e =>
971
977
  e['@odata.foreignKey4'] === element.name,
@@ -985,7 +991,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
985
991
  // proxy required.
986
992
  // association must be managed and not unmanaged
987
993
 
988
- // odataProxies (P) and odataXServiceRefs (X) are evalutated as follows:
994
+ // odataProxies (P) and odataXServiceRefs (X) are evaluated as follows:
989
995
  // P | X | Action
990
996
  // 0 | 0 | No out bound navigation
991
997
  // 0 | 1 | Cross service references are generated
@@ -1211,7 +1217,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1211
1217
  if(typeClone) {
1212
1218
  // Recurse into elements of 'type' (if any)
1213
1219
  typeClone.elements && Object.entries(typeClone.elements).forEach(([elemName, elem]) => {
1214
- // if this is a foreign key elment, we must check whether or not the association
1220
+ // if this is a foreign key element, we must check whether or not the association
1215
1221
  // has been exposed as proxy. If it has not been exposed, no further structured
1216
1222
  // types must be exposed as 'Proxy_' types.
1217
1223
 
@@ -1494,7 +1500,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1494
1500
 
1495
1501
  function finalizeProxyContainments(proxy) {
1496
1502
  // initialise containments after all exposed types are collected
1497
- // AND remove unfullfillable NavRestrictions
1503
+ // AND remove unfulfillable NavRestrictions
1498
1504
  initContainments(proxy);
1499
1505
  const assocPaths = proxy.$containeeAssociations.map(entry => entry.path.join('.'));
1500
1506
  const newNpr = [];
@@ -1643,7 +1649,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1643
1649
  csn.definitions[elt.type] &&
1644
1650
  csnUtils.getFinalTypeInfo(elt.type).items);
1645
1651
  if(type ||
1646
- (options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
1652
+ (options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
1647
1653
  elt.cardinality?.max && elt.cardinality.max !== 1) {
1648
1654
  // many primary key can be induced by a many parameter of a view
1649
1655
  message('odata-spec-violation-key-array', location,
@@ -1857,6 +1863,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1857
1863
  annotateAllowedValues(member, path);
1858
1864
  ComputedDefaultValue(member);
1859
1865
  if (member.returns) {
1866
+ edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
1860
1867
  markCollection(member.returns);
1861
1868
  mapCdsToEdmProp(member.returns);
1862
1869
  annotateAllowedValues(member.returns, [...path, 'returns']);
@@ -1910,7 +1917,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1910
1917
  else if(node.kind !== 'annotation') {
1911
1918
  // omit the entry and warn
1912
1919
  warning('odata-enum-missing-value', path,
1913
- { name: enumSymbol, anno: '@Valiation.AllowedValues', type: typeDef.type },
1920
+ { name: enumSymbol, anno: '@Validation.AllowedValues', type: typeDef.type },
1914
1921
  'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
1915
1922
  }
1916
1923
  }
@@ -1918,13 +1925,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1918
1925
  else { // enumSymbolDef not found
1919
1926
  // omit the entry and warn
1920
1927
  warning('odata-enum-missing-value', path,
1921
- { name: enumSymbol, anno: '@Valiation.AllowedValues', type: typeDef.type },
1928
+ { name: enumSymbol, anno: '@Validation.AllowedValues', type: typeDef.type },
1922
1929
  'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
1923
1930
  }
1924
1931
 
1925
1932
  // Can't rely that @description has already been renamed to @Core.Description
1926
1933
  // Eval description according to precedence (doc comment must be considered already in Odata transformer
1927
- // as in contrast to the other doc commments as it is used to annotate the @Validation.AllowedValues)
1934
+ // as in contrast to the other doc comments as it is used to annotate the @Validation.AllowedValues)
1928
1935
  const desc = enumSymbolDef ? enumSymbolDef['@Core.Description'] || enumSymbolDef['@description'] || enumSymbolDef.doc : undefined;
1929
1936
  if (desc)
1930
1937
  result['@Core.Description'] = desc;
@@ -1945,8 +1952,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1945
1952
 
1946
1953
  if(!options.odataCapabilitiesPullup)
1947
1954
  return;
1948
- // meaningless for non-entities and proxies
1949
- if(rootContainer.kind !== 'entity' || rootContainer.$proxy)
1955
+ // @Capabilities is applicable to EntitySet/Collection only
1956
+ if(!rootContainer.$hasEntitySet)
1950
1957
  return;
1951
1958
 
1952
1959
  const isRecursiveContainment =
@@ -1994,7 +2001,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1994
2001
  const { assoc, path } = entry;
1995
2002
  const containee = assoc._target;
1996
2003
 
1997
- if(isMyServiceRequested(containee.name) || containee.$proxy) {
2004
+ if(edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
1998
2005
  const localAssocPath = path.join('.');
1999
2006
  let navPropEntry= localRestrictions.find(p =>
2000
2007
  p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
@@ -74,10 +74,13 @@ function isToMany(assoc) {
74
74
  return targetMax === '*' || Number(targetMax) > 1;
75
75
  }
76
76
 
77
+ function isNavigable(assoc) {
78
+ return (assoc.target && (assoc['@odata.navigable'] == null || assoc['@odata.navigable']));
79
+ }
77
80
  function isSingleton(entityCsn) {
78
81
  const singleton = entityCsn['@odata.singleton'];
79
- const hasNullable = entityCsn['@odata.singleton.nullable'] !== undefined && entityCsn['@odata.singleton.nullable'] !== null;
80
- return singleton || ((singleton === undefined || singleton === null) && hasNullable);
82
+ const hasNullable = entityCsn['@odata.singleton.nullable'] != null;
83
+ return singleton || (singleton == null && hasNullable);
81
84
  }
82
85
 
83
86
  function isParameterizedEntity(artifact) {
@@ -137,7 +140,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
137
140
  }
138
141
  if(originAssocCsn.target) {
139
142
  // Mark this association as backlink if $self appears exactly once
140
- // to surpress edm:Association generation in V2 mode
143
+ // to suppress edm:Association generation in V2 mode
141
144
  if(isBacklink) {
142
145
  // establish partnership with origin assoc but only if this association is the first one
143
146
  if(originAssocCsn._selfReferences.length === 0) {
@@ -639,7 +642,7 @@ function addTypeFacets(node, csn)
639
642
  * @param {string} identifier
640
643
  */
641
644
  function isODataSimpleIdentifier(identifier){
642
- // this regular expression reflects the specifiation from above
645
+ // this regular expression reflects the specification from above
643
646
  const regex = /^[\p{Letter}\p{Nl}_][_\p{Letter}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]{0,127}$/gu
644
647
  return identifier && identifier.match(regex);
645
648
  }
@@ -898,6 +901,7 @@ module.exports = {
898
901
  foreach,
899
902
  isContainee,
900
903
  isToMany,
904
+ isNavigable,
901
905
  isSingleton,
902
906
  isStructuredType,
903
907
  isStructuredArtifact,
@@ -1844,6 +1844,13 @@
1844
1844
  "EntityType"
1845
1845
  ]
1846
1846
  },
1847
+ "UI.Note": {
1848
+ "Type": "UI.NoteType",
1849
+ "AppliesTo": [
1850
+ "EntityType"
1851
+ ],
1852
+ "$experimental": true
1853
+ },
1847
1854
  "UI.Importance": {
1848
1855
  "Type": "UI.ImportanceType",
1849
1856
  "AppliesTo": [
@@ -3931,6 +3938,17 @@
3931
3938
  "High": "Edm.PrimitiveType"
3932
3939
  }
3933
3940
  },
3941
+ "UI.NoteType": {
3942
+ "$kind": "ComplexType",
3943
+ "Properties": {
3944
+ "Title": "Edm.String",
3945
+ "Content": "Edm.String",
3946
+ "Type": "Edm.String",
3947
+ "MaximalLength": "Edm.Int32",
3948
+ "MultipleNotes": "Edm.Boolean"
3949
+ },
3950
+ "$experimental": true
3951
+ },
3934
3952
  "UI.DataFieldAbstract": {
3935
3953
  "$kind": "ComplexType",
3936
3954
  "Abstract": "true",
@@ -1 +1 @@
1
- 78bdabed1311fe8b3b0921bfc5300ece
1
+ 8c23b0ea94048e8e7a54d2ed4a4e4707