@sap/cds-compiler 2.7.0 → 2.11.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 +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/* eslint max-statements-per-line:off */
|
|
3
3
|
const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
4
|
-
const { forEachDefinition, forEachGeneric,
|
|
4
|
+
const { forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
5
5
|
isEdmPropertyRendered, getUtils, cloneCsn, isBuiltinType } = require('../model/csnUtils');
|
|
6
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
7
6
|
const edmUtils = require('./edmUtils.js');
|
|
8
7
|
const typesExposure = require('../transform/odata/typesExposure');
|
|
9
8
|
const expandCSNToFinalBaseType = require('../transform/odata/toFinalBaseType');
|
|
@@ -35,12 +34,11 @@ const {
|
|
|
35
34
|
* @param {CSN.Model} csn
|
|
36
35
|
* @param {object} _options
|
|
37
36
|
*/
|
|
38
|
-
function initializeModel(csn, _options)
|
|
37
|
+
function initializeModel(csn, _options, messageFunctions)
|
|
39
38
|
{
|
|
40
39
|
if (!_options)
|
|
41
40
|
throw Error('Please debug me: initializeModel must be invoked with options');
|
|
42
41
|
|
|
43
|
-
const messageFunctions = makeMessageFunction(csn, _options);
|
|
44
42
|
const { info, warning, error, throwWithError } = messageFunctions;
|
|
45
43
|
|
|
46
44
|
const csnUtils = getUtils(csn);
|
|
@@ -56,7 +54,7 @@ function initializeModel(csn, _options)
|
|
|
56
54
|
let options = validateOptions(_options);
|
|
57
55
|
|
|
58
56
|
// Fetch service definitions
|
|
59
|
-
const serviceRoots = Object.keys(csn.definitions).reduce((serviceRoots, artName) => {
|
|
57
|
+
const serviceRoots = Object.keys(csn.definitions || {}).reduce((serviceRoots, artName) => {
|
|
60
58
|
const art = csn.definitions[artName];
|
|
61
59
|
if(art.kind === 'service') {
|
|
62
60
|
serviceRoots[artName] = Object.assign(art, { name: artName });
|
|
@@ -69,6 +67,9 @@ function initializeModel(csn, _options)
|
|
|
69
67
|
function whatsMyServiceRootName(n, self=true) {
|
|
70
68
|
return serviceRootNames.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') || (n === sn && self) ? sn : rc, undefined);
|
|
71
69
|
}
|
|
70
|
+
if(serviceRootNames.length === 0) {
|
|
71
|
+
return [serviceRoots, Object.create(null), whatsMyServiceRootName, options];
|
|
72
|
+
}
|
|
72
73
|
|
|
73
74
|
// Structural CSN inbound QA checks
|
|
74
75
|
inboundQualificationChecks();
|
|
@@ -142,14 +143,18 @@ function initializeModel(csn, _options)
|
|
|
142
143
|
// must be run before proxy exposure to avoid potential reference collisions
|
|
143
144
|
convertExposedTypesOfOtherServicesIntoCrossReferences();
|
|
144
145
|
// create association target proxies
|
|
145
|
-
|
|
146
|
+
// Decide if an entity set needs to be constructed or not
|
|
147
|
+
forEachDefinition(csn, [ exposeTargetsAsProxiesOrSchemaRefs, determineEntitySet ]);
|
|
148
|
+
if(options.isV4())
|
|
149
|
+
forEachDefinition(csn, initializeEdmNavPropBindingTargets);
|
|
146
150
|
|
|
147
151
|
// Things that can be done in one pass
|
|
148
152
|
// Create edmKeyRefPaths
|
|
149
|
-
//
|
|
153
|
+
// Create NavigationPropertyBindings, requires determineEntitySet
|
|
150
154
|
// Map /** doc comments */ to @CoreDescription
|
|
151
155
|
// Artifact identifier spec compliance check (should be run last)
|
|
152
|
-
forEachDefinition(csn, [ initializeEdmKeyRefPaths,
|
|
156
|
+
forEachDefinition(csn, [ initializeEdmKeyRefPaths, initializeEdmNavPropBindingPaths,
|
|
157
|
+
initializeEdmTypesAndDescription, checkArtifactIdentifierAndBoundActions ]);
|
|
153
158
|
}
|
|
154
159
|
return [serviceRoots, schemas, whatsMyServiceRootName, options];
|
|
155
160
|
|
|
@@ -337,33 +342,57 @@ function initializeModel(csn, _options)
|
|
|
337
342
|
// entity set. Instead try to rewrite the annotation in such a way that it is effective
|
|
338
343
|
// on the containment navigation property.
|
|
339
344
|
function initializeContainments(container) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
345
|
+
if(['entity', 'view'].includes(container.kind)) {
|
|
346
|
+
forEachMemberRecursively(container, initContainments,
|
|
347
|
+
[], true, { elementsOnly: true });
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function initContainments(elt, eltName) {
|
|
351
|
+
if(isAssociationOrComposition(elt) && elt['@odata.contained'] && !elt._ignore) {
|
|
343
352
|
// Let the containee know its container
|
|
344
353
|
// (array because the contanee may contained more then once)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
354
|
+
let containee = elt._target;
|
|
355
|
+
if (!containee._containerEntity)
|
|
356
|
+
setProp(containee, '_containerEntity', []);
|
|
349
357
|
// add container only once per containee
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
358
|
+
if (!containee._containerEntity.includes(container.name))
|
|
359
|
+
containee._containerEntity.push(container.name);
|
|
360
|
+
// Mark associations in the containee pointing to the container (i.e. to this entity)
|
|
361
|
+
forEachMemberRecursively(containee, markToContainer,
|
|
362
|
+
[], true, { elementsOnly: true });
|
|
363
|
+
rewriteContainmentAnnotations(container, containee, eltName);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// try to find elements to drill down further
|
|
367
|
+
while(elt && !(isBuiltinType(elt.type) || elt.elements)) {
|
|
368
|
+
elt = csn.definitions[elt.type];
|
|
369
|
+
}
|
|
370
|
+
if(elt && elt.elements) {
|
|
371
|
+
forEachMemberRecursively(elt, initContainments,
|
|
372
|
+
[], true, { elementsOnly: true });
|
|
364
373
|
}
|
|
365
374
|
}
|
|
366
|
-
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function markToContainer(elt) {
|
|
378
|
+
if(elt._target && elt._target.name) {
|
|
379
|
+
// If this is an association that points to the container (but is not by itself contained,
|
|
380
|
+
// which would indicate the top role in a hierarchy) mark it with '_isToContainer'
|
|
381
|
+
if(elt._target.name === container.name && !elt['odata.contained']) {
|
|
382
|
+
setProp(elt, '_isToContainer', true);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
// try to find elements to drill down further
|
|
387
|
+
while(elt && !(isBuiltinType(elt.type) || elt.elements)) {
|
|
388
|
+
elt = csn.definitions[elt.type];
|
|
389
|
+
}
|
|
390
|
+
if(elt && elt.elements) {
|
|
391
|
+
forEachMemberRecursively(elt, markToContainer,
|
|
392
|
+
[], true, { elementsOnly: true });
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
367
396
|
}
|
|
368
397
|
|
|
369
398
|
// Split an entity with parameters into two entity types with their entity sets,
|
|
@@ -420,7 +449,6 @@ function initializeModel(csn, _options)
|
|
|
420
449
|
assignProp(parameterCsn, '_SetAttributes',
|
|
421
450
|
{'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.pageable': false });
|
|
422
451
|
|
|
423
|
-
assignProp(parameterCsn, '$keys', Object.create(null));
|
|
424
452
|
setProp(parameterCsn, '$isParamEntity', true);
|
|
425
453
|
setProp(parameterCsn, '$mySchemaName', entityCsn.$mySchemaName);
|
|
426
454
|
|
|
@@ -438,9 +466,10 @@ function initializeModel(csn, _options)
|
|
|
438
466
|
elt.name = n;
|
|
439
467
|
delete elt.kind;
|
|
440
468
|
elt.key = true; // params become primary key in parameter entity
|
|
441
|
-
parameterCsn
|
|
469
|
+
parameterCsn.elements[n] = elt;
|
|
442
470
|
});
|
|
443
|
-
|
|
471
|
+
linkAssociationTarget(parameterCsn);
|
|
472
|
+
initializeContainments(parameterCsn);
|
|
444
473
|
// add assoc to result set, FIXME: is the cardinality correct?
|
|
445
474
|
parameterCsn.elements[parameterToOriginalAssocName] = {
|
|
446
475
|
'@odata.contained': true,
|
|
@@ -543,16 +572,17 @@ function initializeModel(csn, _options)
|
|
|
543
572
|
let keys = Object.create(null);
|
|
544
573
|
let validFrom = [], validKey = [];
|
|
545
574
|
|
|
546
|
-
let structParent = def.items || def;
|
|
547
|
-
|
|
548
575
|
// Iterate all struct elements
|
|
549
|
-
|
|
550
|
-
|
|
576
|
+
forEachMemberRecursively(def.items || def, (element, elementName, prop, path = [], construct) => {
|
|
577
|
+
if(!['elements'].includes(prop))
|
|
578
|
+
return;
|
|
579
|
+
|
|
580
|
+
initElement(element, elementName, construct);
|
|
551
581
|
|
|
552
582
|
if(!['event', 'aspect'].includes(def.kind)) {
|
|
553
583
|
if(element._parent && element._parent.$mySchemaName) {
|
|
554
584
|
if(!isODataSimpleIdentifier(elementName)) {
|
|
555
|
-
signalIllegalIdentifier(elementName, ['definitions', def.name
|
|
585
|
+
signalIllegalIdentifier(elementName, ['definitions', def.name].concat(path));
|
|
556
586
|
} else if (options.isV2() && /^(_|[0-9])/.test(elementName) && ['view', 'entity'].includes(element._parent.kind)) {
|
|
557
587
|
// FIXME: Rewrite signalIllegalIdentifier function to be more flexible
|
|
558
588
|
error(null, ['definitions', def.name, 'elements', elementName], { prop: elementName[0] },
|
|
@@ -567,6 +597,16 @@ function initializeModel(csn, _options)
|
|
|
567
597
|
if(element['@cds.valid.from']) {
|
|
568
598
|
validFrom.push(element);
|
|
569
599
|
}
|
|
600
|
+
//forward annotations from managed association element to its foreign keys
|
|
601
|
+
const elements = construct.items && construct.items.elements || construct.elements;
|
|
602
|
+
forAll(elements[element['@odata.foreignKey4']], (attr, attrName) => {
|
|
603
|
+
if(attrName[0] === '@') {
|
|
604
|
+
element[attrName] = attr;
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// and eventually remove some afterwards:)
|
|
608
|
+
if(options.isV2())
|
|
609
|
+
setSAPSpecificV2AnnotationsToAssociation(element);
|
|
570
610
|
|
|
571
611
|
// initialize an association
|
|
572
612
|
if(isAssociationOrComposition(element)) {
|
|
@@ -575,18 +615,6 @@ function initializeModel(csn, _options)
|
|
|
575
615
|
assignProp(element._target, '$proxies', []);
|
|
576
616
|
// $abspath is used as partner path
|
|
577
617
|
assignProp(element, '$abspath', $path2path(element.$path));
|
|
578
|
-
|
|
579
|
-
//forward annotations from managed association element to its foreign keys
|
|
580
|
-
if(element.keys && options.isFlatFormat) {
|
|
581
|
-
for(let fk of element.keys) {
|
|
582
|
-
forAll(element, (attr, attrName) => {
|
|
583
|
-
if(attrName[0] === '@' && fk.$generatedFieldName)
|
|
584
|
-
def.elements[fk.$generatedFieldName][attrName] = attr;
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
// and afterwards eventually remove some :)
|
|
589
|
-
setSAPSpecificV2AnnotationsToAssociation(options, element, def);
|
|
590
618
|
}
|
|
591
619
|
|
|
592
620
|
// Collect keys
|
|
@@ -594,7 +622,7 @@ function initializeModel(csn, _options)
|
|
|
594
622
|
keys[elementName] = element;
|
|
595
623
|
}
|
|
596
624
|
applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
|
|
597
|
-
});
|
|
625
|
+
}, [], true, { elementsOnly: true });
|
|
598
626
|
|
|
599
627
|
if(!isDeprecatedEnabled(options, 'v1KeysForTemporal')) {
|
|
600
628
|
// if artifact has a cds.valid.key mention it as @Core.AlternateKey
|
|
@@ -646,14 +674,14 @@ function initializeModel(csn, _options)
|
|
|
646
674
|
if(!isStructuredArtifact(struct))
|
|
647
675
|
return;
|
|
648
676
|
|
|
649
|
-
|
|
677
|
+
forEachMemberRecursively(struct.items || struct, (element) => {
|
|
650
678
|
if (isAssociationOrComposition(element) && !element._ignore) {
|
|
651
679
|
// setup the constraints object
|
|
652
680
|
setProp(element, '_constraints', { constraints: Object.create(null), selfs: [], _origins: [], termCount: 0 });
|
|
653
681
|
// and crack the ON condition
|
|
654
682
|
resolveOnConditionAndPrepareConstraints(csn, element, messageFunctions);
|
|
655
683
|
}
|
|
656
|
-
});
|
|
684
|
+
}, [], true, { elementsOnly: true });
|
|
657
685
|
}
|
|
658
686
|
|
|
659
687
|
/*
|
|
@@ -669,13 +697,16 @@ function initializeModel(csn, _options)
|
|
|
669
697
|
4) All of this can be revoked with options.renderForeignKeys.
|
|
670
698
|
*/
|
|
671
699
|
function ignoreProperties(struct) {
|
|
672
|
-
|
|
700
|
+
if(!isStructuredArtifact(struct))
|
|
701
|
+
return;
|
|
702
|
+
|
|
703
|
+
forEachMemberRecursively(struct.items || struct, (element) => {
|
|
673
704
|
if(!element.target) {
|
|
674
705
|
if(element['@odata.foreignKey4']) {
|
|
675
706
|
let isContainerAssoc = false;
|
|
676
|
-
let elements = struct.elements;
|
|
707
|
+
let elements = (struct.items || struct).elements;
|
|
677
708
|
let assoc = undefined;
|
|
678
|
-
|
|
709
|
+
const paths = element['@odata.foreignKey4'].split('.')
|
|
679
710
|
for(let p of paths) {
|
|
680
711
|
assoc = elements[p];
|
|
681
712
|
if(assoc) // could be that the @odata.foreignKey4 was propagated...
|
|
@@ -714,7 +745,7 @@ function initializeModel(csn, _options)
|
|
|
714
745
|
// ignore it if option odataContainment is true and no foreign keys should be rendered
|
|
715
746
|
assignAnnotation(element, '@odata.navigable', false);
|
|
716
747
|
}
|
|
717
|
-
});
|
|
748
|
+
}, [], true, { elementsOnly: true });
|
|
718
749
|
}
|
|
719
750
|
|
|
720
751
|
/*
|
|
@@ -726,7 +757,7 @@ function initializeModel(csn, _options)
|
|
|
726
757
|
if(!isStructuredArtifact(struct))
|
|
727
758
|
return;
|
|
728
759
|
|
|
729
|
-
|
|
760
|
+
forEachMemberRecursively(struct.items || struct, (element) => {
|
|
730
761
|
if (isAssociationOrComposition(element) && !element._ignore) {
|
|
731
762
|
finalizeReferentialConstraints(csn, element, options, info);
|
|
732
763
|
|
|
@@ -753,7 +784,7 @@ function initializeModel(csn, _options)
|
|
|
753
784
|
}
|
|
754
785
|
}
|
|
755
786
|
}
|
|
756
|
-
});
|
|
787
|
+
}, [], true, { elementsOnly: true });
|
|
757
788
|
}
|
|
758
789
|
|
|
759
790
|
/*
|
|
@@ -816,7 +847,7 @@ function initializeModel(csn, _options)
|
|
|
816
847
|
const globalSchemaPrefix = whatsMyServiceRootName(struct.$mySchemaName);
|
|
817
848
|
// if this artifact is a service member check its associations
|
|
818
849
|
if(globalSchemaPrefix) {
|
|
819
|
-
forEachGeneric(struct, 'elements', element => {
|
|
850
|
+
forEachGeneric(struct.items || struct, 'elements', element => {
|
|
820
851
|
if(!isAssociationOrComposition(element) || element._ignore || element['@odata.navigable'] === false)
|
|
821
852
|
return;
|
|
822
853
|
/*
|
|
@@ -874,7 +905,7 @@ function initializeModel(csn, _options)
|
|
|
874
905
|
}
|
|
875
906
|
else {
|
|
876
907
|
// fake the target to be proxy
|
|
877
|
-
element._target
|
|
908
|
+
setProp(element._target, '$externalRef', true);
|
|
878
909
|
}
|
|
879
910
|
}
|
|
880
911
|
else {
|
|
@@ -923,7 +954,6 @@ function initializeModel(csn, _options)
|
|
|
923
954
|
setProp(proxy, '$keys', Object.create(null));
|
|
924
955
|
setProp(proxy, '$hasEntitySet', false);
|
|
925
956
|
setProp(proxy, '$exposedTypes', Object.create(null));
|
|
926
|
-
|
|
927
957
|
// copy all annotations of the target to the proxy
|
|
928
958
|
Object.entries(assoc._target).forEach(([k, v]) => {
|
|
929
959
|
if(k[0] === '@')
|
|
@@ -1070,10 +1100,12 @@ function initializeModel(csn, _options)
|
|
|
1070
1100
|
if(!elem.target) {
|
|
1071
1101
|
type.elements[elemName] = Object.create(null);
|
|
1072
1102
|
Object.keys(elem).forEach(prop => type.elements[elemName][prop] = elem[prop])
|
|
1103
|
+
type.elements[elemName].notNull = true;
|
|
1073
1104
|
}
|
|
1074
1105
|
else {
|
|
1075
1106
|
type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
|
|
1076
1107
|
}
|
|
1108
|
+
setProp(type.elements[elemName], 'name', elem.name);
|
|
1077
1109
|
});
|
|
1078
1110
|
return type;
|
|
1079
1111
|
}
|
|
@@ -1100,6 +1132,7 @@ function initializeModel(csn, _options)
|
|
|
1100
1132
|
// art is in the target side, clone it and remove key property
|
|
1101
1133
|
let cloneArt = cloneCsn(art, options);
|
|
1102
1134
|
setProp(cloneArt, 'name', art.name);
|
|
1135
|
+
cloneArt.notNull = true;
|
|
1103
1136
|
delete cloneArt.key;
|
|
1104
1137
|
newElt.elements[art.name] = cloneArt;
|
|
1105
1138
|
});
|
|
@@ -1189,14 +1222,14 @@ function initializeModel(csn, _options)
|
|
|
1189
1222
|
function registerProxy(proxy, element) {
|
|
1190
1223
|
if(proxy === undefined)
|
|
1191
1224
|
return undefined;
|
|
1192
|
-
const
|
|
1193
|
-
const
|
|
1225
|
+
const fqProxyName = globalSchemaPrefix + '.' + proxy.name;
|
|
1226
|
+
const fqSchemaName = globalSchemaPrefix + '.' + proxy.$mySchemaName;
|
|
1194
1227
|
|
|
1195
1228
|
if(!element._target.$cachedProxy)
|
|
1196
1229
|
assignProp(element._target, '$cachedProxy', Object.create(null));
|
|
1197
1230
|
if(getProxyForTargetOf(element)) {
|
|
1198
1231
|
info(null, ['definitions', struct.name, 'elements', element.name],
|
|
1199
|
-
{ name:
|
|
1232
|
+
{ name: fqProxyName }, 'Proxy EDM entity type $(NAME) has already been registered');
|
|
1200
1233
|
}
|
|
1201
1234
|
else
|
|
1202
1235
|
element._target.$cachedProxy[globalSchemaPrefix] = proxy;
|
|
@@ -1206,7 +1239,7 @@ function initializeModel(csn, _options)
|
|
|
1206
1239
|
// (that may reside in another subcontext schema), but only once
|
|
1207
1240
|
const schemaSet = new Set();
|
|
1208
1241
|
// start with the schema name for the proxy
|
|
1209
|
-
schemaSet.add(
|
|
1242
|
+
schemaSet.add(fqSchemaName);
|
|
1210
1243
|
// followed by all namespaces that are potentially exposed by the exposed types
|
|
1211
1244
|
// don't forget to prepend the global namespace prefix
|
|
1212
1245
|
// schemas are ordered in csn2edm.js for each service
|
|
@@ -1219,12 +1252,16 @@ function initializeModel(csn, _options)
|
|
|
1219
1252
|
}
|
|
1220
1253
|
});
|
|
1221
1254
|
/** @type {object} */
|
|
1222
|
-
const alreadyRegistered = csn.definitions[
|
|
1255
|
+
const alreadyRegistered = csn.definitions[fqProxyName]
|
|
1223
1256
|
if(!alreadyRegistered) {
|
|
1224
|
-
csn.definitions[
|
|
1257
|
+
csn.definitions[fqProxyName] = proxy;
|
|
1258
|
+
setProp(proxy, '$path', ['definitions', fqProxyName]);
|
|
1225
1259
|
Object.entries(proxy.$exposedTypes).forEach(([tn, v]) => {
|
|
1226
|
-
|
|
1227
|
-
|
|
1260
|
+
const fqtn = globalSchemaPrefix + '.' + tn;
|
|
1261
|
+
if(csn.definitions[fqtn] === undefined) {
|
|
1262
|
+
csn.definitions[fqtn] = v;
|
|
1263
|
+
setProp(v, '$path', ['definitions', fqtn]);
|
|
1264
|
+
}
|
|
1228
1265
|
});
|
|
1229
1266
|
info(null, ['definitions', element._parent.name, 'elements', element.name],
|
|
1230
1267
|
{ name: proxy.name }, 'Created proxy EDM entity type $(NAME)');
|
|
@@ -1232,16 +1269,16 @@ function initializeModel(csn, _options)
|
|
|
1232
1269
|
else if(alreadyRegistered && !alreadyRegistered.$proxy &&
|
|
1233
1270
|
!['entity', 'view'].includes(alreadyRegistered.kind)) {
|
|
1234
1271
|
warning(null, ['definitions', element._parent.name, 'elements', element.name],
|
|
1235
|
-
{ name:
|
|
1272
|
+
{ name: fqProxyName, kind: alreadyRegistered.kind },
|
|
1236
1273
|
'No proxy EDM entity type created due to name collision with $(NAME) of kind $(KIND)');
|
|
1237
1274
|
return undefined;
|
|
1238
1275
|
}
|
|
1239
1276
|
}
|
|
1240
1277
|
else {
|
|
1241
1278
|
// it's a service reference, just add that reference proxy
|
|
1242
|
-
if(!schemas[
|
|
1243
|
-
schemas[
|
|
1244
|
-
schemaNames.push(
|
|
1279
|
+
if(!schemas[fqSchemaName]) {
|
|
1280
|
+
schemas[fqSchemaName] = proxy;
|
|
1281
|
+
schemaNames.push(fqSchemaName);
|
|
1245
1282
|
info(null, ['definitions', struct.name, 'elements', element.name],
|
|
1246
1283
|
{ name: proxy.name }, 'Created EDM namespace reference $(NAME)');
|
|
1247
1284
|
}
|
|
@@ -1286,11 +1323,8 @@ function initializeModel(csn, _options)
|
|
|
1286
1323
|
else if(!k.target) {
|
|
1287
1324
|
struct.$edmKeyPaths.push([kn]);
|
|
1288
1325
|
}
|
|
1289
|
-
//
|
|
1290
|
-
|
|
1291
|
-
const pathToElement = ['definitions', struct.name, 'elements', k.name];
|
|
1292
|
-
signalErrorForNullableKey(pathToElement);
|
|
1293
|
-
}
|
|
1326
|
+
// check toplevel key for spec violations
|
|
1327
|
+
checkKeySpecViolations(k, ['definitions', struct.name, 'elements', k.name]);
|
|
1294
1328
|
}
|
|
1295
1329
|
});
|
|
1296
1330
|
}
|
|
@@ -1306,7 +1340,7 @@ function initializeModel(csn, _options)
|
|
|
1306
1340
|
If element is of scalar type, return it as an array.
|
|
1307
1341
|
*/
|
|
1308
1342
|
function produceKeyRefPaths(eltCsn, prefix) {
|
|
1309
|
-
|
|
1343
|
+
const keyPaths = [];
|
|
1310
1344
|
if(!isEdmPropertyRendered(eltCsn, options)) {
|
|
1311
1345
|
// let annos = Object.keys(eltCsn).filter(a=>a[0]==='@').join(', ');
|
|
1312
1346
|
// warning(null, ['definitions', struct.name, 'elements', eltCsn.name ],
|
|
@@ -1314,15 +1348,20 @@ function initializeModel(csn, _options)
|
|
|
1314
1348
|
return keyPaths;
|
|
1315
1349
|
}
|
|
1316
1350
|
// OData requires all elements along the path to be nullable: false (that is either key or notNull)
|
|
1317
|
-
|
|
1351
|
+
|
|
1352
|
+
const finalType = getFinalTypeDef(eltCsn.items && eltCsn.items.type || eltCsn.type);
|
|
1353
|
+
const elements = eltCsn.elements || eltCsn.items && eltCsn.items.elements ||
|
|
1354
|
+
(finalType && (finalType.elements || finalType.items && finalType.items.elements));
|
|
1318
1355
|
if(elements) {
|
|
1319
1356
|
Object.entries(elements).forEach(([eltName, elt]) => {
|
|
1320
|
-
|
|
1321
|
-
if(
|
|
1322
|
-
|
|
1357
|
+
const newRefs = produceKeyRefPaths(elt, prefix + options.pathDelimiter + eltName);
|
|
1358
|
+
if(newRefs.length) {
|
|
1359
|
+
keyPaths.push(...newRefs);
|
|
1360
|
+
// check path step key for spec violations
|
|
1361
|
+
const pathSegment = `${prefix}/${eltName}`;
|
|
1323
1362
|
// we want to point to the element in the entity which is the first path step
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1363
|
+
const location = struct.$path.concat(['elements']).concat(pathSegment.split('/')[0]);
|
|
1364
|
+
checkKeySpecViolations(elt, location, pathSegment);
|
|
1326
1365
|
}
|
|
1327
1366
|
});
|
|
1328
1367
|
}
|
|
@@ -1353,17 +1392,169 @@ function initializeModel(csn, _options)
|
|
|
1353
1392
|
return keyPaths;
|
|
1354
1393
|
}
|
|
1355
1394
|
|
|
1356
|
-
function
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1395
|
+
function checkKeySpecViolations(elt, location, pathSegment) {
|
|
1396
|
+
// Nullability
|
|
1397
|
+
if((!elt.key && (elt.notNull === undefined || elt.notNull === false)) ||
|
|
1398
|
+
elt.key && (elt.notNull !== undefined && elt.notNull === false)) {
|
|
1399
|
+
error('odata-spec-violation-key-null', location,
|
|
1400
|
+
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1401
|
+
}
|
|
1402
|
+
// many
|
|
1403
|
+
let type = elt.items || elt.type && !isBuiltinType(elt.type) && getFinalTypeDef(elt.type).items;
|
|
1404
|
+
if(type) {
|
|
1405
|
+
error('odata-spec-violation-key-array', location,
|
|
1406
|
+
{name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
|
|
1407
|
+
}
|
|
1408
|
+
// type
|
|
1409
|
+
if(!elt.elements) {
|
|
1410
|
+
if(!type)
|
|
1411
|
+
type = isBuiltinType(elt.type) ? elt : csn.definitions[elt.type];
|
|
1412
|
+
|
|
1413
|
+
// check for legal scalar types, proxy exposed structured types are not resolvable in CSN
|
|
1414
|
+
// V2 allows any Edm.PrimitiveType (even Double and Binary), V4 is more specific:
|
|
1415
|
+
if(options.isV4() && type && !isAssociationOrComposition(type) && isBuiltinType(type.type)) {
|
|
1416
|
+
const edmType = edmUtils.mapCdsToEdmType(type);
|
|
1417
|
+
const legalEdmTypes = [
|
|
1418
|
+
'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Duration',
|
|
1419
|
+
'Edm.Guid', 'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte', 'Edm.String', 'Edm.TimeOfDay' ];
|
|
1420
|
+
if(!legalEdmTypes.includes(edmType)) {
|
|
1421
|
+
warning('odata-spec-violation-key-type', location,
|
|
1422
|
+
{name: pathSegment, type: type.type, id: edmType, '#': pathSegment ? 'std' : 'scalar'});
|
|
1423
|
+
}
|
|
1362
1424
|
}
|
|
1363
|
-
|
|
1425
|
+
}
|
|
1364
1426
|
}
|
|
1365
1427
|
}
|
|
1366
1428
|
|
|
1429
|
+
/*
|
|
1430
|
+
Calculate all reachable entity set paths for a given navigation start point
|
|
1431
|
+
|
|
1432
|
+
Rule: First non-containment association terminates Path, if association is
|
|
1433
|
+
containment enabling assoc, Target is own Struct/ plus the path down to the
|
|
1434
|
+
n-2nd path segment (which is the path to the n-1st implicit entity set).
|
|
1435
|
+
|
|
1436
|
+
Example:
|
|
1437
|
+
entity Header {
|
|
1438
|
+
items: composition of many {
|
|
1439
|
+
toF: association to F;
|
|
1440
|
+
subitems: composition of many {
|
|
1441
|
+
toG: association to G;
|
|
1442
|
+
subitems: composition of many {
|
|
1443
|
+
toG: association to G;
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
Must produce:
|
|
1449
|
+
Path="items/up_" Target="Header"/>
|
|
1450
|
+
Path="items/toF" Target="F"/>
|
|
1451
|
+
Path="items/subitems/up_" Target="Header/items"/>
|
|
1452
|
+
Path="items/subitems/toG" Target="G"/>
|
|
1453
|
+
Path="items/subitems/subitems/up_" Target="Header/items/subitems"/>
|
|
1454
|
+
Path="items/subitems/subitems/toG" Target="G"/>
|
|
1455
|
+
*/
|
|
1456
|
+
function initializeEdmNavPropBindingTargets(struct) {
|
|
1457
|
+
if(options.isV4() && struct.$mySchemaName && struct.$hasEntitySet) {
|
|
1458
|
+
forEachGeneric(struct.items || struct, 'elements', (element) => {
|
|
1459
|
+
produceTargetPath([edmUtils.getBaseName(struct.name)], element, struct);
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
function produceTargetPath(prefix, elt, curDef) {
|
|
1464
|
+
const newPrefix = [...prefix, elt.name];
|
|
1465
|
+
if(isEdmPropertyRendered(elt, options)) {
|
|
1466
|
+
// Assoc can never be a derived TypeDefinition, no need to
|
|
1467
|
+
// unroll derived type chains for assocs
|
|
1468
|
+
if(isAssociationOrComposition(elt) && !elt.$touched) {
|
|
1469
|
+
if(!elt._target.$edmTgtPaths)
|
|
1470
|
+
setProp(elt._target, '$edmTgtPaths', []);
|
|
1471
|
+
if(!elt._target.$hasEntitySet && !elt._isToContainer && curDef !== elt._target) {
|
|
1472
|
+
// follow elements in the target but avoid cycles
|
|
1473
|
+
setProp(elt, '$touched', true);
|
|
1474
|
+
elt._target.$edmTgtPaths.push(newPrefix);
|
|
1475
|
+
Object.values(elt._target.elements).forEach(e => produceTargetPath(newPrefix, e, elt._target));
|
|
1476
|
+
delete elt.$touched;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
else {
|
|
1480
|
+
// try to find elements to drill down further
|
|
1481
|
+
while(elt && !(isBuiltinType(elt.type) || elt.elements)) {
|
|
1482
|
+
elt = csn.definitions[elt.type];
|
|
1483
|
+
}
|
|
1484
|
+
elt && elt.elements && Object.values(elt.elements).forEach(e => produceTargetPath(newPrefix, e, curDef));
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function initializeEdmNavPropBindingPaths(struct) {
|
|
1491
|
+
if(options.isV4() && struct.$mySchemaName && struct.$hasEntitySet) {
|
|
1492
|
+
let npbs = [];
|
|
1493
|
+
forEachGeneric(struct.items || struct, 'elements', (element) => {
|
|
1494
|
+
npbs = npbs.concat(produceNavigationPath(element, struct));
|
|
1495
|
+
});
|
|
1496
|
+
setProp(struct, '$edmNPBs', npbs);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// collect all paths originating from this element that end up in an entity set
|
|
1500
|
+
function produceNavigationPath(elt, curDef) {
|
|
1501
|
+
let npbs = [];
|
|
1502
|
+
const prefix = elt.name;
|
|
1503
|
+
if(isEdmPropertyRendered(elt, options)) {
|
|
1504
|
+
// Assoc can never be a derived TypeDefinition, no need to
|
|
1505
|
+
// unroll derived type chains for assocs
|
|
1506
|
+
if(isAssociationOrComposition(elt) && !elt.$touched) {
|
|
1507
|
+
// drill into target only if
|
|
1508
|
+
// 1) target has no entity set and this assoc is not going to the container
|
|
1509
|
+
// 2) current definition and target are the same (cycle)
|
|
1510
|
+
if(!elt._target.$hasEntitySet && !elt._isToContainer && curDef !== elt._target) {
|
|
1511
|
+
// follow elements in the target but avoid cycles
|
|
1512
|
+
setProp(elt, '$touched', true);
|
|
1513
|
+
Object.values(elt._target.elements).forEach(e => npbs = npbs.concat(produceNavigationPath(e, elt._target)));
|
|
1514
|
+
delete elt.$touched;
|
|
1515
|
+
}
|
|
1516
|
+
else if(!(options.odataContainment && options.isV4() && elt['@odata.contained'])) {
|
|
1517
|
+
// end point reached but must not be an external reference nor a proxy nor a composition itself
|
|
1518
|
+
// last assoc step must not be to-n and target a singleton
|
|
1519
|
+
let p = undefined;
|
|
1520
|
+
if (!elt._target.$externalRef &&
|
|
1521
|
+
!(edmUtils.isToMany(elt) && edmUtils.isSingleton(elt._target, options.isV4()))) {
|
|
1522
|
+
if(elt._target.$edmTgtPaths && elt._target.$edmTgtPaths.length) {
|
|
1523
|
+
p = elt._target.$edmTgtPaths.find(p => p[0] === edmUtils.getBaseName(struct.name)) || elt._target.$edmTgtPaths[0];
|
|
1524
|
+
}
|
|
1525
|
+
else if(elt._target.$hasEntitySet) {
|
|
1526
|
+
const baseName = edmUtils.getBaseName(elt._target.$entitySetName || elt._target.name);
|
|
1527
|
+
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1528
|
+
// if target is in another schema, target the full qualified entity set
|
|
1529
|
+
p = (elt._target.$mySchemaName === struct.$mySchemaName) ?
|
|
1530
|
+
[ baseName ] : [elt._target.$mySchemaName + '.EntityContainer', baseName];
|
|
1531
|
+
}
|
|
1532
|
+
if(p) {
|
|
1533
|
+
// if own struct and target have a set they either are in the same $mySchemaName or not
|
|
1534
|
+
// if target is in another schema, target the full qualified entity set
|
|
1535
|
+
const npb = {
|
|
1536
|
+
Path: elt.name,
|
|
1537
|
+
Target: p.join('/')
|
|
1538
|
+
};
|
|
1539
|
+
npbs.push( npb );
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
// Do not prepend prefix here!
|
|
1543
|
+
return npbs;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
else {
|
|
1547
|
+
// try to find elements to drill down further
|
|
1548
|
+
while(elt && !(isBuiltinType(elt.type) || elt.elements)) {
|
|
1549
|
+
elt = csn.definitions[elt.type];
|
|
1550
|
+
}
|
|
1551
|
+
elt && elt.elements && Object.values(elt.elements).forEach(e => npbs = npbs.concat(produceNavigationPath(e, curDef)));
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
npbs.forEach(p => p.Path = prefix + '/' + p.Path );
|
|
1555
|
+
return npbs;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1367
1558
|
|
|
1368
1559
|
function determineEntitySet(struct) {
|
|
1369
1560
|
// if this is an entity or a view, determine if an entity set is required or not
|
|
@@ -1827,7 +2018,7 @@ function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error
|
|
|
1827
2018
|
let key = { name: keyName, key : true, type : 'cds.String', '@sap.sortable':false, '@sap.filterable':false, '@UI.Hidden': true };
|
|
1828
2019
|
elements[keyName] = key;
|
|
1829
2020
|
setProp(struct, '$keys',{ [keyName] : key } );
|
|
1830
|
-
forEachGeneric(struct, 'elements', (e,n) =>
|
|
2021
|
+
forEachGeneric(struct.items || struct, 'elements', (e,n) =>
|
|
1831
2022
|
{
|
|
1832
2023
|
if(e.key) delete e.key;
|
|
1833
2024
|
elements[n] = e;
|
|
@@ -1967,19 +2158,17 @@ function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
|
1967
2158
|
}
|
|
1968
2159
|
}
|
|
1969
2160
|
|
|
1970
|
-
function setSAPSpecificV2AnnotationsToAssociation(
|
|
1971
|
-
if(!options.isV2())
|
|
1972
|
-
return;
|
|
2161
|
+
function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
1973
2162
|
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0
|
|
1974
2163
|
const SetAttributes = {
|
|
1975
2164
|
// Applicable to NavProp and foreign keys, add to AssociationSet
|
|
1976
|
-
'@sap.creatable' : (
|
|
2165
|
+
'@sap.creatable' : (c, pn, pv) => { addToAssociationSet(c, pn, pv, false); },
|
|
1977
2166
|
// Not applicable to NavProp, applicable to foreign keys, add to AssociationSet
|
|
1978
|
-
'@sap.updatable' :
|
|
2167
|
+
'@sap.updatable' : addToAssociationSet,
|
|
1979
2168
|
// Not applicable to NavProp, not applicable to foreign key, add to AssociationSet
|
|
1980
|
-
'@sap.deletable': (
|
|
1981
|
-
|
|
1982
|
-
removeFromForeignKey(
|
|
2169
|
+
'@sap.deletable': (c, pn, pv) => {
|
|
2170
|
+
addToAssociationSet(c, pn, pv);
|
|
2171
|
+
removeFromForeignKey(c, pn);
|
|
1983
2172
|
},
|
|
1984
2173
|
// applicable to NavProp, not applicable to foreign keys, not applicable to AssociationSet
|
|
1985
2174
|
'@sap.creatable.path': removeFromForeignKey,
|
|
@@ -1987,24 +2176,22 @@ function setSAPSpecificV2AnnotationsToAssociation(options, carrier, struct) {
|
|
|
1987
2176
|
};
|
|
1988
2177
|
|
|
1989
2178
|
Object.entries(carrier).forEach(([p, v]) => {
|
|
1990
|
-
(SetAttributes[p] || function() {/* no-op */})(
|
|
2179
|
+
(SetAttributes[p] || function() {/* no-op */})(carrier, p, v);
|
|
1991
2180
|
});
|
|
1992
2181
|
|
|
1993
|
-
function
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2182
|
+
function addToAssociationSet(carrier, propName, propValue, removeFromType=true) {
|
|
2183
|
+
if(isAssociationOrComposition(carrier)) {
|
|
2184
|
+
assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
2185
|
+
assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
2186
|
+
if(removeFromType) {
|
|
2187
|
+
delete carrier[propName];
|
|
2188
|
+
}
|
|
1998
2189
|
}
|
|
1999
2190
|
}
|
|
2000
2191
|
|
|
2001
|
-
function removeFromForeignKey(
|
|
2002
|
-
if(carrier.
|
|
2003
|
-
|
|
2004
|
-
if(e['@odata.foreignKey4'] === carrier.name) {
|
|
2005
|
-
delete e[propName];
|
|
2006
|
-
}
|
|
2007
|
-
});
|
|
2192
|
+
function removeFromForeignKey(carrier, propName) {
|
|
2193
|
+
if(carrier['@odata.foreignKey4'] && carrier[propName] !== undefined) {
|
|
2194
|
+
delete carrier[propName];
|
|
2008
2195
|
}
|
|
2009
2196
|
}
|
|
2010
2197
|
}
|