@sap/cds-compiler 3.9.2 → 4.0.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.
- package/CHANGELOG.md +98 -0
- package/README.md +0 -1
- package/bin/cdsc.js +11 -23
- package/bin/cdsse.js +3 -3
- package/doc/API.md +5 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +17 -1
- package/doc/CHANGELOG_DEPRECATED.md +28 -0
- package/lib/api/.eslintrc.json +1 -1
- package/lib/api/main.js +26 -8
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +144 -65
- package/lib/base/messages.js +213 -107
- package/lib/base/model.js +11 -11
- package/lib/checks/.eslintrc.json +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +26 -3
- package/lib/checks/onConditions.js +67 -12
- package/lib/checks/queryNoDbArtifacts.js +106 -105
- package/lib/checks/sql-snippets.js +2 -0
- package/lib/checks/types.js +12 -6
- package/lib/checks/validator.js +2 -2
- package/lib/compiler/assert-consistency.js +10 -8
- package/lib/compiler/builtins.js +8 -2
- package/lib/compiler/checks.js +52 -35
- package/lib/compiler/define.js +31 -26
- package/lib/compiler/extend.js +120 -65
- package/lib/compiler/finalize-parse-cdl.js +12 -43
- package/lib/compiler/generate.js +16 -5
- package/lib/compiler/index.js +8 -5
- package/lib/compiler/kick-start.js +4 -3
- package/lib/compiler/populate.js +96 -95
- package/lib/compiler/propagator.js +7 -8
- package/lib/compiler/resolve.js +377 -103
- package/lib/compiler/shared.js +794 -517
- package/lib/compiler/tweak-assocs.js +8 -6
- package/lib/compiler/utils.js +44 -0
- package/lib/edm/annotations/genericTranslation.js +24 -6
- package/lib/edm/csn2edm.js +47 -45
- package/lib/edm/edm.js +34 -31
- package/lib/edm/edmAnnoPreprocessor.js +0 -23
- package/lib/edm/edmInboundChecks.js +7 -2
- package/lib/edm/edmPreprocessor.js +18 -17
- package/lib/edm/edmUtils.js +8 -4
- package/lib/gen/Dictionary.json +18 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -2
- package/lib/gen/languageParser.js +5006 -4582
- package/lib/json/from-csn.js +159 -114
- package/lib/json/to-csn.js +60 -89
- package/lib/language/antlrParser.js +17 -13
- package/lib/language/docCommentParser.js +11 -1
- package/lib/language/genericAntlrParser.js +13 -10
- package/lib/language/language.g4 +168 -97
- package/lib/main.d.ts +128 -36
- package/lib/main.js +1 -1
- package/lib/model/csnRefs.js +24 -5
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +7 -12
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +40 -2
- package/lib/optionProcessor.js +0 -3
- package/lib/render/toCdl.js +247 -214
- package/lib/render/toHdbcds.js +197 -181
- package/lib/render/toSql.js +325 -289
- package/lib/render/utils/common.js +42 -4
- package/lib/render/utils/delta.js +1 -1
- package/lib/render/utils/sql.js +3 -3
- package/lib/transform/braceExpression.js +2 -2
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/associations.js +24 -12
- package/lib/transform/db/expansion.js +17 -18
- package/lib/transform/db/flattening.js +17 -21
- package/lib/transform/db/rewriteCalculatedElements.js +171 -64
- package/lib/transform/db/views.js +3 -4
- package/lib/transform/draft/db.js +21 -12
- package/lib/transform/draft/odata.js +4 -0
- package/lib/transform/forOdataNew.js +11 -10
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +5 -3
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/parseExpr.js +3 -0
- package/lib/transform/transformUtilsNew.js +43 -23
- package/lib/transform/translateAssocsToJoins.js +7 -6
- package/lib/transform/universalCsn/.eslintrc.json +1 -1
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +2 -2
- package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
- 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
|
-
//
|
|
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
|
-
// $
|
|
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']
|
|
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
|
|
@@ -829,7 +829,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
829
829
|
}
|
|
830
830
|
}
|
|
831
831
|
// Only in containment:
|
|
832
|
-
// Ignore this (foreign key)
|
|
832
|
+
// Ignore this (foreign key) element if renderForeignKeys is false
|
|
833
833
|
if(options.odataContainment && element['@odata.containment.ignore']) {
|
|
834
834
|
if(!options.renderForeignKeys)
|
|
835
835
|
edmUtils.assignAnnotation(element, '@cds.api.ignore', true);
|
|
@@ -859,7 +859,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
859
859
|
forEachMemberRecursively(def.items || def, finalizeConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
860
860
|
}
|
|
861
861
|
function finalizeConstraintsOnAssoc(element) {
|
|
862
|
-
if (element.target &&
|
|
862
|
+
if (element.target && element._constraints) {
|
|
863
863
|
edmUtils.finalizeReferentialConstraints(csn, element, options, info);
|
|
864
864
|
|
|
865
865
|
if(element._constraints?._partnerCsn) {
|
|
@@ -958,14 +958,14 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
958
958
|
// if this artifact is a service member check its associations
|
|
959
959
|
if(globalSchemaPrefix) {
|
|
960
960
|
forEachGeneric(struct.items || struct, 'elements', element => {
|
|
961
|
-
if(!
|
|
961
|
+
if(!edmUtils.isNavigable(element))
|
|
962
962
|
return;
|
|
963
963
|
/*
|
|
964
964
|
* Consider everything @cds.autoexpose: falsy to be a proxy candidate for now
|
|
965
965
|
*/
|
|
966
966
|
/*
|
|
967
967
|
if(element._target['@cds.autoexpose'] === false) {
|
|
968
|
-
// :TODO: Also
|
|
968
|
+
// :TODO: Also ignore foreign keys to association?
|
|
969
969
|
edmUtils.foreach(struct.elements,
|
|
970
970
|
e =>
|
|
971
971
|
e['@odata.foreignKey4'] === element.name,
|
|
@@ -985,7 +985,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
985
985
|
// proxy required.
|
|
986
986
|
// association must be managed and not unmanaged
|
|
987
987
|
|
|
988
|
-
// odataProxies (P) and odataXServiceRefs (X) are
|
|
988
|
+
// odataProxies (P) and odataXServiceRefs (X) are evaluated as follows:
|
|
989
989
|
// P | X | Action
|
|
990
990
|
// 0 | 0 | No out bound navigation
|
|
991
991
|
// 0 | 1 | Cross service references are generated
|
|
@@ -1211,7 +1211,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1211
1211
|
if(typeClone) {
|
|
1212
1212
|
// Recurse into elements of 'type' (if any)
|
|
1213
1213
|
typeClone.elements && Object.entries(typeClone.elements).forEach(([elemName, elem]) => {
|
|
1214
|
-
// if this is a foreign key
|
|
1214
|
+
// if this is a foreign key element, we must check whether or not the association
|
|
1215
1215
|
// has been exposed as proxy. If it has not been exposed, no further structured
|
|
1216
1216
|
// types must be exposed as 'Proxy_' types.
|
|
1217
1217
|
|
|
@@ -1494,7 +1494,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1494
1494
|
|
|
1495
1495
|
function finalizeProxyContainments(proxy) {
|
|
1496
1496
|
// initialise containments after all exposed types are collected
|
|
1497
|
-
// AND remove
|
|
1497
|
+
// AND remove unfulfillable NavRestrictions
|
|
1498
1498
|
initContainments(proxy);
|
|
1499
1499
|
const assocPaths = proxy.$containeeAssociations.map(entry => entry.path.join('.'));
|
|
1500
1500
|
const newNpr = [];
|
|
@@ -1643,7 +1643,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1643
1643
|
csn.definitions[elt.type] &&
|
|
1644
1644
|
csnUtils.getFinalTypeInfo(elt.type).items);
|
|
1645
1645
|
if(type ||
|
|
1646
|
-
(options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
|
|
1646
|
+
(options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
|
|
1647
1647
|
elt.cardinality?.max && elt.cardinality.max !== 1) {
|
|
1648
1648
|
// many primary key can be induced by a many parameter of a view
|
|
1649
1649
|
message('odata-spec-violation-key-array', location,
|
|
@@ -1857,6 +1857,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1857
1857
|
annotateAllowedValues(member, path);
|
|
1858
1858
|
ComputedDefaultValue(member);
|
|
1859
1859
|
if (member.returns) {
|
|
1860
|
+
edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
|
|
1860
1861
|
markCollection(member.returns);
|
|
1861
1862
|
mapCdsToEdmProp(member.returns);
|
|
1862
1863
|
annotateAllowedValues(member.returns, [...path, 'returns']);
|
|
@@ -1910,7 +1911,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1910
1911
|
else if(node.kind !== 'annotation') {
|
|
1911
1912
|
// omit the entry and warn
|
|
1912
1913
|
warning('odata-enum-missing-value', path,
|
|
1913
|
-
{ name: enumSymbol, anno: '@
|
|
1914
|
+
{ name: enumSymbol, anno: '@Validation.AllowedValues', type: typeDef.type },
|
|
1914
1915
|
'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
|
|
1915
1916
|
}
|
|
1916
1917
|
}
|
|
@@ -1918,13 +1919,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1918
1919
|
else { // enumSymbolDef not found
|
|
1919
1920
|
// omit the entry and warn
|
|
1920
1921
|
warning('odata-enum-missing-value', path,
|
|
1921
|
-
{ name: enumSymbol, anno: '@
|
|
1922
|
+
{ name: enumSymbol, anno: '@Validation.AllowedValues', type: typeDef.type },
|
|
1922
1923
|
'Expected enum element $(NAME) of type $(TYPE) to have a value, not added to $(ANNO)');
|
|
1923
1924
|
}
|
|
1924
1925
|
|
|
1925
1926
|
// Can't rely that @description has already been renamed to @Core.Description
|
|
1926
1927
|
// Eval description according to precedence (doc comment must be considered already in Odata transformer
|
|
1927
|
-
// as in contrast to the other doc
|
|
1928
|
+
// as in contrast to the other doc comments as it is used to annotate the @Validation.AllowedValues)
|
|
1928
1929
|
const desc = enumSymbolDef ? enumSymbolDef['@Core.Description'] || enumSymbolDef['@description'] || enumSymbolDef.doc : undefined;
|
|
1929
1930
|
if (desc)
|
|
1930
1931
|
result['@Core.Description'] = desc;
|
|
@@ -1945,8 +1946,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1945
1946
|
|
|
1946
1947
|
if(!options.odataCapabilitiesPullup)
|
|
1947
1948
|
return;
|
|
1948
|
-
//
|
|
1949
|
-
if(rootContainer
|
|
1949
|
+
// @Capabilities is applicable to EntitySet/Collection only
|
|
1950
|
+
if(!rootContainer.$hasEntitySet)
|
|
1950
1951
|
return;
|
|
1951
1952
|
|
|
1952
1953
|
const isRecursiveContainment =
|
|
@@ -1994,7 +1995,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1994
1995
|
const { assoc, path } = entry;
|
|
1995
1996
|
const containee = assoc._target;
|
|
1996
1997
|
|
|
1997
|
-
if(isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
1998
|
+
if(edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
1998
1999
|
const localAssocPath = path.join('.');
|
|
1999
2000
|
let navPropEntry= localRestrictions.find(p =>
|
|
2000
2001
|
p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -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']
|
|
80
|
-
return singleton || (
|
|
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
|
|
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
|
|
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,
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -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
|
-
|
|
1
|
+
8c23b0ea94048e8e7a54d2ed4a4e4707
|