@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.
- package/CHANGELOG.md +107 -4
- 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 +55 -9
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +143 -64
- 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 +41 -5
- package/lib/edm/csn2edm.js +34 -32
- 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 +25 -18
- 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 +157 -112
- 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/model/sortViews.js +4 -2
- 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 +62 -47
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +4 -2
- 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/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
|
|
@@ -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)
|
|
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 &&
|
|
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(!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: '@
|
|
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: '@
|
|
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
|
|
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
|
-
//
|
|
1949
|
-
if(rootContainer
|
|
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('.'));
|
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
|