@sap/cds-compiler 2.15.2 → 3.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 +66 -1590
- package/bin/cdsc.js +42 -46
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +312 -143
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +80 -24
- package/lib/base/messages.js +103 -52
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +127 -10
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +63 -12
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +16 -14
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +194 -222
- package/lib/compiler/shared.js +56 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +7 -2
- package/lib/edm/annotations/genericTranslation.js +60 -6
- package/lib/edm/annotations/preprocessAnnotations.js +10 -11
- package/lib/edm/csn2edm.js +39 -41
- package/lib/edm/edm.js +22 -15
- package/lib/edm/edmPreprocessor.js +66 -69
- package/lib/edm/edmUtils.js +12 -62
- package/lib/gen/Dictionary.json +8 -6
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20717 -22376
- package/lib/json/from-csn.js +73 -68
- package/lib/json/to-csn.js +13 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +333 -259
- package/lib/language/language.g4 +600 -645
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +2 -1
- package/lib/model/csnUtils.js +183 -285
- package/lib/model/revealInternalProperties.js +32 -9
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +64 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +334 -339
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +60 -54
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +19 -22
- package/lib/transform/forOdataNew.js +13 -15
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +11 -9
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +6 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
|
@@ -11,14 +11,11 @@ const {
|
|
|
11
11
|
intersect,
|
|
12
12
|
validateOptions,
|
|
13
13
|
foreach,
|
|
14
|
-
forAll,
|
|
15
|
-
isAssociationOrComposition,
|
|
16
14
|
isComposition,
|
|
17
15
|
isStructuredArtifact,
|
|
18
16
|
isParameterizedEntity,
|
|
19
17
|
resolveOnConditionAndPrepareConstraints,
|
|
20
18
|
finalizeReferentialConstraints,
|
|
21
|
-
isEntity,
|
|
22
19
|
getSchemaPrefix,
|
|
23
20
|
} = require('./edmUtils.js');
|
|
24
21
|
|
|
@@ -54,6 +51,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
54
51
|
fallBackSchemaName,
|
|
55
52
|
whatsMyServiceRootName ] = getAnOverviewOnTheServices(csn);
|
|
56
53
|
|
|
54
|
+
if(serviceRootNames.length === 0) {
|
|
55
|
+
return [serviceRoots, Object.create(null), reqDefs, whatsMyServiceRootName, fallBackSchemaName, options];
|
|
56
|
+
}
|
|
57
|
+
|
|
57
58
|
if(requestedServiceNames === undefined)
|
|
58
59
|
requestedServiceNames = options.serviceNames;
|
|
59
60
|
if(requestedServiceNames === undefined) {
|
|
@@ -64,10 +65,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
64
65
|
return requestedServiceNames.includes(whatsMyServiceRootName(n));
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
if(serviceRootNames.length === 0) {
|
|
68
|
-
return [serviceRoots, Object.create(null), reqDefs, whatsMyServiceRootName, fallBackSchemaName, options];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
68
|
// Structural CSN inbound QA checks
|
|
72
69
|
inboundQualificationChecks();
|
|
73
70
|
// not needed at the moment
|
|
@@ -85,7 +82,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
85
82
|
|
|
86
83
|
/*
|
|
87
84
|
Final base type expansion is required here when:
|
|
88
|
-
1) The input CSN was already transformed for V4 but shall be rendered in V2 and the
|
|
85
|
+
1) The input CSN was already transformed for V4 but shall be rendered in V2 and the
|
|
89
86
|
edmx generator is called directly (bypassing OData transformation)
|
|
90
87
|
2) The input CSN was already transformed for V4 and persisted (all non-enumerables are
|
|
91
88
|
stripped of)
|
|
@@ -119,17 +116,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
if(schemaNames.length) {
|
|
122
|
-
// First attach names to all definitions (and actions/params) in the model
|
|
123
|
-
// elements are done in initializeStruct
|
|
124
|
-
// Set myServiceName for later reference and indication of a service member
|
|
125
|
-
// First attach names to all definitions in the model and fill reqDefs
|
|
126
|
-
// Link association targets and spray @odata.contained over untagged compositions
|
|
127
119
|
forEachDefinition(csn, [
|
|
128
120
|
attachNameProperty,
|
|
129
121
|
(def, defName) => {
|
|
130
122
|
const mySchemaName = whatsMySchemaName(defName);
|
|
131
123
|
mySchemaName && setProp(def, '$mySchemaName', mySchemaName);
|
|
132
|
-
if(isMyServiceRequested(defName))
|
|
124
|
+
if(isMyServiceRequested(defName) && def.kind !== 'aspect')
|
|
133
125
|
reqDefs.definitions[defName] = def;
|
|
134
126
|
},
|
|
135
127
|
linkAssociationTarget ]);
|
|
@@ -307,7 +299,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
307
299
|
const myServiceRoot = whatsMyServiceRootName(defName);
|
|
308
300
|
const mySchemaPrefix = getSchemaPrefix(defName);
|
|
309
301
|
if(myServiceRoot && options.isV4() &&
|
|
310
|
-
/*(options.
|
|
302
|
+
/*(options.odataProxies || options.odataXServiceRefs) && options.isStructFormat && */
|
|
311
303
|
defName !== myServiceRoot && myServiceRoot !== mySchemaPrefix) {
|
|
312
304
|
const service = { kind: 'service', name: mySchemaPrefix };
|
|
313
305
|
serviceRoots[mySchemaPrefix] = service;
|
|
@@ -341,23 +333,21 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
341
333
|
// link association target to association and add @odata.contained to compositions in V4
|
|
342
334
|
function linkAssociationTarget(struct) {
|
|
343
335
|
forEachMemberRecursively(struct, (element, name, prop, subpath) => {
|
|
344
|
-
if(
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
setProp(element, '_target', target);
|
|
336
|
+
if(element.target && !element._target) {
|
|
337
|
+
let target = csn.definitions[element.target];
|
|
338
|
+
if(target) {
|
|
339
|
+
setProp(element, '_target', target);
|
|
349
340
|
// If target has parameters, xref assoc at target for redirection
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
target.$sources[struct.name + '.' + name] = element;
|
|
341
|
+
if(isParameterizedEntity(target)) {
|
|
342
|
+
if(!target.$sources) {
|
|
343
|
+
setProp(target, '$sources', Object.create(null));
|
|
355
344
|
}
|
|
356
|
-
|
|
357
|
-
else {
|
|
358
|
-
error(null, subpath, { target: element.target }, "Target $(TARGET) can't be found in the model");
|
|
345
|
+
target.$sources[struct.name + '.' + name] = element;
|
|
359
346
|
}
|
|
360
347
|
}
|
|
348
|
+
else {
|
|
349
|
+
error(null, subpath, { target: element.target }, "Target $(TARGET) can't be found in the model");
|
|
350
|
+
}
|
|
361
351
|
}
|
|
362
352
|
// in V4 tag all compositions to be containments
|
|
363
353
|
if(options.odataContainment &&
|
|
@@ -389,7 +379,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
389
379
|
}
|
|
390
380
|
|
|
391
381
|
function initContainments(elt, eltName) {
|
|
392
|
-
if(
|
|
382
|
+
if(elt.target && elt['@odata.contained'] && !elt._ignore) {
|
|
393
383
|
// Let the containee know its container
|
|
394
384
|
// (array because the contanee may contained more then once)
|
|
395
385
|
let containee = elt._target;
|
|
@@ -660,17 +650,19 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
660
650
|
}
|
|
661
651
|
//forward annotations from managed association element to its foreign keys
|
|
662
652
|
const elements = construct.items && construct.items.elements || construct.elements;
|
|
663
|
-
|
|
653
|
+
const fk = elements[element['@odata.foreignKey4']];
|
|
654
|
+
for(const attrName in fk) {
|
|
655
|
+
const attr = fk[attrName];
|
|
664
656
|
if(attrName[0] === '@') {
|
|
665
657
|
element[attrName] = attr;
|
|
666
658
|
}
|
|
667
|
-
}
|
|
659
|
+
}
|
|
668
660
|
// and eventually remove some afterwards:)
|
|
669
661
|
if(options.isV2())
|
|
670
662
|
setSAPSpecificV2AnnotationsToAssociation(element);
|
|
671
663
|
|
|
672
664
|
// initialize an association
|
|
673
|
-
if(
|
|
665
|
+
if(element.target) {
|
|
674
666
|
// in case this is a forward assoc, store the backlink partners here, _selfReferences.length > 1 => error
|
|
675
667
|
assignProp(element, '_selfReferences', []);
|
|
676
668
|
assignProp(element._target, '$proxies', []);
|
|
@@ -685,7 +677,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
685
677
|
applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
|
|
686
678
|
}, [], true, { elementsOnly: true });
|
|
687
679
|
|
|
688
|
-
if(!isDeprecatedEnabled(options, '
|
|
680
|
+
if(!isDeprecatedEnabled(options, '_v1KeysForTemporal')) {
|
|
689
681
|
// if artifact has a cds.valid.key mention it as @Core.AlternateKey
|
|
690
682
|
if(validKey.length) {
|
|
691
683
|
let altKeys = [{ Key: [] }];
|
|
@@ -699,7 +691,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
699
691
|
// @Core.AlternateKeys: [{ Key: [ { Name: 'slID', Alias: 'slID' }, { Name: 'validFrom', Alias: 'validFrom'} ] }]
|
|
700
692
|
if(validKey.length) {
|
|
701
693
|
let altKeys = [{ Key: [] }];
|
|
702
|
-
|
|
694
|
+
Object.entries(([kn, k]) => {
|
|
703
695
|
altKeys[0].Key.push( { Name: kn, Alias: kn } );
|
|
704
696
|
delete k.key;
|
|
705
697
|
});
|
|
@@ -722,7 +714,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
722
714
|
}
|
|
723
715
|
|
|
724
716
|
// prepare the structure itself
|
|
725
|
-
if(
|
|
717
|
+
if(def.kind === 'entity') {
|
|
726
718
|
assignProp(def, '_SetAttributes', Object.create(null));
|
|
727
719
|
assignProp(def, '$keys', keys);
|
|
728
720
|
applyAppSpecificLateCsnTransformationOnStructure(options, def, error);
|
|
@@ -738,7 +730,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
738
730
|
forEachMemberRecursively(def.items || def, initConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
739
731
|
}
|
|
740
732
|
function initConstraintsOnAssoc(element) {
|
|
741
|
-
if (
|
|
733
|
+
if (element.target && !element._constraints) {
|
|
742
734
|
// setup the constraints object
|
|
743
735
|
setProp(element, '_constraints', { constraints: Object.create(null), selfs: [], _origins: [], termCount: 0 });
|
|
744
736
|
// and crack the ON condition
|
|
@@ -790,7 +782,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
790
782
|
delete struct.$keys[element.name];
|
|
791
783
|
}
|
|
792
784
|
}
|
|
793
|
-
// deprecated
|
|
785
|
+
// deprecated._unmanagedUpInComponent:
|
|
794
786
|
// Only in containment:
|
|
795
787
|
// Ignore this (foreign key) elment if renderForeignKeys is false
|
|
796
788
|
if(options.odataContainment && element['@odata.containment.ignore']) {
|
|
@@ -822,7 +814,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
822
814
|
forEachMemberRecursively(def.items || def, finalizeConstraintsOnAssoc, [], true, { elementsOnly: true });
|
|
823
815
|
}
|
|
824
816
|
function finalizeConstraintsOnAssoc(element) {
|
|
825
|
-
if (
|
|
817
|
+
if (element.target && !element._ignore && element._constraints) {
|
|
826
818
|
finalizeReferentialConstraints(csn, element, options, info);
|
|
827
819
|
|
|
828
820
|
if(element._constraints._partnerCsn && element.cardinality && element.cardinality.max) {
|
|
@@ -855,7 +847,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
855
847
|
sub artifacts exposed by the initial type exposure
|
|
856
848
|
*/
|
|
857
849
|
function convertExposedTypesOfOtherServicesIntoCrossReferences() {
|
|
858
|
-
if(options.
|
|
850
|
+
if(options.odataXServiceRefs && options.isV4()) {
|
|
859
851
|
serviceRootNames.forEach(srn => {
|
|
860
852
|
schemaNames.forEach(fqSchemaName => {
|
|
861
853
|
if(fqSchemaName.startsWith(srn + '.')) {
|
|
@@ -912,7 +904,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
912
904
|
// if this artifact is a service member check its associations
|
|
913
905
|
if(globalSchemaPrefix) {
|
|
914
906
|
forEachGeneric(struct.items || struct, 'elements', element => {
|
|
915
|
-
if(!
|
|
907
|
+
if(!element.target || element['@odata.navigable'] === false)
|
|
916
908
|
return;
|
|
917
909
|
/*
|
|
918
910
|
* Consider everything @cds.autoexpose: falsy to be a proxy candidate for now
|
|
@@ -949,18 +941,18 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
949
941
|
const targetSchemaName = element._target.$mySchemaName;
|
|
950
942
|
if(isProxyRequired(element)) {
|
|
951
943
|
if(options.isV4() &&
|
|
952
|
-
(options.
|
|
944
|
+
(options.odataProxies || options.odataXServiceRefs) &&
|
|
953
945
|
// must be a managed association with keys OR an unambiguous backlink
|
|
954
|
-
element.keys ||
|
|
946
|
+
element.keys ||
|
|
955
947
|
(element.on && element._constraints.selfs.length === 1 && element._constraints.termCount === 1)
|
|
956
948
|
) {
|
|
957
949
|
// reuse proxy if available
|
|
958
950
|
let proxy = getProxyForTargetOf(element);
|
|
959
951
|
if(!proxy) {
|
|
960
|
-
if(targetSchemaName && options.
|
|
952
|
+
if(targetSchemaName && options.odataXServiceRefs) {
|
|
961
953
|
proxy = createSchemaRefFor(targetSchemaName);
|
|
962
954
|
}
|
|
963
|
-
else if(options.
|
|
955
|
+
else if(options.odataProxies) {
|
|
964
956
|
proxy = createProxyFor(element, targetSchemaName);
|
|
965
957
|
}
|
|
966
958
|
proxy = registerProxy(proxy, element);
|
|
@@ -1024,7 +1016,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1024
1016
|
// proxyDefinitionName: strip the serviceName and replace '.' with '_'
|
|
1025
1017
|
let defName =
|
|
1026
1018
|
`${assoc._target.name.replace(proxySchemaName + '.', '').replace(/\./g, '_')}`;
|
|
1027
|
-
|
|
1019
|
+
|
|
1028
1020
|
// fullName: Prepend serviceName and if in same service add '_proxy'
|
|
1029
1021
|
const proxy = isParamProxy
|
|
1030
1022
|
? createParameterEntity(assoc._target, proxySchemaName + '.' + defName, true)
|
|
@@ -1068,13 +1060,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1068
1060
|
// copy over the primary keys of the target and trigger the type exposure
|
|
1069
1061
|
// if the element already exists we assume it was fully exposed
|
|
1070
1062
|
function populateProxyElements(assoc, proxy, elements) {
|
|
1071
|
-
|
|
1063
|
+
Object.values(elements).forEach(e => {
|
|
1072
1064
|
if (isEdmPropertyRendered(e, options)) {
|
|
1073
1065
|
let newElt = proxy.elements[e.name];
|
|
1074
1066
|
if(!newElt) {
|
|
1075
1067
|
if(csnUtils.isAssocOrComposition(e.type)) {
|
|
1076
1068
|
if(!e.on && e.keys) {
|
|
1077
|
-
if(options.
|
|
1069
|
+
if(options.odataNoTransitiveProxies)
|
|
1078
1070
|
newElt = convertManagedAssocIntoStruct(e);
|
|
1079
1071
|
else
|
|
1080
1072
|
newElt = createProxyOrSchemaRefForManagedAssoc(e);
|
|
@@ -1275,10 +1267,10 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1275
1267
|
proxy = getProxyForTargetOf(e);
|
|
1276
1268
|
if(!proxy) {
|
|
1277
1269
|
// option odataXServiceRefs has precedence over odataProxies
|
|
1278
|
-
if(e._target.$mySchemaName && options.
|
|
1270
|
+
if(e._target.$mySchemaName && options.odataXServiceRefs) {
|
|
1279
1271
|
proxy = createSchemaRefFor(e._target.$mySchemaName);
|
|
1280
1272
|
}
|
|
1281
|
-
else if(options.
|
|
1273
|
+
else if(options.odataProxies) {
|
|
1282
1274
|
proxy = createProxyFor(e, e._target.$mySchemaName);
|
|
1283
1275
|
if(!e._target.$isParamEntity)
|
|
1284
1276
|
populateProxyElements(e, proxy, getForeignKeyDefinitions(e));
|
|
@@ -1451,16 +1443,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1451
1443
|
if(def.$keys) {
|
|
1452
1444
|
setProp(def, '$edmKeyPaths', []);
|
|
1453
1445
|
// for all key elements that shouldn't be ignored produce the paths
|
|
1454
|
-
foreach(def.$keys, k => !
|
|
1446
|
+
foreach(def.$keys, k => !(k._isToContainer && k._selfReferences.length), (k, kn) => {
|
|
1455
1447
|
if(isEdmPropertyRendered(k, options) &&
|
|
1456
1448
|
!(options.isV2() && k['@Core.MediaType'])) {
|
|
1457
1449
|
if(options.isV4() && options.isStructFormat) {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
if(options.renderForeignKeys && !k.target)
|
|
1461
|
-
def.$edmKeyPaths.push([kn]);
|
|
1462
|
-
// else produce paths (isEdmPropertyRendered() has filtered @odata.foreignKey4 already)
|
|
1463
|
-
else if(!options.renderForeignKeys)
|
|
1450
|
+
// This is structured OData ONLY
|
|
1451
|
+
// if the foreign keys are explicitly requested, ignore associations and use the flat foreign keys instead
|
|
1452
|
+
if(!options.renderForeignKeys || (options.renderForeignKeys && !k.target))
|
|
1464
1453
|
def.$edmKeyPaths.push(...produceKeyRefPaths(k, kn));
|
|
1465
1454
|
}
|
|
1466
1455
|
// In v2/v4 flat, associations are never rendered
|
|
@@ -1543,13 +1532,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1543
1532
|
// Nullability
|
|
1544
1533
|
if((!elt.key && (elt.notNull === undefined || elt.notNull === false)) ||
|
|
1545
1534
|
elt.key && (elt.notNull !== undefined && elt.notNull === false)) {
|
|
1546
|
-
|
|
1535
|
+
message('odata-spec-violation-key-null', location,
|
|
1547
1536
|
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1548
1537
|
}
|
|
1549
1538
|
// many
|
|
1550
1539
|
let type = elt.items || elt.type && !isBuiltinType(elt.type) && csnUtils.getFinalTypeDef(elt.type).items;
|
|
1551
1540
|
if(type) {
|
|
1552
|
-
|
|
1541
|
+
message('odata-spec-violation-key-array', location,
|
|
1553
1542
|
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1554
1543
|
}
|
|
1555
1544
|
// type
|
|
@@ -1559,7 +1548,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1559
1548
|
|
|
1560
1549
|
// check for legal scalar types, proxy exposed structured types are not resolvable in CSN
|
|
1561
1550
|
// V2 allows any Edm.PrimitiveType (even Double and Binary), V4 is more specific:
|
|
1562
|
-
if(options.isV4() && type && !
|
|
1551
|
+
if(options.isV4() && type && !type.target && isBuiltinType(type.type)) {
|
|
1563
1552
|
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1564
1553
|
const legalEdmTypes = {
|
|
1565
1554
|
'Edm.Boolean':1, 'Edm.Byte':1, 'Edm.Date':1, 'Edm.DateTimeOffset':1, 'Edm.Decimal':1, 'Edm.Duration':1,
|
|
@@ -1612,10 +1601,17 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1612
1601
|
if(isEdmPropertyRendered(elt, options)) {
|
|
1613
1602
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1614
1603
|
// unroll derived type chains for assocs
|
|
1615
|
-
if(
|
|
1604
|
+
if(elt.target && !elt.$touched) {
|
|
1616
1605
|
if(!elt._target.$edmTgtPaths)
|
|
1617
1606
|
setProp(elt._target, '$edmTgtPaths', []);
|
|
1618
|
-
|
|
1607
|
+
// drill into target only if
|
|
1608
|
+
// 1) target has no entity set and this assoc is not going to the container
|
|
1609
|
+
// 2) current definition and target are the same (cycle)
|
|
1610
|
+
// 3) it's no external reference
|
|
1611
|
+
if(!elt.$externalRef &&
|
|
1612
|
+
!elt._target.$hasEntitySet &&
|
|
1613
|
+
!elt._isToContainer &&
|
|
1614
|
+
curDef !== elt._target) {
|
|
1619
1615
|
// follow elements in the target but avoid cycles
|
|
1620
1616
|
setProp(elt, '$touched', true);
|
|
1621
1617
|
elt._target.$edmTgtPaths.push(newPrefix);
|
|
@@ -1650,10 +1646,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1650
1646
|
if(isEdmPropertyRendered(elt, options)) {
|
|
1651
1647
|
// Assoc can never be a derived TypeDefinition, no need to
|
|
1652
1648
|
// unroll derived type chains for assocs
|
|
1653
|
-
if(
|
|
1649
|
+
if(elt.target && !elt.$touched) {
|
|
1654
1650
|
// drill into target only if
|
|
1655
1651
|
// 1) target has no entity set and this assoc is not going to the container
|
|
1656
1652
|
// 2) current definition and target are the same (cycle)
|
|
1653
|
+
// 3) it's no external reference
|
|
1657
1654
|
if(!elt.$externalRef &&
|
|
1658
1655
|
!elt._target.$hasEntitySet &&
|
|
1659
1656
|
!elt._isToContainer &&
|
|
@@ -1713,7 +1710,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1713
1710
|
// 1) must not be a proxy and not a containee in V4
|
|
1714
1711
|
// No annos are rendered for non-existing EntitySet targets.
|
|
1715
1712
|
if(def.$hasEntitySet === undefined) {
|
|
1716
|
-
const hasEntitySet =
|
|
1713
|
+
const hasEntitySet = def.kind === 'entity' && !(options.isV4() && edmUtils.isContainee(def)) && !def.$proxy;
|
|
1717
1714
|
setProp(def, '$hasEntitySet', hasEntitySet);
|
|
1718
1715
|
}
|
|
1719
1716
|
}
|
|
@@ -1917,7 +1914,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1917
1914
|
if(Object.keys(o).length) {
|
|
1918
1915
|
// ReadRestrictions may have sub type ReadByKeyRestrictions { Description, LongDescription }
|
|
1919
1916
|
// chop annotations into dictionaries
|
|
1920
|
-
if(prefix === '@Capabilities.ReadRestrictions' &&
|
|
1917
|
+
if(prefix === '@Capabilities.ReadRestrictions' &&
|
|
1921
1918
|
Object.keys(o).some(k => k.startsWith('ReadByKeyRestrictions.'))) {
|
|
1922
1919
|
const no = {};
|
|
1923
1920
|
Object.entries(o).forEach(([k,v]) => {
|
|
@@ -1958,16 +1955,16 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1958
1955
|
}
|
|
1959
1956
|
|
|
1960
1957
|
function mapCdsToEdmProp(obj) {
|
|
1961
|
-
if (obj.type && isBuiltinType(obj.type) && !
|
|
1962
|
-
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.
|
|
1958
|
+
if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
|
|
1959
|
+
let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
1963
1960
|
assignProp(obj, '_edmType', edmType);
|
|
1964
1961
|
} else if (obj._isCollection && (obj.items && isBuiltinType(csnUtils.getFinalTypeDef(obj.items.type)))) {
|
|
1965
|
-
let edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.
|
|
1962
|
+
let edmType = edmUtils.mapCdsToEdmType(obj.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType'], obj.$path);
|
|
1966
1963
|
assignProp(obj, '_edmType', edmType);
|
|
1967
1964
|
}
|
|
1968
1965
|
// This is the special case when we have array of array, but will not be supported in the future
|
|
1969
1966
|
else if (obj._isCollection && obj.items && obj.items.type && obj.items.items && isBuiltinType(csnUtils.getFinalTypeDef(obj.items.items.type))) {
|
|
1970
|
-
let edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.
|
|
1967
|
+
let edmType = edmUtils.mapCdsToEdmType(obj.items.items, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
|
|
1971
1968
|
assignProp(obj, '_edmType', edmType);
|
|
1972
1969
|
}
|
|
1973
1970
|
}
|
|
@@ -2321,7 +2318,7 @@ function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
|
2321
2318
|
});
|
|
2322
2319
|
|
|
2323
2320
|
function addToAssociationSet(carrier, propName, propValue, removeFromType=true) {
|
|
2324
|
-
if(
|
|
2321
|
+
if(carrier.target) {
|
|
2325
2322
|
assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
2326
2323
|
assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
2327
2324
|
if(removeFromType) {
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -8,31 +8,17 @@ function validateOptions(_options)
|
|
|
8
8
|
{
|
|
9
9
|
if(!_options.isV2 && !_options.isV4)
|
|
10
10
|
{
|
|
11
|
-
// csn2edm expects "
|
|
11
|
+
// csn2edm expects "odataVersion" to be a top-level property of options
|
|
12
12
|
// set to 'v4' as default, override with value from incoming options
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
options.odataFormat = options.toOdata.odataFormat;
|
|
20
|
-
if(options.toOdata.odataContainment)
|
|
21
|
-
options.odataContainment = options.toOdata.odataContainment;
|
|
22
|
-
if(options.toOdata.odataForeignKeys)
|
|
23
|
-
options.odataForeignKeys = options.toOdata.odataForeignKeys;
|
|
24
|
-
if(options.toOdata.odataV2PartialConstr)
|
|
25
|
-
options.odataV2PartialConstr = options.toOdata.odataV2PartialConstr;
|
|
26
|
-
// global flag that indicates whether or not FKs shall be rendered in general
|
|
27
|
-
// V2/V4 flat: yes
|
|
28
|
-
// V4/struct: depending on odataForeignKeys
|
|
29
|
-
options.renderForeignKeys =
|
|
30
|
-
options.version === 'v4' ? options.odataFormat === 'structured' && !!options.odataForeignKeys : true;
|
|
13
|
+
const options = Object.assign({ odataVersion: 'v4'}, _options);
|
|
14
|
+
// global flag that indicates whether or not FKs shall be rendered in general
|
|
15
|
+
// V2/V4 flat: yes
|
|
16
|
+
// V4/struct: depending on odataForeignKeys
|
|
17
|
+
options.renderForeignKeys =
|
|
18
|
+
options.odataVersion === 'v4' ? options.odataFormat === 'structured' && !!options.odataForeignKeys : true;
|
|
31
19
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const v2 = options.version.match(/v2/i) !== null;
|
|
35
|
-
const v4 = options.version.match(/v4/i) !== null;
|
|
20
|
+
const v2 = options.odataVersion.match(/v2/i) !== null;
|
|
21
|
+
const v4 = options.odataVersion.match(/v4/i) !== null;
|
|
36
22
|
|
|
37
23
|
options.v = [v2, v4];
|
|
38
24
|
options.isStructFormat = options.odataFormat && options.odataFormat === 'structured';
|
|
@@ -70,11 +56,6 @@ function foreach(dictionary, filter, func) {
|
|
|
70
56
|
});
|
|
71
57
|
}
|
|
72
58
|
|
|
73
|
-
// Call func(art, name) for each artifact 'art' with name 'name' in 'dictionary'
|
|
74
|
-
function forAll(dictionary, func) {
|
|
75
|
-
foreach(dictionary, ()=>true, func);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
59
|
// true if _containerEntity is unequal to artifact name (non-recursive containment association)
|
|
79
60
|
// or if artifact belongs to an artificial parameter entity
|
|
80
61
|
function isContainee(artifact) {
|
|
@@ -82,27 +63,11 @@ function isContainee(artifact) {
|
|
|
82
63
|
return (artifact._containerEntity && (artifact._containerEntity.length > 1 || artifact._containerEntity[0] != artifact.name));
|
|
83
64
|
}
|
|
84
65
|
|
|
85
|
-
// Return true if 'artifact' has an association type
|
|
86
|
-
function isAssociation(artifact) {
|
|
87
|
-
return (artifact.type === 'cds.Association' || artifact.type === 'Association') && artifact.target != undefined;
|
|
88
|
-
//return artifact.target != undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
66
|
function isComposition(artifact) {
|
|
92
67
|
return (artifact.type === 'cds.Composition' || artifact.type === 'Composition') &&
|
|
93
68
|
artifact.target != undefined;
|
|
94
69
|
}
|
|
95
70
|
|
|
96
|
-
function isAssociationOrComposition(artifact)
|
|
97
|
-
{
|
|
98
|
-
return isAssociation(artifact) || isComposition(artifact);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function isManagedAssociation(artifact)
|
|
102
|
-
{
|
|
103
|
-
return isAssociation(artifact) && artifact.on == undefined;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
71
|
// Return true if the association 'assoc' has cardinality 'to-many'
|
|
107
72
|
function isToMany(assoc) {
|
|
108
73
|
if (!assoc.cardinality) {
|
|
@@ -122,13 +87,8 @@ function isSingleton(entityCsn) {
|
|
|
122
87
|
return singleton || ((singleton === undefined || singleton === null) && hasNullable);
|
|
123
88
|
}
|
|
124
89
|
|
|
125
|
-
function isEntity(artifact)
|
|
126
|
-
{
|
|
127
|
-
return artifact.kind === 'entity';
|
|
128
|
-
}
|
|
129
|
-
|
|
130
90
|
function isParameterizedEntity(artifact) {
|
|
131
|
-
return
|
|
91
|
+
return artifact.kind === 'entity' && artifact.params;
|
|
132
92
|
}
|
|
133
93
|
|
|
134
94
|
// Return true if 'artifact' is structured (i.e. has elements, like a structured type or an entity)
|
|
@@ -146,10 +106,6 @@ function isDerivedType(artifact) {
|
|
|
146
106
|
return artifact.kind === 'type' && !isStructuredArtifact(artifact);
|
|
147
107
|
}
|
|
148
108
|
|
|
149
|
-
function isActionOrFunction(artifact) {
|
|
150
|
-
return artifact.kind === 'action' || artifact.kind === 'function';
|
|
151
|
-
}
|
|
152
|
-
|
|
153
109
|
function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions) {
|
|
154
110
|
if(!assocCsn._constraints)
|
|
155
111
|
throw Error('Please debug me: need _constraints');
|
|
@@ -189,7 +145,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
|
|
|
189
145
|
info(null, ['definitions', parentName, 'elements', assocCsn.name],
|
|
190
146
|
`"${originParentName}:${partnerPath.join('.')}" with target "${originAssocCsn._target.name}" is compared with $self which represents "${parentName}"`);
|
|
191
147
|
}
|
|
192
|
-
if(
|
|
148
|
+
if(originAssocCsn.target) {
|
|
193
149
|
// Mark this association as backlink if $self appears exactly once
|
|
194
150
|
// to surpress edm:Association generation in V2 mode
|
|
195
151
|
if(isBacklink) {
|
|
@@ -359,7 +315,7 @@ function finalizeReferentialConstraints(csn, assocCsn, options, info)
|
|
|
359
315
|
// in structured mode only resolve top level element (path rewriting is done elsewhere)
|
|
360
316
|
const depEltName = ( options.isFlatFormat ? c[0].join('_') : c[0][0] );
|
|
361
317
|
const principalEltName = ( options.isFlatFormat ? c[1].join('_') : c[1][0] );
|
|
362
|
-
const fk = (
|
|
318
|
+
const fk = (dependentEntity.kind === 'entity' && dependentEntity.elements[ depEltName ]) ||
|
|
363
319
|
(localDepEntity && localDepEntity.elements && localDepEntity.elements[ depEltName ]);
|
|
364
320
|
const pk = principalEntity.$keys && principalEntity.$keys[ principalEltName ];
|
|
365
321
|
if(isConstraintCandidate(fk) && isConstraintCandidate(pk)) {
|
|
@@ -801,20 +757,14 @@ module.exports = {
|
|
|
801
757
|
validateOptions,
|
|
802
758
|
intersect,
|
|
803
759
|
foreach,
|
|
804
|
-
forAll,
|
|
805
760
|
isContainee,
|
|
806
|
-
isAssociation,
|
|
807
|
-
isManagedAssociation,
|
|
808
761
|
isComposition,
|
|
809
|
-
isAssociationOrComposition,
|
|
810
762
|
isToMany,
|
|
811
763
|
isSingleton,
|
|
812
|
-
isEntity,
|
|
813
764
|
isStructuredType,
|
|
814
765
|
isStructuredArtifact,
|
|
815
766
|
isParameterizedEntity,
|
|
816
767
|
isDerivedType,
|
|
817
|
-
isActionOrFunction,
|
|
818
768
|
resolveOnConditionAndPrepareConstraints,
|
|
819
769
|
finalizeReferentialConstraints,
|
|
820
770
|
determineMultiplicity,
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -658,6 +658,7 @@
|
|
|
658
658
|
"Type": "Common.FieldControlType",
|
|
659
659
|
"AppliesTo": [
|
|
660
660
|
"Property",
|
|
661
|
+
"Parameter",
|
|
661
662
|
"Record",
|
|
662
663
|
"EntityType"
|
|
663
664
|
]
|
|
@@ -3246,6 +3247,7 @@
|
|
|
3246
3247
|
"TypeNamePlural": "Edm.String",
|
|
3247
3248
|
"Title": "UI.DataFieldAbstract",
|
|
3248
3249
|
"Description": "UI.DataFieldAbstract",
|
|
3250
|
+
"Image": "Edm.Stream",
|
|
3249
3251
|
"ImageUrl": "Edm.String",
|
|
3250
3252
|
"TypeImageUrl": "Edm.String",
|
|
3251
3253
|
"Initials": "Edm.String"
|
|
@@ -3693,7 +3695,7 @@
|
|
|
3693
3695
|
"$kind": "ComplexType",
|
|
3694
3696
|
"BaseType": "UI.DataFieldAbstract",
|
|
3695
3697
|
"Properties": {
|
|
3696
|
-
"Value": "Edm.
|
|
3698
|
+
"Value": "Edm.Untyped",
|
|
3697
3699
|
"Label": "Edm.String",
|
|
3698
3700
|
"Criticality": "UI.CriticalityType",
|
|
3699
3701
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
@@ -3704,8 +3706,8 @@
|
|
|
3704
3706
|
"$kind": "ComplexType",
|
|
3705
3707
|
"BaseType": "UI.DataField",
|
|
3706
3708
|
"Properties": {
|
|
3707
|
-
"Action": "Common.QualifiedName",
|
|
3708
3709
|
"Value": "Edm.PrimitiveType",
|
|
3710
|
+
"Action": "Common.QualifiedName",
|
|
3709
3711
|
"Label": "Edm.String",
|
|
3710
3712
|
"Criticality": "UI.CriticalityType",
|
|
3711
3713
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
@@ -3716,10 +3718,10 @@
|
|
|
3716
3718
|
"$kind": "ComplexType",
|
|
3717
3719
|
"BaseType": "UI.DataField",
|
|
3718
3720
|
"Properties": {
|
|
3721
|
+
"Value": "Edm.PrimitiveType",
|
|
3719
3722
|
"SemanticObject": "Edm.String",
|
|
3720
3723
|
"Action": "Edm.String",
|
|
3721
3724
|
"Mapping": "Collection(Common.SemanticObjectMappingType)",
|
|
3722
|
-
"Value": "Edm.PrimitiveType",
|
|
3723
3725
|
"Label": "Edm.String",
|
|
3724
3726
|
"Criticality": "UI.CriticalityType",
|
|
3725
3727
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
@@ -3730,8 +3732,8 @@
|
|
|
3730
3732
|
"$kind": "ComplexType",
|
|
3731
3733
|
"BaseType": "UI.DataField",
|
|
3732
3734
|
"Properties": {
|
|
3733
|
-
"Target": "Edm.NavigationPropertyPath",
|
|
3734
3735
|
"Value": "Edm.PrimitiveType",
|
|
3736
|
+
"Target": "Edm.NavigationPropertyPath",
|
|
3735
3737
|
"Label": "Edm.String",
|
|
3736
3738
|
"Criticality": "UI.CriticalityType",
|
|
3737
3739
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
@@ -3742,9 +3744,9 @@
|
|
|
3742
3744
|
"$kind": "ComplexType",
|
|
3743
3745
|
"BaseType": "UI.DataField",
|
|
3744
3746
|
"Properties": {
|
|
3747
|
+
"Value": "Edm.PrimitiveType",
|
|
3745
3748
|
"Url": "Edm.String",
|
|
3746
3749
|
"UrlContentType": "Edm.String",
|
|
3747
|
-
"Value": "Edm.PrimitiveType",
|
|
3748
3750
|
"Label": "Edm.String",
|
|
3749
3751
|
"Criticality": "UI.CriticalityType",
|
|
3750
3752
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
@@ -3755,8 +3757,8 @@
|
|
|
3755
3757
|
"$kind": "ComplexType",
|
|
3756
3758
|
"BaseType": "UI.DataField",
|
|
3757
3759
|
"Properties": {
|
|
3758
|
-
"Actions": "Collection(UI.DataField)",
|
|
3759
3760
|
"Value": "Edm.PrimitiveType",
|
|
3761
|
+
"Actions": "Collection(UI.DataField)",
|
|
3760
3762
|
"Label": "Edm.String",
|
|
3761
3763
|
"Criticality": "UI.CriticalityType",
|
|
3762
3764
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ca4caa65192cebc39f35cd598958dbb0
|