@sap/cds-compiler 2.13.6 → 2.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +128 -4
- package/bin/cdsc.js +112 -37
- package/lib/api/main.js +20 -22
- package/lib/api/options.js +2 -3
- package/lib/api/validate.js +6 -6
- package/lib/base/message-registry.js +92 -17
- package/lib/base/messages.js +85 -64
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/validator.js +2 -4
- package/lib/compiler/assert-consistency.js +1 -0
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +11 -0
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +59 -11
- package/lib/compiler/extend.js +20 -3
- package/lib/compiler/finalize-parse-cdl.js +26 -20
- package/lib/compiler/index.js +75 -26
- package/lib/compiler/populate.js +6 -5
- package/lib/compiler/propagator.js +4 -1
- package/lib/compiler/resolve.js +104 -16
- package/lib/compiler/shared.js +61 -27
- package/lib/compiler/tweak-assocs.js +7 -1
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +216 -98
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +499 -423
- package/lib/edm/edmUtils.js +22 -22
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageParser.js +4636 -4368
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +0 -2
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +47 -2
- package/lib/language/language.g4 +59 -27
- package/lib/main.d.ts +19 -1
- package/lib/main.js +6 -0
- package/lib/model/csnRefs.js +33 -6
- package/lib/model/csnUtils.js +193 -75
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +2 -2
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +62 -26
- package/lib/render/toCdl.js +844 -679
- package/lib/render/toHdbcds.js +189 -243
- package/lib/render/toSql.js +180 -198
- package/lib/render/utils/common.js +131 -15
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/constraints.js +3 -1
- package/lib/transform/db/expansion.js +15 -10
- package/lib/transform/db/flattening.js +95 -68
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +6 -3
- package/lib/transform/forHanaNew.js +43 -26
- package/lib/transform/forOdataNew.js +43 -42
- package/lib/transform/localized.js +12 -7
- package/lib/transform/odata/toFinalBaseType.js +8 -6
- package/lib/transform/odata/typesExposure.js +145 -197
- package/lib/transform/transformUtilsNew.js +9 -12
- package/lib/transform/translateAssocsToJoins.js +5 -1
- package/lib/transform/universalCsn/coreComputed.js +5 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
- package/lib/utils/moduleResolve.js +13 -6
- package/package.json +1 -1
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
|
@@ -4,15 +4,16 @@ const edmUtils = require('../edmUtils.js');
|
|
|
4
4
|
const preprocessAnnotations = require('./preprocessAnnotations.js');
|
|
5
5
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
6
6
|
const { forEachDefinition } = require('../../model/csnUtils');
|
|
7
|
+
const { forEach } = require("../../utils/objectUtils");
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
/*
|
|
10
|
-
|
|
10
|
+
/*
|
|
11
11
|
OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/master/vocabularies
|
|
12
12
|
Aggregation (published)
|
|
13
13
|
Authorization (published)
|
|
14
14
|
Capabilities (published)
|
|
15
15
|
Core (published)
|
|
16
|
+
JSON (published)
|
|
16
17
|
Measures (published)
|
|
17
18
|
Repeatability (published)
|
|
18
19
|
Temporal (not published, not yet finalized)
|
|
@@ -23,8 +24,9 @@ const { forEachDefinition } = require('../../model/csnUtils');
|
|
|
23
24
|
CodeList (published)
|
|
24
25
|
Common (pubished)
|
|
25
26
|
Communication (published)
|
|
27
|
+
DataIntegration (published)
|
|
26
28
|
Graph (published, experimental)
|
|
27
|
-
Hierarchy (
|
|
29
|
+
Hierarchy (published, experimental)
|
|
28
30
|
HTML5 (published, experimental)
|
|
29
31
|
ODM (published, experimental)
|
|
30
32
|
PersonalData (published)
|
|
@@ -73,16 +75,31 @@ const vocabularyDefinitions = {
|
|
|
73
75
|
'inc': { Alias: 'Core', Namespace: 'Org.OData.Core.V1' },
|
|
74
76
|
'int': { filename: 'Core.xml' }
|
|
75
77
|
},
|
|
78
|
+
'DataIntegration': {
|
|
79
|
+
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/DataIntegration.xml' },
|
|
80
|
+
'inc': { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
|
|
81
|
+
'int': { filename: 'DataIntegration.xml' }
|
|
82
|
+
},
|
|
76
83
|
'Graph': {
|
|
77
84
|
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
|
|
78
85
|
'inc': { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
|
|
79
86
|
'int': { filename: 'Graph.xml' }
|
|
80
87
|
},
|
|
88
|
+
'Hierarchy': {
|
|
89
|
+
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Hierarchy.xml' },
|
|
90
|
+
'inc': { Alias: 'Hierarchy', Namespace: 'com.sap.vocabularies.Hierarchy.v1' },
|
|
91
|
+
'int': { filename: 'Hierarchy.xml' }
|
|
92
|
+
},
|
|
81
93
|
'HTML5': {
|
|
82
94
|
'ref': { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml' },
|
|
83
95
|
'inc': { Alias: 'HTML5', Namespace: 'com.sap.vocabularies.HTML5.v1' },
|
|
84
96
|
'int': { filename: 'HTML5.xml' }
|
|
85
97
|
},
|
|
98
|
+
'JSON': {
|
|
99
|
+
'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.JSON.V1.xml' },
|
|
100
|
+
'inc': { Alias: 'JSON', Namespace: 'Org.OData.JSON.V1' },
|
|
101
|
+
'int': { filename: 'JSON.xml' }
|
|
102
|
+
},
|
|
86
103
|
'Measures': {
|
|
87
104
|
'ref': { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml' },
|
|
88
105
|
'inc': { Alias: 'Measures', Namespace: 'Org.OData.Measures.V1' },
|
|
@@ -316,7 +333,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
316
333
|
function handleNestedElements(objname, baseElemName, elementsObj) {
|
|
317
334
|
if(!elementsObj) return;
|
|
318
335
|
Object.entries(elementsObj).forEach(([elemName, element]) => {
|
|
319
|
-
if (Object.keys(element).filter( x => x
|
|
336
|
+
if (Object.keys(element).filter( x => x[0] === '@' ).filter(filterKnownVocabularies).length > 0) {
|
|
320
337
|
message(warning, null, `annotations at nested elements are not yet supported, object ${objname}, element ${baseElemName}.${elemName}`);
|
|
321
338
|
}
|
|
322
339
|
|
|
@@ -414,17 +431,23 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
414
431
|
// do nothing
|
|
415
432
|
|
|
416
433
|
if(!isEdmPropertyRendered(carrier, options) ||
|
|
417
|
-
(isV2() && (edmUtils.isDerivedType(carrier)
|
|
434
|
+
(isV2() && (edmUtils.isDerivedType(carrier)))) {
|
|
418
435
|
return;
|
|
419
436
|
}
|
|
420
437
|
|
|
421
438
|
// Filter unknown toplevel annotations
|
|
422
439
|
// Final filtering of all annotations is done in handleTerm
|
|
423
|
-
|
|
440
|
+
|
|
441
|
+
let annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
|
|
424
442
|
const nullWhitelist = [ '@Core.OperationAvailable' ];
|
|
425
|
-
|
|
443
|
+
let knownAnnos = annoNames.filter(filterKnownVocabularies).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
|
|
426
444
|
if (knownAnnos.length === 0) return;
|
|
427
445
|
|
|
446
|
+
if(rewriteInnerAnnotations()) {
|
|
447
|
+
annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
|
|
448
|
+
knownAnnos = annoNames.filter(filterKnownVocabularies).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
|
|
449
|
+
if (knownAnnos.length === 0) return;
|
|
450
|
+
}
|
|
428
451
|
const prefixTree = createPrefixTree();
|
|
429
452
|
|
|
430
453
|
// usually, for a given carrier there is one target
|
|
@@ -593,6 +616,51 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
593
616
|
*/
|
|
594
617
|
}
|
|
595
618
|
|
|
619
|
+
function rewriteInnerAnnotations() {
|
|
620
|
+
let rc = false;
|
|
621
|
+
for (let a of knownAnnos) {
|
|
622
|
+
const [ prefix, innerAnnotation ] = a.split('.@');
|
|
623
|
+
/*
|
|
624
|
+
New inner annotation (de-)structuring of the core compiler to make
|
|
625
|
+
$value arrays extendable via ellipsis
|
|
626
|
+
@anno: { $value: [ ... ], @innerAnno: ... } is now cracked up by
|
|
627
|
+
the core compiler into:
|
|
628
|
+
@anno: [ ...]
|
|
629
|
+
@anno.@innerAnno: ...
|
|
630
|
+
|
|
631
|
+
Conflict handling if $value is present:
|
|
632
|
+
@anno
|
|
633
|
+
@anno.$value
|
|
634
|
+
@anno.@innerAnno
|
|
635
|
+
|
|
636
|
+
@anno has precedence (as it was before this change) but now
|
|
637
|
+
@anno.$value is overwritten with @anno and the inner annotations
|
|
638
|
+
are applied.
|
|
639
|
+
|
|
640
|
+
Trigger is always the inner annotation, if no inner annotation
|
|
641
|
+
is available, @anno has precedence.
|
|
642
|
+
|
|
643
|
+
Insert $value into $edmJson with inner annotation as well.
|
|
644
|
+
*/
|
|
645
|
+
if(innerAnnotation) {
|
|
646
|
+
if(carrier[prefix]) {
|
|
647
|
+
const valPrefix = prefix + '.$value';
|
|
648
|
+
carrier[valPrefix] = carrier[prefix];
|
|
649
|
+
delete carrier[prefix];
|
|
650
|
+
rc = true;
|
|
651
|
+
}
|
|
652
|
+
const edmJsonPrefix = prefix + '.$edmJson';
|
|
653
|
+
if(carrier[edmJsonPrefix]) {
|
|
654
|
+
const valPrefix = prefix + '.$value.$edmJson';
|
|
655
|
+
carrier[valPrefix] = carrier[edmJsonPrefix];
|
|
656
|
+
delete carrier[edmJsonPrefix];
|
|
657
|
+
rc = true;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return rc;
|
|
662
|
+
}
|
|
663
|
+
|
|
596
664
|
function createPrefixTree() {
|
|
597
665
|
// in csn, all annotations are flattened
|
|
598
666
|
// => values can be - primitive values (string, number)
|
|
@@ -602,6 +670,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
602
670
|
// by building a "prefix tree" for the annotations attached to the carrier
|
|
603
671
|
// see example at definition of function mergePathStepsIntoPrefixTree
|
|
604
672
|
const prefixTree = {};
|
|
673
|
+
|
|
605
674
|
for (let a of knownAnnos) {
|
|
606
675
|
// remove leading @ and split at "."
|
|
607
676
|
// stop splitting at ".@" (used for nested annotations)
|
|
@@ -663,7 +732,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
663
732
|
}
|
|
664
733
|
mergePathStepsIntoPrefixTree(tree[name], pathSteps, index+1, carrier);
|
|
665
734
|
}
|
|
666
|
-
else {
|
|
735
|
+
else if(typeof tree === 'object' ){
|
|
667
736
|
tree[name] = carrier['@' + pathSteps.join('.')];
|
|
668
737
|
}
|
|
669
738
|
}
|
|
@@ -714,9 +783,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
714
783
|
* @type {object}
|
|
715
784
|
* */
|
|
716
785
|
let newAnno = undefined;
|
|
717
|
-
const
|
|
786
|
+
const omissions = { 'Aggregation.default':1 };
|
|
787
|
+
const nullList = { 'Core.OperationAvailable':1 };
|
|
718
788
|
const voc = termName.slice(0, termName.indexOf('.'));
|
|
719
|
-
if(vocabularyDefinitions[voc] && annoValue !== null ||
|
|
789
|
+
if(vocabularyDefinitions[voc] && annoValue !== null && !omissions[termName]|| nullList[termName]) {
|
|
720
790
|
newAnno = new Edm.Annotation(v, termName);
|
|
721
791
|
|
|
722
792
|
// termName may contain a qualifier: @UI.FieldGroup#shippingStatus
|
|
@@ -729,8 +799,8 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
729
799
|
message(error, context,
|
|
730
800
|
`OData annotation qualifier "${ p[1] }" must start with a letter or underscore, followed by at most 127 letters, underscores or digits`);
|
|
731
801
|
}
|
|
732
|
-
newAnno.Term
|
|
733
|
-
newAnno.Qualifier
|
|
802
|
+
newAnno.setEdmAttribute('Term', termNameWithoutQualifiers);
|
|
803
|
+
newAnno.setEdmAttribute('Qualifier', p[1]);
|
|
734
804
|
}
|
|
735
805
|
if (p.length>2) {
|
|
736
806
|
message(warning, context, `multiple qualifiers (${ p[1] },${ p[2] }${ p.length > 3 ? ',...' : '' })`);
|
|
@@ -827,7 +897,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
827
897
|
// "pseudo-structure" used for embedding a piece of JSON that represents "OData CSDL, JSON Representation"
|
|
828
898
|
oTarget.append(handleEdmJson(cAnnoValue['$edmJson'], context));
|
|
829
899
|
}
|
|
830
|
-
else if ( Object.keys(cAnnoValue).filter( x => x
|
|
900
|
+
else if ( Object.keys(cAnnoValue).filter( x => x[0] !== '@' ).length === 0) {
|
|
831
901
|
// object consists only of properties starting with "@"
|
|
832
902
|
message(warning, context, 'nested annotations without corresponding base annotation');
|
|
833
903
|
}
|
|
@@ -934,6 +1004,8 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
934
1004
|
// Edm.Decimal -> Decimal
|
|
935
1005
|
// integer tpye -> Int
|
|
936
1006
|
function handleSimpleValue(val, dTypeName, context) {
|
|
1007
|
+
// these types must be represented as "String" values in XML:
|
|
1008
|
+
const castToXmlString = [ 'Edm.PrimitiveType', 'Edm.Stream', 'Edm.Untyped' ];
|
|
937
1009
|
// caller already made sure that val is neither object nor array
|
|
938
1010
|
dTypeName = resolveType(dTypeName);
|
|
939
1011
|
|
|
@@ -971,12 +1043,12 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
971
1043
|
message(warning, context, `found String, but expected enum type ${ dTypeName }`);
|
|
972
1044
|
typeName = 'EnumMember';
|
|
973
1045
|
}
|
|
974
|
-
else if (dTypeName && dTypeName.startsWith('Edm.') && dTypeName
|
|
1046
|
+
else if (dTypeName && dTypeName.startsWith('Edm.') && !castToXmlString.includes(dTypeName)) {
|
|
975
1047
|
// this covers also all paths
|
|
976
1048
|
typeName = dTypeName.substring(4);
|
|
977
1049
|
}
|
|
978
1050
|
else {
|
|
979
|
-
if(dTypeName == undefined ||
|
|
1051
|
+
if(dTypeName == undefined || castToXmlString.some(t => t === dTypeName))
|
|
980
1052
|
dTypeName = 'Edm.String';
|
|
981
1053
|
// TODO
|
|
982
1054
|
//message(warning, context, "type is not yet handled: found String, expected type: " + dTypeName);
|
|
@@ -1072,7 +1144,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1072
1144
|
// this type doesn't exist
|
|
1073
1145
|
message(warning, context, `explicitly specified type '${ actualTypeName }' not found in vocabulary`);
|
|
1074
1146
|
// explicitly mentioned type, render in XML and JSON
|
|
1075
|
-
newRecord.Type
|
|
1147
|
+
newRecord.setEdmAttribute('Type', actualTypeName);
|
|
1076
1148
|
}
|
|
1077
1149
|
else if (dTypeName && !isDerivedFrom(actualTypeName, dTypeName)) {
|
|
1078
1150
|
// this type doesn't fit the expected one
|
|
@@ -1080,7 +1152,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1080
1152
|
}' is not derived from expected type '${ dTypeName }'`);
|
|
1081
1153
|
actualTypeName = dTypeName;
|
|
1082
1154
|
// explicitly mentioned type, render in XML and JSON
|
|
1083
|
-
newRecord.Type
|
|
1155
|
+
newRecord.setEdmAttribute('Type', actualTypeName);
|
|
1084
1156
|
}
|
|
1085
1157
|
else if (isAbstractType(actualTypeName)) {
|
|
1086
1158
|
// this type is abstract
|
|
@@ -1088,7 +1160,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1088
1160
|
if(dTypeName)
|
|
1089
1161
|
actualTypeName = dTypeName;
|
|
1090
1162
|
// set to definition name and render in XML and JSON
|
|
1091
|
-
newRecord.Type
|
|
1163
|
+
newRecord.setEdmAttribute('Type', actualTypeName);
|
|
1092
1164
|
}
|
|
1093
1165
|
else {
|
|
1094
1166
|
// ok
|
|
@@ -1251,7 +1323,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1251
1323
|
const props = Object.create(null);
|
|
1252
1324
|
Object.entries(obj).forEach(([k, val]) => {
|
|
1253
1325
|
if(k === '@type') {
|
|
1254
|
-
edmNode.Type
|
|
1326
|
+
edmNode.setEdmAttribute('Type', val);
|
|
1255
1327
|
}
|
|
1256
1328
|
else {
|
|
1257
1329
|
let child = undefined;
|
|
@@ -1297,14 +1369,14 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
|
|
|
1297
1369
|
edmNode = exprDef.create(obj);
|
|
1298
1370
|
|
|
1299
1371
|
// iterate over each obj.property and translate expression into EDM
|
|
1300
|
-
|
|
1372
|
+
forEach(obj, (name, val) => {
|
|
1301
1373
|
if(exprDef) {
|
|
1302
1374
|
if(exprDef.anno && name[0] === '@') {
|
|
1303
1375
|
edmNode.append(handleTerm(name.slice(1), val, context));
|
|
1304
1376
|
}
|
|
1305
1377
|
else if (exprDef.attr && exprDef.attr.includes(name)) {
|
|
1306
1378
|
if (name[0] === '$') {
|
|
1307
|
-
edmNode
|
|
1379
|
+
edmNode.setEdmAttribute(name.slice(1), val);
|
|
1308
1380
|
}
|
|
1309
1381
|
}
|
|
1310
1382
|
else if (exprDef.jsonAttr && exprDef.jsonAttr.includes(name)) {
|