@sap/cds-compiler 3.5.4 → 3.6.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 +65 -2
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +28 -13
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +37 -40
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +32 -4
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +22 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +31 -22
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +2 -14
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +4 -6
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +13 -7
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +59 -41
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
|
@@ -33,7 +33,7 @@ const capabilities = Object.keys(require('../gen/Dictionary.json').
|
|
|
33
33
|
function initializeModel(csn, _options, messageFunctions, requestedServiceNames=undefined)
|
|
34
34
|
{
|
|
35
35
|
const { info, warning, error, message } = messageFunctions;
|
|
36
|
-
|
|
36
|
+
const special$self = !csn?.definitions?.$self && '$self';
|
|
37
37
|
const csnUtils = getUtils(csn);
|
|
38
38
|
|
|
39
39
|
// proxies are merged into the final model after all proxy elements are collected
|
|
@@ -68,7 +68,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
68
68
|
|
|
69
69
|
// Structural CSN inbound QA checks
|
|
70
70
|
inboundQualificationChecks(csn, options, messageFunctions,
|
|
71
|
-
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName);
|
|
71
|
+
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName, csnUtils);
|
|
72
72
|
// not needed at the moment
|
|
73
73
|
// resolveForeignKeyRefs();
|
|
74
74
|
|
|
@@ -101,7 +101,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
101
101
|
/*
|
|
102
102
|
Enrich the CSN by de-anonymizing and exposing types that are required to make the service self contained.
|
|
103
103
|
Type exposure will add additional schema contexts and group the exposed types in these contexts.
|
|
104
|
-
|
|
104
|
+
Contexts either represent another service (if the type to be exposed resides in that
|
|
105
105
|
service), the namespace (including (sub-)contexts) or as last resort (if the type name
|
|
106
106
|
has no prefix path) a 'root' namespace.
|
|
107
107
|
*/
|
|
@@ -430,7 +430,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
430
430
|
[ 'definitions', containee.name ], true, { elementsOnly: true });
|
|
431
431
|
}
|
|
432
432
|
else if(elt.type && !elt.elements) {
|
|
433
|
-
|
|
433
|
+
// try to find elements to drill down further
|
|
434
434
|
while(elt && !isBuiltinType(elt.type) && !elt.elements) {
|
|
435
435
|
elt = csn.definitions[elt.type];
|
|
436
436
|
}
|
|
@@ -599,7 +599,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
599
599
|
});
|
|
600
600
|
linkAssociationTarget(parameterCsn);
|
|
601
601
|
initContainments(parameterCsn);
|
|
602
|
-
|
|
602
|
+
// add assoc to result set, FIXME: is the cardinality correct?
|
|
603
603
|
if(!isProxy) {
|
|
604
604
|
parameterCsn.elements[parameterToTypeAssocName] = {
|
|
605
605
|
'@odata.contained': true,
|
|
@@ -654,7 +654,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
654
654
|
// convert $path to path starting at main artifact
|
|
655
655
|
function $path2path(p) {
|
|
656
656
|
const path = [];
|
|
657
|
-
/** @type {
|
|
657
|
+
/** @type {any} */
|
|
658
658
|
let env = csn;
|
|
659
659
|
for (let i = 0; p && env && i < p.length; i++) {
|
|
660
660
|
const ps = p[i];
|
|
@@ -704,7 +704,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
704
704
|
element[attrName] = attr;
|
|
705
705
|
}
|
|
706
706
|
}
|
|
707
|
-
// and eventually remove some afterwards
|
|
707
|
+
// and eventually remove some afterwards
|
|
708
708
|
if(options.isV2())
|
|
709
709
|
edmAnnoPreproc.setSAPSpecificV2AnnotationsToAssociation(element);
|
|
710
710
|
|
|
@@ -789,9 +789,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
789
789
|
Do not render (ignore) elements as properties
|
|
790
790
|
In V4:
|
|
791
791
|
1) If this is a foreign key of an association to a container which *is* used
|
|
792
|
-
to establish the containment via composition and $self comparison
|
|
793
|
-
|
|
794
|
-
|
|
792
|
+
to establish the containment via composition and $self comparison.
|
|
793
|
+
The $self comparison can only be evaluated after the ON conditions have been
|
|
794
|
+
parsed in prepareConstraints().
|
|
795
795
|
2) For all other foreign keys let isEdmPropertyRendered() decide.
|
|
796
796
|
3) If an element/association is annotated with @odata.containment.ignore and containment is
|
|
797
797
|
active, assign @cds.api.ignore or @odata.navigable: false
|
|
@@ -817,8 +817,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
817
817
|
if(assoc)
|
|
818
818
|
isContainerAssoc = !!(assoc._isToContainer && assoc._selfReferences.length || assoc['@odata.contained']);
|
|
819
819
|
/*
|
|
820
|
-
|
|
821
|
-
|
|
820
|
+
If this foreign key is NOT a container fk, let isEdmPropertyRendered() decide
|
|
821
|
+
Else, if fk is container fk, omit it if it wasn't requested in structured mode
|
|
822
822
|
*/
|
|
823
823
|
if((!isContainerAssoc && !isEdmPropertyRendered(element, options)) ||
|
|
824
824
|
(isContainerAssoc && !options.renderForeignKeys))
|
|
@@ -829,7 +829,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
829
829
|
delete struct.$keys[element.name];
|
|
830
830
|
}
|
|
831
831
|
}
|
|
832
|
-
// deprecated._unmanagedUpInComponent:
|
|
833
832
|
// Only in containment:
|
|
834
833
|
// Ignore this (foreign key) elment if renderForeignKeys is false
|
|
835
834
|
if(options.odataContainment && element['@odata.containment.ignore']) {
|
|
@@ -842,8 +841,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
842
841
|
}
|
|
843
842
|
// it's an association
|
|
844
843
|
else if(element['@odata.containment.ignore'] && options.odataContainment && !options.renderForeignKeys) {
|
|
845
|
-
|
|
846
|
-
|
|
844
|
+
// if this is an explicitly containment ignore tagged association,
|
|
845
|
+
// ignore it if option odataContainment is true and no foreign keys should be rendered
|
|
847
846
|
edmUtils.assignAnnotation(element, '@odata.navigable', false);
|
|
848
847
|
}
|
|
849
848
|
}, [], true, { elementsOnly: true });
|
|
@@ -865,20 +864,20 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
865
864
|
edmUtils.finalizeReferentialConstraints(csn, element, options, info);
|
|
866
865
|
|
|
867
866
|
if(element._constraints._partnerCsn && element.cardinality && element.cardinality.max) {
|
|
868
|
-
|
|
867
|
+
// if this is a partnership and this assoc has a set target cardinality, assign it as source cardinality to the partner
|
|
869
868
|
if(element._constraints._partnerCsn.cardinality) {
|
|
870
|
-
|
|
871
|
-
|
|
869
|
+
// if the forward association has set a src cardinality and it deviates from the backlink target cardinality raise a warning
|
|
870
|
+
// in V2 only, in V4 the source cardinality is rendered implicitly at the Type property
|
|
872
871
|
if(element._constraints._partnerCsn.cardinality.src) {
|
|
873
872
|
let srcMult = (element._constraints._partnerCsn.cardinality.src == 1) ? '0..1' : '*';
|
|
874
873
|
let newMult = (element.cardinality.max > 1) ? '*' : '0..1';
|
|
875
874
|
if(options.isV2() && srcMult !== newMult) {
|
|
876
|
-
|
|
875
|
+
// Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
|
|
877
876
|
warning(null, null, `Source cardinality "${element._constraints._partnerCsn.cardinality.src}" of "${element._constraints._partnerCsn._parent.name}/${element._constraints._partnerCsn.name}" conflicts with target cardinality "${element.cardinality.max}" of association "${element._parent.name}/${element.name}"`);
|
|
878
877
|
}
|
|
879
878
|
}
|
|
880
879
|
else {
|
|
881
|
-
|
|
880
|
+
// .. but only if the original assoc hasn't set src yet
|
|
882
881
|
element._constraints._partnerCsn.cardinality.src = element.cardinality.max;
|
|
883
882
|
}
|
|
884
883
|
}
|
|
@@ -900,7 +899,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
900
899
|
if(fqSchemaName.startsWith(srn + '.')) {
|
|
901
900
|
const targetSchemaName = fqSchemaName.replace(srn + '.', '');
|
|
902
901
|
if(serviceRootNames.includes(targetSchemaName)) {
|
|
903
|
-
|
|
902
|
+
// remove all definitions starting with < fqSchemaName >. and add a schema reference
|
|
904
903
|
Object.keys(csn.definitions).forEach(dn => {
|
|
905
904
|
if(dn.startsWith(fqSchemaName)) {
|
|
906
905
|
delete csn.definitions[dn];
|
|
@@ -1007,7 +1006,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1007
1006
|
// and this association is not a good one, don't expose this association.
|
|
1008
1007
|
muteNavProp(element, 'oncond');
|
|
1009
1008
|
return;
|
|
1010
|
-
}
|
|
1009
|
+
}
|
|
1011
1010
|
if(proxy) {
|
|
1012
1011
|
// if a proxy was either already created or could be created and
|
|
1013
1012
|
// if it's a 'real' proxy, link the _target to it and remove constraints
|
|
@@ -1170,45 +1169,45 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1170
1169
|
|
|
1171
1170
|
// Always expose types referred to by a proxy, never reuse an eventually existing type
|
|
1172
1171
|
// as the nested elements must all be not nullable
|
|
1173
|
-
|
|
1172
|
+
// elements have precedence over type
|
|
1174
1173
|
const typeDef = !node.elements && node.type ? csn.definitions[node.type] : node;
|
|
1175
1174
|
|
|
1176
1175
|
if (typeDef) {
|
|
1177
1176
|
let typeClone;
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1177
|
+
// the type clone must be produced for each service as this type may
|
|
1178
|
+
// produce references and/or proxies into multiple services
|
|
1179
|
+
// (but only once per service, therefore cache it).
|
|
1181
1180
|
if(typeDef.$proxyTypes && typeDef.$proxyTypes[globalSchemaPrefix]) {
|
|
1182
1181
|
// if type has been exposed in a schema use this type
|
|
1183
1182
|
typeClone = typeDef.$proxyTypes[globalSchemaPrefix];
|
|
1184
1183
|
}
|
|
1185
1184
|
else {
|
|
1186
|
-
|
|
1185
|
+
// Set the correct name
|
|
1187
1186
|
let typeId = artificialName; // the artificialName has no namespace, it's the element
|
|
1188
1187
|
if(node.type) {
|
|
1189
|
-
|
|
1188
|
+
// same as for proxies, use schema or namespace, 'root' is last resort
|
|
1190
1189
|
typeSchemaName = typeDef.$mySchemaName || edmUtils.getSchemaPrefix(node.type);
|
|
1191
1190
|
typeId = node.type.replace(typeSchemaName + '.', '').replace(/\./g, '_');
|
|
1192
|
-
|
|
1191
|
+
// strip the service root of that type (if any)
|
|
1193
1192
|
const myServiceRootName = whatsMyServiceRootName(typeSchemaName);
|
|
1194
1193
|
if(myServiceRootName)
|
|
1195
1194
|
typeSchemaName = typeSchemaName.replace(myServiceRootName + '.', '');
|
|
1196
1195
|
}
|
|
1197
1196
|
|
|
1198
1197
|
if(edmUtils.isStructuredArtifact(typeDef)) {
|
|
1199
|
-
|
|
1200
|
-
|
|
1198
|
+
// pull forceNotNull to false for named types and non-key nodes
|
|
1199
|
+
// only toplevel nodes (elements) can be key
|
|
1201
1200
|
forceToNotNull = !!(forceToNotNull && isKey && node.elements && !node.type);
|
|
1202
1201
|
|
|
1203
1202
|
typeClone = cloneStructTypeForProxy(typeSchemaName, `${typeSchemaName}.${typeId}`, typeDef);
|
|
1204
1203
|
if(typeClone) {
|
|
1205
|
-
|
|
1204
|
+
// Recurse into elements of 'type' (if any)
|
|
1206
1205
|
typeClone.elements && Object.entries(typeClone.elements).forEach(([elemName, elem]) => {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1206
|
+
// if this is a foreign key elment, we must check whether or not the association
|
|
1207
|
+
// has been exposed as proxy. If it has not been exposed, no further structured
|
|
1208
|
+
// types must be exposed as 'Proxy_' types.
|
|
1210
1209
|
|
|
1211
|
-
|
|
1210
|
+
// TODO: expose types of assoc.keys and don't rely on exposed foreign keys
|
|
1212
1211
|
if(!elem['@odata.foreignKey4'] ||
|
|
1213
1212
|
(elem['@odata.foreignKey4'] && !typeClone.elements[elem['@odata.foreignKey4']].$exposed))
|
|
1214
1213
|
exposeStructTypeForProxyOf(proxy, elem, `${typeId}_${elemName}`,
|
|
@@ -1220,26 +1219,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1220
1219
|
}
|
|
1221
1220
|
}
|
|
1222
1221
|
else {
|
|
1223
|
-
|
|
1222
|
+
// FUTURE: expose scalar type definition as well
|
|
1224
1223
|
}
|
|
1225
1224
|
}
|
|
1226
1225
|
if(typeClone) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1226
|
+
// register the type clone at the proxy
|
|
1227
|
+
// Reminder: Each proxy receives a full set of type clones, even if the types are shared
|
|
1228
|
+
// (no scattered type clone caching). registerProxy() checks if a clone needs to be added to
|
|
1229
|
+
// csn.definitions.
|
|
1231
1230
|
proxy.$exposedTypes[typeClone.name] = typeClone;
|
|
1232
1231
|
|
|
1233
|
-
|
|
1232
|
+
// set the node's new type name
|
|
1234
1233
|
node.type = typeClone.name;
|
|
1235
|
-
|
|
1236
|
-
|
|
1234
|
+
// the key path generator must use the type clone directly, because it can't resolve
|
|
1235
|
+
// the type clone in the CSN (its name is the final name and not the definition name).
|
|
1237
1236
|
setProp(node, '_type', typeClone);
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1237
|
+
// Hack alert:
|
|
1238
|
+
// beta feature 'subElemRedirections' (now the default in v2) adds elements to the node by
|
|
1239
|
+
// default, without we must do it to get the primary key tuple calculation correct.
|
|
1240
|
+
// Remember: node.type is the service local type name (not prepended by the service name),
|
|
1241
|
+
// so it can't be resolved in definitions later on
|
|
1243
1242
|
if(typeClone.elements)
|
|
1244
1243
|
node.elements = typeClone.elements;
|
|
1245
1244
|
}
|
|
@@ -1263,7 +1262,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1263
1262
|
Object.keys(elem).forEach(prop => type.elements[elemName][prop] = elem[prop])
|
|
1264
1263
|
}
|
|
1265
1264
|
else if(elem.keys && !elem.on) {
|
|
1266
|
-
|
|
1265
|
+
// a primary key can never be an unmanaged association
|
|
1267
1266
|
type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
|
|
1268
1267
|
}
|
|
1269
1268
|
if(forceToNotNull)
|
|
@@ -1436,7 +1435,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1436
1435
|
schemaNames.push(schemaName);
|
|
1437
1436
|
}
|
|
1438
1437
|
});
|
|
1439
|
-
/** @type {object} */
|
|
1440
1438
|
const alreadyRegistered = csn.definitions[fqProxyName];
|
|
1441
1439
|
if(!alreadyRegistered) {
|
|
1442
1440
|
csn.definitions[fqProxyName] = proxy;
|
|
@@ -1476,7 +1474,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1476
1474
|
// don't error on duplicate schemas, if it's already present then all is good....
|
|
1477
1475
|
}
|
|
1478
1476
|
});
|
|
1479
|
-
// sort the global schemaNames array
|
|
1480
1477
|
schemaNames.sort((a,b) => b.length-a.length);
|
|
1481
1478
|
|
|
1482
1479
|
function finalizeProxyContainments(proxy) {
|
|
@@ -1526,12 +1523,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1526
1523
|
if(!options.renderForeignKeys || (options.renderForeignKeys && !k.target))
|
|
1527
1524
|
def.$edmKeyPaths.push(...produceKeyRefPaths(k, kn, [ 'definitions', defName, 'elements', kn ]));
|
|
1528
1525
|
}
|
|
1529
|
-
|
|
1526
|
+
// In v2/v4 flat, associations are never rendered
|
|
1530
1527
|
else if(!k.target) {
|
|
1531
1528
|
def.$edmKeyPaths.push([kn]);
|
|
1532
1529
|
}
|
|
1533
1530
|
// check toplevel key for spec violations
|
|
1534
|
-
checkKeySpecViolations(k, ['definitions',
|
|
1531
|
+
checkKeySpecViolations(k, ['definitions', defName, 'elements', k.name]);
|
|
1535
1532
|
}
|
|
1536
1533
|
});
|
|
1537
1534
|
}
|
|
@@ -1558,10 +1555,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1558
1555
|
}
|
|
1559
1556
|
// OData requires all elements along the path to be nullable: false (that is either key or notNull)
|
|
1560
1557
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1558
|
+
let elements = eltCsn.elements || eltCsn.items?.elements;
|
|
1559
|
+
if (!elements) {
|
|
1560
|
+
const finalType = csnUtils.getFinalBaseTypeWithProps(eltCsn.items?.type || eltCsn.type);
|
|
1561
|
+
elements = finalType?.elements || finalType?.items?.elements;
|
|
1562
|
+
}
|
|
1563
|
+
if (elements) {
|
|
1565
1564
|
Object.entries(elements).forEach(([eltName, elt]) => {
|
|
1566
1565
|
if(!elt.$visited) {
|
|
1567
1566
|
setProp(elt, '$visited', true);
|
|
@@ -1616,9 +1615,15 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1616
1615
|
message('odata-spec-violation-key-null', location,
|
|
1617
1616
|
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1618
1617
|
}
|
|
1619
|
-
// many
|
|
1620
|
-
|
|
1618
|
+
// many is either directly on elements or on the type
|
|
1619
|
+
// due to added proxy types it might be that the type can't be found in definitions
|
|
1620
|
+
let type = elt.items ||
|
|
1621
|
+
(elt.type &&
|
|
1622
|
+
!isBuiltinType(elt.type) &&
|
|
1623
|
+
csn.definitions[elt.type] &&
|
|
1624
|
+
csnUtils.getFinalBaseTypeWithProps(elt.type).items);
|
|
1621
1625
|
if(type) {
|
|
1626
|
+
// many primary key can be induced by a many parameter of a view
|
|
1622
1627
|
message('odata-spec-violation-key-array', location,
|
|
1623
1628
|
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1624
1629
|
}
|
|
@@ -1904,13 +1909,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1904
1909
|
if(rootContainer.kind !== 'entity' || rootContainer.$proxy)
|
|
1905
1910
|
return;
|
|
1906
1911
|
|
|
1907
|
-
const isRecursiveContainment =
|
|
1908
|
-
!!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
1912
|
+
const isRecursiveContainment =
|
|
1913
|
+
!!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
|
|
1909
1914
|
rootContainer.$containerNames.length === 1 &&
|
|
1910
1915
|
rootContainer.$containeeAssociations.some(entry => rootContainer.$containerNames.includes(entry.assoc.target)));
|
|
1911
1916
|
|
|
1912
1917
|
// Root nodes are not contained
|
|
1913
|
-
const isRootNode =
|
|
1918
|
+
const isRootNode =
|
|
1914
1919
|
!!(!rootContainer.$containerNames ||
|
|
1915
1920
|
rootContainer.$containerNames && rootContainer.$containerNames.length === 0);
|
|
1916
1921
|
|
|
@@ -1921,7 +1926,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1921
1926
|
addContainmentAnnotationsRecursively([], rootContainer);
|
|
1922
1927
|
if(rootRestrictions.length)
|
|
1923
1928
|
rootContainer[NavResAnno] = rootRestrictions;
|
|
1924
|
-
|
|
1929
|
+
|
|
1925
1930
|
function addContainmentAnnotationsRecursively(prefix, container) {
|
|
1926
1931
|
if(container.$containeeAssociations) {
|
|
1927
1932
|
// copy or create container restrictions, don't modify original
|
|
@@ -1951,9 +1956,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1951
1956
|
|
|
1952
1957
|
if(isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
1953
1958
|
const localAssocPath = path.join('.');
|
|
1954
|
-
let navPropEntry
|
|
1955
|
-
|
|
1956
|
-
|
|
1959
|
+
let navPropEntry= localRestrictions.find(p =>
|
|
1960
|
+
p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
|
|
1961
|
+
const hasEntry = !!navPropEntry;
|
|
1957
1962
|
|
|
1958
1963
|
if(!hasEntry) {
|
|
1959
1964
|
navPropEntry = { NavigationProperty: { '=': prefix.concat(localAssocPath).join('.') } };
|
|
@@ -2008,13 +2013,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
2008
2013
|
}
|
|
2009
2014
|
|
|
2010
2015
|
function iterateParams(def, loc) {
|
|
2011
|
-
let
|
|
2012
|
-
|
|
2013
|
-
for(const pn in def.params) {
|
|
2014
|
-
const p = def.params[pn];
|
|
2016
|
+
let optPns = [];
|
|
2017
|
+
def.params && Object.values(def.params).forEach(p => {
|
|
2015
2018
|
// user assigned annotation, don't touch it
|
|
2016
2019
|
if(Object.keys(p).some(a => a.startsWith('@Core.OptionalParameter') && p[a] !== null)) {
|
|
2017
|
-
|
|
2020
|
+
optPns.push(p);
|
|
2018
2021
|
}
|
|
2019
2022
|
// default value automatically makes param optional
|
|
2020
2023
|
else if(p.default) {
|
|
@@ -2022,19 +2025,24 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
2022
2025
|
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.DefaultValue', p.default.val);
|
|
2023
2026
|
else
|
|
2024
2027
|
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2025
|
-
|
|
2028
|
+
optPns.push(p);
|
|
2026
2029
|
}
|
|
2027
2030
|
// if no default is available, nullable makes param optional
|
|
2028
2031
|
else if(!p.notNull) {
|
|
2029
2032
|
edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
|
|
2030
|
-
|
|
2033
|
+
optPns.push(p);
|
|
2034
|
+
|
|
2031
2035
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2036
|
+
else {
|
|
2037
|
+
// this is a mandatory parameter, warn about all previously collected optional parameters
|
|
2038
|
+
optPns.forEach(p => {
|
|
2039
|
+
const type = p.items?.type || p.type;
|
|
2040
|
+
if(type !== special$self)
|
|
2041
|
+
warning('odata-parameter-order', loc.concat(p.name));
|
|
2042
|
+
});
|
|
2043
|
+
optPns = [];
|
|
2035
2044
|
}
|
|
2036
|
-
|
|
2037
|
-
}
|
|
2045
|
+
});
|
|
2038
2046
|
}
|
|
2039
2047
|
}
|
|
2040
2048
|
|
|
@@ -2043,7 +2051,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
2043
2051
|
// Helper section starts here
|
|
2044
2052
|
//
|
|
2045
2053
|
|
|
2046
|
-
|
|
2054
|
+
|
|
2047
2055
|
function mapCdsToEdmProp(obj) {
|
|
2048
2056
|
if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
|
|
2049
2057
|
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const { setProp } = require('../base/model');
|
|
3
3
|
const { isBuiltinType, isEdmPropertyRendered, applyTransformations, cloneAnnotationValue } = require('../model/csnUtils');
|
|
4
4
|
const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
|
|
5
|
+
const {CompilerAssertion} = require('../base/error');
|
|
5
6
|
|
|
6
7
|
/* eslint max-statements-per-line:off */
|
|
7
8
|
function validateOptions(_options)
|
|
@@ -24,9 +25,6 @@ function validateOptions(_options)
|
|
|
24
25
|
options.isStructFormat = options.odataFormat && options.odataFormat === 'structured';
|
|
25
26
|
options.isFlatFormat = !options.isStructFormat;
|
|
26
27
|
|
|
27
|
-
if(options.v.filter(v=>v).length !== 1)
|
|
28
|
-
throw Error(`Please debug me: EDM V2:${v2}, V4:${v4}`);
|
|
29
|
-
|
|
30
28
|
options.isV2 = () => v2;
|
|
31
29
|
options.isV4 = () => v4;
|
|
32
30
|
|
|
@@ -107,9 +105,6 @@ function isDerivedType(artifact) {
|
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions) {
|
|
110
|
-
if(!assocCsn._constraints)
|
|
111
|
-
throw Error('Please debug me: need _constraints');
|
|
112
|
-
|
|
113
108
|
const { info, warning } = messageFunctions;
|
|
114
109
|
|
|
115
110
|
if(assocCsn.on)
|
|
@@ -170,7 +165,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
|
|
|
170
165
|
key id : Integer;
|
|
171
166
|
toMe: association to E on toMe.id = $self; };
|
|
172
167
|
*/
|
|
173
|
-
throw
|
|
168
|
+
throw new CompilerAssertion('Backlink association element is not an association or composition: "' + originAssocCsn.name);
|
|
174
169
|
}
|
|
175
170
|
}
|
|
176
171
|
else
|
|
@@ -255,11 +250,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
|
|
|
255
250
|
|
|
256
251
|
function finalizeReferentialConstraints(csn, assocCsn, options, info)
|
|
257
252
|
{
|
|
258
|
-
if(
|
|
259
|
-
throw Error('Please debug me: need _constraints');
|
|
260
|
-
|
|
261
|
-
if(assocCsn.on)
|
|
262
|
-
{
|
|
253
|
+
if (assocCsn.on) {
|
|
263
254
|
/* example for originalTarget:
|
|
264
255
|
entity E (with parameters) {
|
|
265
256
|
... keys and all the stuff ...
|
|
@@ -439,7 +430,7 @@ function determineMultiplicity(csn)
|
|
|
439
430
|
1 => 0..1 // Association
|
|
440
431
|
1 => 1 // Composition
|
|
441
432
|
n => '*'
|
|
442
|
-
* => *
|
|
433
|
+
* => '*'
|
|
443
434
|
|
|
444
435
|
=> TGT Cardinality
|
|
445
436
|
CDS => EDM
|
|
@@ -621,7 +612,7 @@ function addTypeFacets(node, csn)
|
|
|
621
612
|
*/
|
|
622
613
|
function isODataSimpleIdentifier(identifier){
|
|
623
614
|
// this regular expression reflects the specifiation from above
|
|
624
|
-
const regex = /^[\p{Letter}\p{Nl}_]
|
|
615
|
+
const regex = /^[\p{Letter}\p{Nl}_][_\p{Letter}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]{0,127}$/gu
|
|
625
616
|
return identifier && identifier.match(regex);
|
|
626
617
|
}
|
|
627
618
|
|
|
@@ -776,7 +767,7 @@ function mergeIntoNavPropEntry(annoPrefix, navPropEntry, prefix, props) {
|
|
|
776
767
|
// don't overwrite existing restrictions
|
|
777
768
|
const prop = annoPrefix.split('.')[1];
|
|
778
769
|
if(!navPropEntry[prop]) {
|
|
779
|
-
// if dictionary has entries, add them to
|
|
770
|
+
// if dictionary has entries, add them to navPropEntry
|
|
780
771
|
if(Object.keys(o).length) {
|
|
781
772
|
// ReadRestrictions may have sub type ReadByKeyRestrictions { Description, LongDescription }
|
|
782
773
|
// chop annotations into dictionaries
|