@sap/cds-compiler 2.13.8 → 2.15.6

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +128 -4
  2. package/bin/cdsc.js +112 -37
  3. package/lib/api/main.js +63 -22
  4. package/lib/api/options.js +2 -3
  5. package/lib/api/validate.js +6 -6
  6. package/lib/base/message-registry.js +100 -17
  7. package/lib/base/messages.js +85 -64
  8. package/lib/base/optionProcessorHelper.js +19 -0
  9. package/lib/checks/annotationsOData.js +11 -32
  10. package/lib/checks/arrayOfs.js +1 -34
  11. package/lib/checks/validator.js +2 -4
  12. package/lib/compiler/assert-consistency.js +1 -0
  13. package/lib/compiler/base.js +1 -0
  14. package/lib/compiler/builtins.js +11 -0
  15. package/lib/compiler/checks.js +22 -70
  16. package/lib/compiler/define.js +59 -11
  17. package/lib/compiler/extend.js +20 -3
  18. package/lib/compiler/finalize-parse-cdl.js +26 -20
  19. package/lib/compiler/index.js +75 -26
  20. package/lib/compiler/populate.js +36 -17
  21. package/lib/compiler/propagator.js +4 -1
  22. package/lib/compiler/resolve.js +104 -16
  23. package/lib/compiler/shared.js +61 -27
  24. package/lib/compiler/tweak-assocs.js +7 -1
  25. package/lib/edm/annotations/genericTranslation.js +93 -21
  26. package/lib/edm/csn2edm.js +216 -98
  27. package/lib/edm/edm.js +305 -226
  28. package/lib/edm/edmPreprocessor.js +499 -423
  29. package/lib/edm/edmUtils.js +22 -22
  30. package/lib/gen/Dictionary.json +98 -22
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +3 -1
  33. package/lib/gen/languageParser.js +4636 -4368
  34. package/lib/json/csnVersion.js +10 -11
  35. package/lib/json/from-csn.js +3 -2
  36. package/lib/json/to-csn.js +0 -2
  37. package/lib/language/docCommentParser.js +2 -2
  38. package/lib/language/genericAntlrParser.js +47 -2
  39. package/lib/language/language.g4 +59 -27
  40. package/lib/main.d.ts +19 -1
  41. package/lib/main.js +6 -0
  42. package/lib/model/csnRefs.js +33 -6
  43. package/lib/model/csnUtils.js +193 -75
  44. package/lib/model/enrichCsn.js +1 -0
  45. package/lib/model/revealInternalProperties.js +2 -2
  46. package/lib/modelCompare/compare.js +6 -6
  47. package/lib/optionProcessor.js +62 -26
  48. package/lib/render/toCdl.js +844 -679
  49. package/lib/render/toHdbcds.js +189 -243
  50. package/lib/render/toSql.js +180 -198
  51. package/lib/render/utils/common.js +131 -15
  52. package/lib/transform/db/.eslintrc.json +1 -1
  53. package/lib/transform/db/associations.js +2 -2
  54. package/lib/transform/db/constraints.js +3 -1
  55. package/lib/transform/db/expansion.js +15 -10
  56. package/lib/transform/db/flattening.js +94 -64
  57. package/lib/transform/db/transformExists.js +7 -7
  58. package/lib/transform/db/views.js +6 -3
  59. package/lib/transform/forHanaNew.js +43 -26
  60. package/lib/transform/forOdataNew.js +43 -42
  61. package/lib/transform/localized.js +12 -7
  62. package/lib/transform/odata/toFinalBaseType.js +8 -6
  63. package/lib/transform/odata/typesExposure.js +145 -197
  64. package/lib/transform/transformUtilsNew.js +9 -12
  65. package/lib/transform/translateAssocsToJoins.js +5 -1
  66. package/lib/transform/universalCsn/coreComputed.js +5 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
  68. package/lib/utils/moduleResolve.js +13 -6
  69. package/package.json +1 -1
  70. package/share/messages/message-explanations.json +2 -1
  71. package/share/messages/syntax-expected-integer.md +37 -0
  72. package/lib/transform/odata/attachPath.js +0 -96
  73. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  74. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  75. package/lib/transform/odata/referenceFlattener.js +0 -296
  76. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  77. package/lib/transform/odata/structuralPath.js +0 -72
  78. 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
- /* Vocabulary overview as of January 2020:
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 (not published, still experimental)
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.substr(0,1) === '@' ).filter(filterKnownVocabularies).length > 0) {
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) || carrier['@Core.MediaType']))) {
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
- const annoNames = Object.keys(carrier).filter( x => x.substr(0,1) === '@' );
440
+
441
+ let annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
424
442
  const nullWhitelist = [ '@Core.OperationAvailable' ];
425
- const knownAnnos = annoNames.filter(filterKnownVocabularies).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
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 nullWhitelist = [ 'Core.OperationAvailable' ];
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 || nullWhitelist.includes(termName)) {
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 = termNameWithoutQualifiers;
733
- newAnno.Qualifier = p[1];
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.substr(0,1) !== '@' ).length === 0) {
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 !== 'Edm.PrimitiveType') {
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 || dTypeName === 'Edm.PrimitiveType')
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 = actualTypeName;
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 = actualTypeName;
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 = actualTypeName;
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 = val;
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
- Object.entries(obj).forEach(([name, val]) => {
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[name.slice(1)] = val;
1379
+ edmNode.setEdmAttribute(name.slice(1), val);
1308
1380
  }
1309
1381
  }
1310
1382
  else if (exprDef.jsonAttr && exprDef.jsonAttr.includes(name)) {