@sap/cds-compiler 4.6.0 → 4.7.4
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 +44 -0
- package/bin/cds_update_identifiers.js +6 -2
- package/bin/cdsc.js +1 -1
- package/doc/CHANGELOG_ARCHIVE.md +9 -9
- package/doc/CHANGELOG_BETA.md +6 -0
- package/lib/api/main.js +56 -9
- package/lib/api/options.js +6 -3
- package/lib/api/validate.js +20 -29
- package/lib/base/message-registry.js +27 -3
- package/lib/base/messages.js +8 -3
- package/lib/base/model.js +2 -0
- package/lib/checks/dbFeatureFlags.js +28 -0
- package/lib/checks/elements.js +81 -13
- package/lib/checks/enricher.js +3 -2
- package/lib/checks/validator.js +38 -4
- package/lib/compiler/assert-consistency.js +4 -4
- package/lib/compiler/checks.js +5 -4
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/generate.js +2 -1
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +3 -11
- package/lib/compiler/shared.js +2 -1
- package/lib/compiler/tweak-assocs.js +43 -24
- package/lib/edm/annotations/edmJson.js +3 -0
- package/lib/edm/annotations/genericTranslation.js +156 -106
- package/lib/edm/annotations/preprocessAnnotations.js +11 -14
- package/lib/edm/csn2edm.js +27 -24
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +135 -37
- package/lib/edm/edmUtils.js +20 -7
- package/lib/gen/Dictionary.json +2 -1
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -11
- package/lib/gen/languageParser.js +5942 -5446
- package/lib/json/to-csn.js +7 -114
- package/lib/language/genericAntlrParser.js +106 -48
- package/lib/model/cloneCsn.js +203 -0
- package/lib/model/csnRefs.js +11 -3
- package/lib/model/csnUtils.js +42 -85
- package/lib/optionProcessor.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +133 -88
- package/lib/render/toHdbcds.js +1 -5
- package/lib/render/toSql.js +7 -9
- package/lib/render/utils/common.js +9 -16
- package/lib/transform/addTenantFields.js +277 -102
- package/lib/transform/db/applyTransformations.js +14 -9
- package/lib/transform/db/backlinks.js +2 -1
- package/lib/transform/db/constraints.js +60 -82
- package/lib/transform/db/expansion.js +6 -6
- package/lib/transform/db/featureFlags.js +5 -0
- package/lib/transform/db/flattening.js +4 -4
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/rewriteCalculatedElements.js +2 -2
- package/lib/transform/db/transformExists.js +12 -0
- package/lib/transform/db/views.js +5 -2
- package/lib/transform/draft/odata.js +7 -6
- package/lib/transform/effective/associations.js +2 -1
- package/lib/transform/effective/main.js +3 -2
- package/lib/transform/effective/types.js +6 -3
- package/lib/transform/forOdata.js +39 -24
- package/lib/transform/forRelationalDB.js +34 -27
- package/lib/transform/localized.js +29 -9
- package/lib/transform/odata/flattening.js +419 -0
- package/lib/transform/odata/toFinalBaseType.js +95 -15
- package/lib/transform/odata/typesExposure.js +9 -7
- package/lib/transform/transformUtils.js +7 -6
- package/lib/transform/translateAssocsToJoins.js +3 -3
- package/lib/utils/objectUtils.js +14 -0
- package/package.json +1 -1
package/lib/edm/edm.js
CHANGED
|
@@ -664,7 +664,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
// Set the collection property if this is either an element, parameter or a term def
|
|
667
|
-
this
|
|
667
|
+
this.$isCollection = (csn.kind === undefined || csn.kind === 'annotation') ? csn.$isCollection : false;
|
|
668
668
|
|
|
669
669
|
if (options.whatsMySchemaName && this._edmAttributes[typeName]) {
|
|
670
670
|
const schemaName = options.whatsMySchemaName(this._edmAttributes[typeName]);
|
|
@@ -675,7 +675,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
675
675
|
// store undecorated type for JSON
|
|
676
676
|
this._type = this._edmAttributes[typeName];
|
|
677
677
|
// decorate for XML (not for Complex/EntityType)
|
|
678
|
-
if (this
|
|
678
|
+
if (this.$isCollection && this._edmAttributes[typeName])
|
|
679
679
|
this._edmAttributes[typeName] = `Collection(${this._edmAttributes[typeName]})`;
|
|
680
680
|
}
|
|
681
681
|
|
|
@@ -694,8 +694,8 @@ function getEdm( options, messageFunctions ) {
|
|
|
694
694
|
});
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
-
if (this
|
|
698
|
-
json.$Collection = this
|
|
697
|
+
if (this.$isCollection)
|
|
698
|
+
json.$Collection = this.$isCollection;
|
|
699
699
|
|
|
700
700
|
return json;
|
|
701
701
|
}
|
|
@@ -848,7 +848,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
848
848
|
|
|
849
849
|
setNullable() {
|
|
850
850
|
// From the Spec: In OData 4.01 responses a collection-valued property MUST specify a value for the Nullable attribute.
|
|
851
|
-
if (this
|
|
851
|
+
if (this.$isCollection)
|
|
852
852
|
this._edmAttributes.Nullable = !this.isNotNullable();
|
|
853
853
|
|
|
854
854
|
// Nullable=true is default, mention Nullable=false only in XML
|
|
@@ -1000,7 +1000,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
1000
1000
|
const [ src, tgt ] = edmUtils.determineMultiplicity(csn._constraints._partnerCsn || csn);
|
|
1001
1001
|
csn._constraints._multiplicity = csn._constraints._partnerCsn ? [ tgt, src ] : [ src, tgt ];
|
|
1002
1002
|
this._type = attributes.Type;
|
|
1003
|
-
this
|
|
1003
|
+
this.$isCollection = this.isToMany();
|
|
1004
1004
|
this._targetCsn = csn._target;
|
|
1005
1005
|
|
|
1006
1006
|
if (this.v4) {
|
|
@@ -1008,7 +1008,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
1008
1008
|
this._edmAttributes.Nullable = false;
|
|
1009
1009
|
|
|
1010
1010
|
// either csn has multiplicity or we have to use the multiplicity of the backlink
|
|
1011
|
-
if (this
|
|
1011
|
+
if (this.$isCollection) {
|
|
1012
1012
|
this._edmAttributes.Type = `Collection(${attributes.Type})`;
|
|
1013
1013
|
// attribute Nullable is not allowed in combination with Collection (see Spec)
|
|
1014
1014
|
// Even if min cardinality is > 0, remove Nullable, because the implicit OData contract
|
|
@@ -1082,7 +1082,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
1082
1082
|
return (nodeCsn.notNull === true && !nodeCsn.target || tgtCard.min > 0);
|
|
1083
1083
|
}
|
|
1084
1084
|
isToMany() {
|
|
1085
|
-
return (this
|
|
1085
|
+
return (this.$isCollection || this._csn._constraints._multiplicity[1] === '*');
|
|
1086
1086
|
}
|
|
1087
1087
|
|
|
1088
1088
|
toJSONattributes(json) {
|
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
5
5
|
const {
|
|
6
6
|
forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
7
|
-
isEdmPropertyRendered, getUtils,
|
|
8
|
-
isBuiltinType, applyTransformations,
|
|
7
|
+
isEdmPropertyRendered, getUtils,
|
|
8
|
+
isBuiltinType, applyTransformations, transformExpression, findAnnotationExpression,
|
|
9
|
+
cardinality2str, isMagicVariable,
|
|
9
10
|
} = require('../model/csnUtils');
|
|
10
11
|
const edmUtils = require('./edmUtils.js');
|
|
11
12
|
const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
|
|
12
13
|
const { inboundQualificationChecks } = require('./edmInboundChecks.js');
|
|
13
14
|
const typesExposure = require('../transform/odata/typesExposure');
|
|
14
15
|
const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
|
|
16
|
+
const { cloneCsnNonDict, cloneAnnotationValue } = require('../model/cloneCsn');
|
|
17
|
+
const { forEach, forEachKey } = require('../utils/objectUtils.js');
|
|
15
18
|
|
|
16
19
|
const NavResAnno = '@Capabilities.NavigationRestrictions.RestrictedProperties';
|
|
17
20
|
|
|
@@ -504,16 +507,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
504
507
|
setProp(parameterCsn, '_origin', entityCsn);
|
|
505
508
|
// create the Type Definition
|
|
506
509
|
// modify the original parameter entity with backlink and new name
|
|
507
|
-
if (csn.definitions[typeEntityName])
|
|
510
|
+
if (csn.definitions[typeEntityName])
|
|
508
511
|
error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: typeEntityName });
|
|
509
|
-
|
|
510
|
-
else {
|
|
511
|
-
csn.definitions[typeEntityName] = entityCsn;
|
|
512
|
-
reqDefs.definitions[typeEntityName] = entityCsn;
|
|
513
|
-
delete csn.definitions[entityCsn.name];
|
|
514
|
-
delete reqDefs.definitions[entityCsn.name];
|
|
512
|
+
else
|
|
515
513
|
entityCsn.name = typeEntityName;
|
|
516
|
-
}
|
|
517
514
|
setProp(entityCsn, '$entitySetName', typeEntitySetName);
|
|
518
515
|
// add backlink association
|
|
519
516
|
if (hasBacklink) {
|
|
@@ -526,15 +523,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
526
523
|
setProp(entityCsn.elements[typeToParameterAssocName], '_selfReferences', []);
|
|
527
524
|
setProp(entityCsn.elements[typeToParameterAssocName], '_target', parameterCsn);
|
|
528
525
|
setProp(entityCsn.elements[typeToParameterAssocName], '$path',
|
|
529
|
-
[ 'definitions',
|
|
530
|
-
|
|
531
|
-
// rewrite $path
|
|
532
|
-
if (entityCsn.$path)
|
|
533
|
-
entityCsn.$path[1] = typeEntityName;
|
|
534
|
-
forEachMemberRecursively(entityCsn, (member) => {
|
|
535
|
-
if (member.$path)
|
|
536
|
-
member.$path[1] = typeEntityName;
|
|
537
|
-
});
|
|
526
|
+
[ 'definitions', entityName, 'elements', typeToParameterAssocName ] );
|
|
538
527
|
}
|
|
539
528
|
|
|
540
529
|
/*
|
|
@@ -720,10 +709,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
720
709
|
// such that the VL annotations are distributed to the associations *before*
|
|
721
710
|
// FK creation.
|
|
722
711
|
// The FK creation already propagates the annotations from the association
|
|
723
|
-
const elements = construct.items
|
|
712
|
+
const elements = construct.items?.elements || construct.elements;
|
|
724
713
|
const assoc = elements[element['@odata.foreignKey4']];
|
|
725
714
|
if (assoc) {
|
|
726
|
-
Object.keys(assoc).filter(pn => pn[0] === '@' && !
|
|
715
|
+
Object.keys(assoc).filter(pn => pn[0] === '@' && !findAnnotationExpression(assoc, pn)).forEach((pn) => {
|
|
727
716
|
edmUtils.assignAnnotation(element, pn, assoc[pn]);
|
|
728
717
|
});
|
|
729
718
|
}
|
|
@@ -731,15 +720,16 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
731
720
|
if (options.isV2())
|
|
732
721
|
edmAnnoPreproc.setSAPSpecificV2AnnotationsToAssociation(element);
|
|
733
722
|
|
|
723
|
+
const absPath = $path2path(element.$path);
|
|
724
|
+
|
|
734
725
|
// initialize an association
|
|
735
726
|
if (element.target) {
|
|
736
727
|
// in case this is a forward assoc, store the backlink partners here, _selfReferences.length > 1 => error
|
|
737
728
|
edmUtils.assignProp(element, '_selfReferences', []);
|
|
738
729
|
edmUtils.assignProp(element._target, '$proxies', []);
|
|
739
730
|
// $abspath is used as partner path
|
|
740
|
-
edmUtils.assignProp(element, '$abspath',
|
|
731
|
+
edmUtils.assignProp(element, '$abspath', absPath);
|
|
741
732
|
}
|
|
742
|
-
|
|
743
733
|
// Collect keys
|
|
744
734
|
if (element.key)
|
|
745
735
|
keys[elementName] = element;
|
|
@@ -935,7 +925,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
935
925
|
const targetSchemaName = fqSchemaName.replace(`${srn}.`, '');
|
|
936
926
|
if (serviceRootNames.includes(targetSchemaName)) {
|
|
937
927
|
// remove all definitions starting with < fqSchemaName >. and add a schema reference
|
|
938
|
-
|
|
928
|
+
forEachKey(csn.definitions, (dn) => {
|
|
939
929
|
if (dn.startsWith(fqSchemaName)) {
|
|
940
930
|
delete csn.definitions[dn];
|
|
941
931
|
delete reqDefs.definitions[dn];
|
|
@@ -1075,7 +1065,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1075
1065
|
function muteNavProp( elt, msg = 'std' ) {
|
|
1076
1066
|
edmUtils.assignAnnotation(elt, '@odata.navigable', false);
|
|
1077
1067
|
if (elt._target['@cds.autoexpose'] !== false) {
|
|
1078
|
-
warning('odata-navigation',
|
|
1068
|
+
warning('odata-navigation', elt.$path,
|
|
1079
1069
|
{ target: elt._target.name, service: globalSchemaPrefix, '#': msg });
|
|
1080
1070
|
}
|
|
1081
1071
|
}
|
|
@@ -1118,7 +1108,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1118
1108
|
setProp(proxy, '$hasEntitySet', false);
|
|
1119
1109
|
setProp(proxy, '$exposedTypes', Object.create(null));
|
|
1120
1110
|
// copy all annotations of the target to the proxy
|
|
1121
|
-
|
|
1111
|
+
forEach(assoc._target, ( k, v ) => {
|
|
1122
1112
|
if (k[0] === '@' && k !== '@open')
|
|
1123
1113
|
proxy[k] = v;
|
|
1124
1114
|
});
|
|
@@ -1165,7 +1155,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1165
1155
|
}
|
|
1166
1156
|
else {
|
|
1167
1157
|
newElt = Object.create(null);
|
|
1168
|
-
|
|
1158
|
+
forEachKey(e, (prop) => {
|
|
1169
1159
|
newElt[prop] = e[prop];
|
|
1170
1160
|
});
|
|
1171
1161
|
}
|
|
@@ -1241,7 +1231,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1241
1231
|
if (typeClone) {
|
|
1242
1232
|
// Recurse into elements of 'type' (if any)
|
|
1243
1233
|
if (typeClone.elements) {
|
|
1244
|
-
|
|
1234
|
+
forEach(typeClone.elements, ( elemName, elem ) => {
|
|
1245
1235
|
// if this is a foreign key element, we must check whether or not the association
|
|
1246
1236
|
// has been exposed as proxy. If it has not been exposed, no further structured
|
|
1247
1237
|
// types must be exposed as 'Proxy_' types.
|
|
@@ -1298,10 +1288,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1298
1288
|
type['@open'] = typeDef['@open'];
|
|
1299
1289
|
|
|
1300
1290
|
if (typeDef.elements) {
|
|
1301
|
-
|
|
1291
|
+
forEach(typeDef.elements, ( elemName, elem ) => {
|
|
1302
1292
|
if (!elem.target) {
|
|
1303
1293
|
type.elements[elemName] = Object.create(null);
|
|
1304
|
-
|
|
1294
|
+
forEachKey(elem, (prop) => {
|
|
1305
1295
|
type.elements[elemName][prop] = elem[prop];
|
|
1306
1296
|
});
|
|
1307
1297
|
}
|
|
@@ -1478,7 +1468,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1478
1468
|
// followed by all namespaces that are potentially exposed by the exposed types
|
|
1479
1469
|
// don't forget to prepend the global namespace prefix
|
|
1480
1470
|
// schemas are ordered in csn2edm.js for each service
|
|
1481
|
-
|
|
1471
|
+
forEachKey(proxy.$exposedTypes, t => schemaSet.add(`${proxy.$globalSchemaPrefix}.${edmUtils.getSchemaPrefix(t)}`));
|
|
1482
1472
|
schemaSet.forEach((schemaName) => {
|
|
1483
1473
|
if (!schemas[schemaName]) {
|
|
1484
1474
|
schemas[schemaName] = { kind: 'schema', name: schemaName };
|
|
@@ -1490,7 +1480,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1490
1480
|
csn.definitions[fqProxyName] = proxy;
|
|
1491
1481
|
reqDefs.definitions[fqProxyName] = proxy;
|
|
1492
1482
|
setProp(proxy, '$path', [ 'definitions', fqProxyName ]);
|
|
1493
|
-
|
|
1483
|
+
forEach(proxy.$exposedTypes, ( tn, v ) => {
|
|
1494
1484
|
const fqtn = `${proxy.$globalSchemaPrefix}.${tn}`;
|
|
1495
1485
|
if (csn.definitions[fqtn] === undefined) {
|
|
1496
1486
|
csn.definitions[fqtn] = v;
|
|
@@ -1610,7 +1600,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1610
1600
|
elements = finalType?.elements || finalType?.items?.elements;
|
|
1611
1601
|
}
|
|
1612
1602
|
if (elements) {
|
|
1613
|
-
|
|
1603
|
+
forEach(elements, ( eltName, elt ) => {
|
|
1614
1604
|
if (!elt.$visited) {
|
|
1615
1605
|
setProp(elt, '$visited', true);
|
|
1616
1606
|
let newRefs = [];
|
|
@@ -1711,7 +1701,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1711
1701
|
'Edm.TimeOfDay': 1,
|
|
1712
1702
|
};
|
|
1713
1703
|
if (!(edmType in legalEdmTypes)) {
|
|
1714
|
-
|
|
1704
|
+
message('odata-spec-violation-key-type', location,
|
|
1715
1705
|
{
|
|
1716
1706
|
name: pathSegment, type: type.type, id: edmType, '#': pathSegment ? 'std' : 'scalar',
|
|
1717
1707
|
});
|
|
@@ -1909,6 +1899,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1909
1899
|
mapCdsToEdmProp(member);
|
|
1910
1900
|
annotateAllowedValues(member, location);
|
|
1911
1901
|
ComputedDefaultValue(member);
|
|
1902
|
+
rewriteAnnotationExpressions(member);
|
|
1912
1903
|
if (member.returns) {
|
|
1913
1904
|
edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
|
|
1914
1905
|
markCollection(member.returns);
|
|
@@ -1921,7 +1912,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1921
1912
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
1922
1913
|
if (items) {
|
|
1923
1914
|
edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : true);
|
|
1924
|
-
edmUtils.assignProp(obj, '
|
|
1915
|
+
edmUtils.assignProp(obj, '$isCollection', true);
|
|
1925
1916
|
}
|
|
1926
1917
|
}
|
|
1927
1918
|
|
|
@@ -2029,6 +2020,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2029
2020
|
if (npe.NavigationProperty &&
|
|
2030
2021
|
npe.NavigationProperty['='] &&
|
|
2031
2022
|
typeof npe.NavigationProperty['='] === 'string') {
|
|
2023
|
+
// TODO: replace with transformExpression
|
|
2032
2024
|
applyTransformations({ definitions: { npe } }, {
|
|
2033
2025
|
'=': (parent, prop, value) => {
|
|
2034
2026
|
parent[prop] = prefix.concat(value).join('.');
|
|
@@ -2146,12 +2138,12 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2146
2138
|
const edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
2147
2139
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2148
2140
|
}
|
|
2149
|
-
else if (obj
|
|
2141
|
+
else if (obj.$isCollection && (obj.items && isBuiltinType(csnUtils.getFinalTypeInfo(obj.items.type)?.type))) {
|
|
2150
2142
|
const edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType'], obj.$path);
|
|
2151
2143
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2152
2144
|
}
|
|
2153
2145
|
// This is the special case when we have array of array, but will not be supported in the future
|
|
2154
|
-
else if (obj
|
|
2146
|
+
else if (obj.$isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(csnUtils.getFinalTypeInfo(obj.items.items.type)?.type)) {
|
|
2155
2147
|
const edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
2156
2148
|
edmUtils.assignProp(obj, '_edmType', edmType);
|
|
2157
2149
|
}
|
|
@@ -2175,6 +2167,112 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2175
2167
|
edmUtils.assignAnnotation(member, '@Core.ComputedDefaultValue', true);
|
|
2176
2168
|
}
|
|
2177
2169
|
}
|
|
2170
|
+
|
|
2171
|
+
function rewriteAnnotationExpressions( carrier ) {
|
|
2172
|
+
// rewrite annotation expression paths such that they are defined against the definition
|
|
2173
|
+
const absPath = $path2path(carrier.$path);
|
|
2174
|
+
let isSubTreeSpan = true;
|
|
2175
|
+
const rootPrefix = absPath.slice(1, absPath.length - 1);
|
|
2176
|
+
|
|
2177
|
+
const isExprAnno = (obj, pn) => isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
|
|
2178
|
+
|
|
2179
|
+
const subTreeSpan = {
|
|
2180
|
+
ref: (parent, _prop, xpr) => {
|
|
2181
|
+
const head = xpr[0].id || xpr[0];
|
|
2182
|
+
if (head === '$self' || parent.param) {
|
|
2183
|
+
let j = parent.param ? 0 : 1;
|
|
2184
|
+
for (let i = 1; i < absPath.length - 1 && isSubTreeSpan; i++, j++)
|
|
2185
|
+
isSubTreeSpan = (xpr[j].id || xpr[j]) === absPath[i];
|
|
2186
|
+
}
|
|
2187
|
+
},
|
|
2188
|
+
};
|
|
2189
|
+
// this element was not a top level element before type exposure
|
|
2190
|
+
// rectify all absolute annotation paths inside
|
|
2191
|
+
const relativize = {
|
|
2192
|
+
ref: (parent, _prop, xpr) => {
|
|
2193
|
+
const head = xpr[0].id || xpr[0];
|
|
2194
|
+
let absPathPrefixEqual = true;
|
|
2195
|
+
if (head === '$self' || parent.param) {
|
|
2196
|
+
let j = parent.param ? 0 : 1;
|
|
2197
|
+
for (let i = 1; i < absPath.length - 1 && absPathPrefixEqual; i++, j++)
|
|
2198
|
+
absPathPrefixEqual = (xpr[j].id || xpr[j]) === absPath[i];
|
|
2199
|
+
|
|
2200
|
+
if (absPathPrefixEqual)
|
|
2201
|
+
// remove prefix between $self and leaf element name
|
|
2202
|
+
// or starting with the parameter name
|
|
2203
|
+
xpr.splice(parent.param ? 0 : 1, absPath.length - 2);
|
|
2204
|
+
}
|
|
2205
|
+
},
|
|
2206
|
+
};
|
|
2207
|
+
|
|
2208
|
+
const absolutize = {
|
|
2209
|
+
ref: (parent, prop, xpr) => {
|
|
2210
|
+
const head = xpr[0].id || xpr[0];
|
|
2211
|
+
if (head !== '$self' && !parent.param && !isMagicVariable(head)) {
|
|
2212
|
+
parent[prop] = [ ...rootPrefix, ...xpr ];
|
|
2213
|
+
if (absolutize.scope === 'params')
|
|
2214
|
+
parent.param = true;
|
|
2215
|
+
else
|
|
2216
|
+
parent[prop].unshift('$self');
|
|
2217
|
+
}
|
|
2218
|
+
},
|
|
2219
|
+
};
|
|
2220
|
+
|
|
2221
|
+
if (absPath.length > 2) {
|
|
2222
|
+
const [ xprANames, nxprANames ] = Object.keys(carrier).reduce((acc, pn) => {
|
|
2223
|
+
if (pn[0] === '@')
|
|
2224
|
+
acc[isExprAnno(carrier, pn) ? 0 : 1].push(pn);
|
|
2225
|
+
return acc;
|
|
2226
|
+
}, [ [], [] ]);
|
|
2227
|
+
xprANames.forEach((xprAName) => {
|
|
2228
|
+
isSubTreeSpan = true;
|
|
2229
|
+
transformExpression(carrier, xprAName, subTreeSpan);
|
|
2230
|
+
if (isSubTreeSpan) {
|
|
2231
|
+
transformExpression(carrier, xprAName, relativize);
|
|
2232
|
+
}
|
|
2233
|
+
else {
|
|
2234
|
+
const scope = carrier.$path[2];
|
|
2235
|
+
absolutize.scope = scope;
|
|
2236
|
+
transformExpression(carrier, xprAName, absolutize);
|
|
2237
|
+
const def = csn.definitions[absPath[0]];
|
|
2238
|
+
const eltPath = absPath.slice(1).join('/');
|
|
2239
|
+
const proxyDict = scope === 'elements' ? '$eltAnnoProxies' : '$paramAnnoProxies';
|
|
2240
|
+
|
|
2241
|
+
if (!def[proxyDict])
|
|
2242
|
+
setProp(def, proxyDict, Object.create(null));
|
|
2243
|
+
let eltProxy = def[proxyDict][eltPath];
|
|
2244
|
+
if (!eltProxy) {
|
|
2245
|
+
eltProxy = Object.create(null);
|
|
2246
|
+
// these attributes are needed to test for
|
|
2247
|
+
// Property, Parameter, NavigationProperty, Collection
|
|
2248
|
+
// Applicability
|
|
2249
|
+
[ 'target',
|
|
2250
|
+
'cardinality',
|
|
2251
|
+
'keys',
|
|
2252
|
+
'$isCollection',
|
|
2253
|
+
'$appliesToReturnType',
|
|
2254
|
+
'$path',
|
|
2255
|
+
'@cds.api.ignore',
|
|
2256
|
+
'@odata.navigable' ].forEach((prop) => {
|
|
2257
|
+
if (carrier[prop] != null)
|
|
2258
|
+
setProp(eltProxy, prop, carrier[prop]);
|
|
2259
|
+
});
|
|
2260
|
+
|
|
2261
|
+
Object.keys(carrier).filter(pn => pn[0] !== '@').forEach((pn) => {
|
|
2262
|
+
eltProxy[pn] = carrier[pn];
|
|
2263
|
+
});
|
|
2264
|
+
def[proxyDict][eltPath] = eltProxy;
|
|
2265
|
+
}
|
|
2266
|
+
eltProxy[xprAName] = carrier[xprAName];
|
|
2267
|
+
carrier[xprAName] = null;
|
|
2268
|
+
nxprANames.filter(an => an.startsWith(`${xprAName}.`)).forEach((nxprAName) => {
|
|
2269
|
+
eltProxy[nxprAName] = carrier[nxprAName];
|
|
2270
|
+
carrier[nxprAName] = null;
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2178
2276
|
}
|
|
2179
2277
|
|
|
2180
2278
|
module.exports = {
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp } = require('../base/model');
|
|
3
|
+
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
isBuiltinType, isEdmPropertyRendered, applyTransformations,
|
|
5
|
+
isBuiltinType, isEdmPropertyRendered, applyTransformations,
|
|
6
6
|
} = require('../model/csnUtils');
|
|
7
7
|
const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
|
|
8
8
|
const { CompilerAssertion } = require('../base/error');
|
|
9
|
+
const { cloneAnnotationValue } = require('../model/cloneCsn');
|
|
9
10
|
|
|
10
11
|
/* eslint max-statements-per-line:off */
|
|
11
12
|
function validateOptions( _options ) {
|
|
@@ -31,6 +32,19 @@ function validateOptions( _options ) {
|
|
|
31
32
|
|
|
32
33
|
options.pathDelimiter = options.isStructFormat ? '/' : '_';
|
|
33
34
|
|
|
35
|
+
if (isBetaEnabled(options, 'v5preview')) {
|
|
36
|
+
if (!options.severities)
|
|
37
|
+
options.severities = {};
|
|
38
|
+
options.severities['odata-spec-violation-array'] ??= 'Error';
|
|
39
|
+
options.severities['odata-spec-violation-assoc'] ??= 'Error';
|
|
40
|
+
options.severities['odata-spec-violation-namespace'] ??= 'Error';
|
|
41
|
+
options.severities['odata-spec-violation-param'] ??= 'Error';
|
|
42
|
+
options.severities['odata-spec-violation-returns'] ??= 'Error';
|
|
43
|
+
options.severities['odata-spec-violation-type-unknown'] ??= 'Error';
|
|
44
|
+
options.severities['odata-spec-violation-no-key'] ??= 'Error';
|
|
45
|
+
options.severities['odata-spec-violation-key-type'] ??= 'Error';
|
|
46
|
+
options.severities['odata-spec-violation-property-name'] ??= 'Error';
|
|
47
|
+
}
|
|
34
48
|
return options;
|
|
35
49
|
}
|
|
36
50
|
return _options;
|
|
@@ -575,15 +589,14 @@ function addTypeFacets( node, csn ) {
|
|
|
575
589
|
const decimalTypes = { 'cds.Decimal': 1, 'cds.DecimalFloat': 1, 'cds.hana.SMALLDECIMAL': 1 };
|
|
576
590
|
if (csn.length != null)
|
|
577
591
|
node.setEdmAttribute('MaxLength', csn.length);
|
|
578
|
-
if (csn.scale !== undefined)
|
|
579
|
-
node.setEdmAttribute('Scale', csn.scale);
|
|
580
|
-
// else if (csn.type === 'cds.hana.SMALLDECIMAL' && !isV2)
|
|
581
|
-
// node._edmAttributes.Scale = 'floating';
|
|
582
|
-
|
|
583
592
|
if (csn.precision != null)
|
|
584
593
|
node.setEdmAttribute('Precision', csn.precision);
|
|
585
594
|
// else if (csn.type === 'cds.hana.SMALLDECIMAL' && !isV2)
|
|
586
595
|
// node.Precision = 16;
|
|
596
|
+
if (csn.scale !== undefined)
|
|
597
|
+
node.setEdmAttribute('Scale', csn.scale);
|
|
598
|
+
// else if (csn.type === 'cds.hana.SMALLDECIMAL' && !isV2)
|
|
599
|
+
// node._edmAttributes.Scale = 'floating';
|
|
587
600
|
else if (csn.type === 'cds.Timestamp' && node._edmAttributes.Type === 'Edm.DateTimeOffset')
|
|
588
601
|
node.setEdmAttribute('Precision', 7);
|
|
589
602
|
if (csn.type in decimalTypes) {
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -2690,7 +2690,7 @@
|
|
|
2690
2690
|
"Capabilities.PermissionType": {
|
|
2691
2691
|
"$kind": "ComplexType",
|
|
2692
2692
|
"Properties": {
|
|
2693
|
-
"SchemeName": "
|
|
2693
|
+
"SchemeName": "Authorization.SchemeName",
|
|
2694
2694
|
"Scopes": "Collection(Capabilities.ScopeType)"
|
|
2695
2695
|
}
|
|
2696
2696
|
},
|
|
@@ -3594,6 +3594,7 @@
|
|
|
3594
3594
|
"$kind": "ComplexType",
|
|
3595
3595
|
"Properties": {
|
|
3596
3596
|
"ChangeNextSiblingAction": "Common.QualifiedName",
|
|
3597
|
+
"ChangeSiblingForRootsSupported": "Edm.Boolean",
|
|
3597
3598
|
"CopyAction": "Common.QualifiedName"
|
|
3598
3599
|
}
|
|
3599
3600
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
37b3697769a5035b6409cd959ebdf610
|