@sap/cds-compiler 3.9.4 → 4.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.
Files changed (95) hide show
  1. package/CHANGELOG.md +107 -4
  2. package/README.md +0 -1
  3. package/bin/cdsc.js +11 -23
  4. package/bin/cdsse.js +3 -3
  5. package/doc/API.md +5 -0
  6. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  7. package/doc/CHANGELOG_BETA.md +17 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +28 -0
  9. package/lib/api/.eslintrc.json +1 -1
  10. package/lib/api/main.js +55 -9
  11. package/lib/api/options.js +2 -0
  12. package/lib/base/error.js +2 -0
  13. package/lib/base/message-registry.js +143 -64
  14. package/lib/base/messages.js +213 -107
  15. package/lib/base/model.js +11 -11
  16. package/lib/checks/.eslintrc.json +1 -1
  17. package/lib/checks/annotationsOData.js +2 -2
  18. package/lib/checks/elements.js +1 -1
  19. package/lib/checks/enricher.js +26 -3
  20. package/lib/checks/onConditions.js +67 -12
  21. package/lib/checks/queryNoDbArtifacts.js +106 -105
  22. package/lib/checks/sql-snippets.js +2 -0
  23. package/lib/checks/types.js +12 -6
  24. package/lib/checks/validator.js +2 -2
  25. package/lib/compiler/assert-consistency.js +10 -8
  26. package/lib/compiler/builtins.js +8 -2
  27. package/lib/compiler/checks.js +52 -35
  28. package/lib/compiler/define.js +31 -26
  29. package/lib/compiler/extend.js +120 -65
  30. package/lib/compiler/finalize-parse-cdl.js +12 -43
  31. package/lib/compiler/generate.js +16 -5
  32. package/lib/compiler/index.js +8 -5
  33. package/lib/compiler/kick-start.js +4 -3
  34. package/lib/compiler/populate.js +96 -95
  35. package/lib/compiler/propagator.js +7 -8
  36. package/lib/compiler/resolve.js +377 -103
  37. package/lib/compiler/shared.js +794 -517
  38. package/lib/compiler/tweak-assocs.js +8 -6
  39. package/lib/compiler/utils.js +44 -0
  40. package/lib/edm/annotations/genericTranslation.js +41 -5
  41. package/lib/edm/csn2edm.js +34 -32
  42. package/lib/edm/edm.js +34 -31
  43. package/lib/edm/edmAnnoPreprocessor.js +0 -23
  44. package/lib/edm/edmInboundChecks.js +7 -2
  45. package/lib/edm/edmPreprocessor.js +25 -18
  46. package/lib/edm/edmUtils.js +8 -4
  47. package/lib/gen/Dictionary.json +18 -0
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +4 -2
  50. package/lib/gen/languageParser.js +5006 -4582
  51. package/lib/json/from-csn.js +157 -112
  52. package/lib/json/to-csn.js +60 -89
  53. package/lib/language/antlrParser.js +17 -13
  54. package/lib/language/docCommentParser.js +11 -1
  55. package/lib/language/genericAntlrParser.js +13 -10
  56. package/lib/language/language.g4 +168 -97
  57. package/lib/main.d.ts +128 -36
  58. package/lib/main.js +1 -1
  59. package/lib/model/csnRefs.js +24 -5
  60. package/lib/model/csnUtils.js +9 -8
  61. package/lib/model/revealInternalProperties.js +7 -12
  62. package/lib/model/sortViews.js +4 -2
  63. package/lib/modelCompare/compare.js +1 -1
  64. package/lib/modelCompare/utils/filter.js +40 -2
  65. package/lib/optionProcessor.js +0 -3
  66. package/lib/render/toCdl.js +247 -214
  67. package/lib/render/toHdbcds.js +197 -181
  68. package/lib/render/toSql.js +325 -289
  69. package/lib/render/utils/common.js +42 -4
  70. package/lib/render/utils/delta.js +1 -1
  71. package/lib/render/utils/sql.js +3 -3
  72. package/lib/transform/braceExpression.js +2 -2
  73. package/lib/transform/db/.eslintrc.json +1 -1
  74. package/lib/transform/db/applyTransformations.js +3 -3
  75. package/lib/transform/db/associations.js +24 -12
  76. package/lib/transform/db/expansion.js +17 -18
  77. package/lib/transform/db/flattening.js +17 -21
  78. package/lib/transform/db/rewriteCalculatedElements.js +171 -64
  79. package/lib/transform/db/views.js +3 -4
  80. package/lib/transform/draft/db.js +21 -12
  81. package/lib/transform/draft/odata.js +4 -0
  82. package/lib/transform/forOdataNew.js +62 -47
  83. package/lib/transform/forRelationalDB.js +12 -7
  84. package/lib/transform/localized.js +4 -2
  85. package/lib/transform/odata/toFinalBaseType.js +5 -5
  86. package/lib/transform/odata/typesExposure.js +3 -3
  87. package/lib/transform/parseExpr.js +3 -0
  88. package/lib/transform/transformUtilsNew.js +43 -23
  89. package/lib/transform/translateAssocsToJoins.js +7 -6
  90. package/lib/transform/universalCsn/.eslintrc.json +1 -1
  91. package/lib/transform/universalCsn/coreComputed.js +7 -5
  92. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
  93. package/package.json +2 -2
  94. package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
  95. package/share/messages/message-explanations.json +1 -1
@@ -17,6 +17,7 @@ const {
17
17
  traverseQueryPost,
18
18
  traverseQueryExtra,
19
19
  setExpandStatus,
20
+ traverseExpr,
20
21
  } = require('./utils');
21
22
 
22
23
  const $location = Symbol.for('cds.$location');
@@ -31,13 +32,12 @@ function tweakAssocs( model ) {
31
32
  const {
32
33
  effectiveType,
33
34
  getOrigin,
34
- resolveExpr,
35
+ navigationEnv,
35
36
  } = model.$functions;
36
- const { environment } = model.$volatileFunctions;
37
37
 
38
38
  // Phase 5: rewrite associations
39
39
  model._entities.forEach( rewriteArtifact );
40
- // Think hard whether an on condition rewrite can lead to a new cylcic
40
+ // Think hard whether an on condition rewrite can lead to a new cyclic
41
41
  // dependency. If so, we need other messages anyway. TODO: probably do
42
42
  // another cyclic check with testMode.js
43
43
  return;
@@ -313,7 +313,8 @@ function tweakAssocs( model ) {
313
313
  return;
314
314
  }
315
315
  if (!nav.tableAlias || nav.tableAlias.path) {
316
- resolveExpr( cond, rewriteExpr, elem, nav.tableAlias );
316
+ traverseExpr( cond, 'rewrite-on', elem,
317
+ expr => rewriteExpr( expr, elem, nav.tableAlias ) );
317
318
  }
318
319
  else {
319
320
  // TODO: support that, now that the ON condition is rewritten in the right order
@@ -452,6 +453,7 @@ function tweakAssocs( model ) {
452
453
  function rewriteItem( elem, item, name, assoc, forKeys ) {
453
454
  // TODO: for rewriting ON conditions of explicitly provided model targets,
454
455
  // we need to only rewrite the current element, not all sibling elements
456
+ // TODO: this will be different in v4
455
457
  if (!elem._redirected)
456
458
  return true;
457
459
  for (const alias of elem._redirected) {
@@ -480,8 +482,8 @@ function tweakAssocs( model ) {
480
482
  item.id = name;
481
483
  }
482
484
  }
483
- const env = name && environment(elem);
484
- elem = setArtifactLink( item, env && env[name] );
485
+ const env = name && navigationEnv(elem);
486
+ elem = setArtifactLink( item, env?.elements?.[name] );
485
487
  if (elem && !Array.isArray(elem))
486
488
  return elem;
487
489
  // TODO: better (extra message), TODO: do it
@@ -442,6 +442,47 @@ function isDirectComposition( art ) {
442
442
  return type && type[0] && type[0].id === 'cds.Composition';
443
443
  }
444
444
 
445
+ function traverseExpr( expr, exprCtx, user, callback ) {
446
+ if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
447
+ return;
448
+
449
+ if (expr.path) {
450
+ callback( expr, exprCtx, user );
451
+ // TODO: move arguments and filter traversal to here
452
+ return;
453
+ }
454
+ else if (expr.type || expr.query) {
455
+ callback( expr, exprCtx, user );
456
+ }
457
+
458
+ if (expr.args) {
459
+ const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
460
+ // TODO: re-think $expected
461
+ args.forEach( e => traverseExpr( e, exprCtx, user, callback ) );
462
+ }
463
+ if (expr.suffix) // fn( … ) OVER …
464
+ expr.suffix.forEach( e => traverseExpr( e, exprCtx, user, callback ) );
465
+ }
466
+
467
+ function userQuery( user ) {
468
+ // TODO: we need _query links set by the definer
469
+ while (user._main) {
470
+ if (user.kind === 'select' || user.kind === '$join')
471
+ return user;
472
+ user = user._parent;
473
+ }
474
+ return null;
475
+ }
476
+
477
+ function definedViaCdl( art ) {
478
+ // return !!art._block?.artifacts;
479
+ // TODO: the above code would work when _block links are correctly set on
480
+ // members of duplicate extensions, see test3/Extensions/DuplicateExtend/. The
481
+ // following is a workaround to make at least ref to builtins work:
482
+ const { $frontend } = art._block || art;
483
+ return $frontend !== 'json' && $frontend !== '$internal';
484
+ }
485
+
445
486
  module.exports = {
446
487
  pushLink,
447
488
  annotationVal,
@@ -470,4 +511,7 @@ module.exports = {
470
511
  setExpandStatus,
471
512
  setExpandStatusAnnotate,
472
513
  isDirectComposition,
514
+ traverseExpr,
515
+ userQuery,
516
+ definedViaCdl,
473
517
  };
@@ -267,6 +267,34 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
267
267
 
268
268
  const v = options.v;
269
269
 
270
+ // Copy annotations from origin to parameter entity if it's
271
+ // qualified with #$parameters or if its applicable to an EntitySet or Singleton
272
+ forEachDefinition(reqDefs, (object) => {
273
+ if(object.$isParamEntity && object._origin) {
274
+ for(const attr in object._origin) {
275
+ if (attr[0] === '@') {
276
+ const [ prefix, innerAnnotation ] = attr.split('.@');
277
+ const ns = whatsMyTermNamespace(prefix);
278
+ if(ns) {
279
+ const steps = prefix.replace('@' + ns + '.', '').split('.');
280
+ const paramAnnoParts = steps[0].split('#$parameters');
281
+ const dictTerm = getDictTerm(ns + '.' + paramAnnoParts[0], options);
282
+ if(paramAnnoParts.length > 1 || ['Singleton', 'EntitySet'].some(y => dictTerm?.AppliesTo?.includes(y))) {
283
+ steps[0] = '@' + ns + '.' + paramAnnoParts.join('');
284
+ let newAnno = steps.join('.');
285
+ if(innerAnnotation)
286
+ newAnno += '.@' + innerAnnotation;
287
+ edmUtils.assignAnnotation(object, newAnno, object._origin[attr]);
288
+ // delete original annotation only if it was qualified with $parameters
289
+ if(paramAnnoParts.length > 1)
290
+ delete object._origin[attr];
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+ });
297
+
270
298
  // Crawl over the csn and trigger the annotation translation for all kinds
271
299
  // of annotated things.
272
300
  // Note: only works for single service
@@ -412,6 +440,12 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
412
440
  handleAnnotations(edmTargetName, p, [ ...location, 'params', n ]);
413
441
  });
414
442
  }
443
+ if(cAction.returns) {
444
+ const edmTargetName = actionName + '/$ReturnType';
445
+ setProp(cAction.returns, '$appliesToReturnType', true);
446
+ handleAnnotations(edmTargetName, cAction.returns, [ ...location, 'returns' ]);
447
+ delete cAction.returns['$appliesToReturnType'];
448
+ }
415
449
 
416
450
  function relParList() {
417
451
  // we rely on the order of params in the csn being the correct one
@@ -464,7 +498,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
464
498
  // keep only those annotations that - start with a known vocabulary name
465
499
  // - have a value other than null
466
500
 
467
- // if the carier is an element that is not rendered or
501
+ // if the carrier is an element that is not rendered or
468
502
  // if the carrier is a derived type of a primitive type which is not rendered in V2
469
503
  // if the carrier is a media stream element in V2
470
504
  // do nothing
@@ -656,8 +690,10 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
656
690
  carrier.cardinality && carrier.cardinality.max === '*' && x.includes('Collection')
657
691
  : true);
658
692
  }
693
+ else if(carrier.$appliesToReturnType)
694
+ testToStandardEdmTarget = (x => x ? x.includes('ReturnType') : true);
659
695
  else {
660
- // this might be more precise if handleAnnotation would know more about the carrier
696
+ // this might be more precise if handleAnnotation would know more about the carrier
661
697
  testToStandardEdmTarget = (x => x
662
698
  ? ['Parameter', 'Property'].some(y => x.includes(y) ||
663
699
  carrier._isCollection && x.includes('Collection'))
@@ -763,7 +799,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
763
799
  const [ prefix, innerAnnotation ] = a.split('.@');
764
800
  const ns = whatsMyTermNamespace(prefix);
765
801
  const steps = prefix.replace('@' + ns + '.', '').split('.');
766
- steps.splice(0,0, ns);
802
+ steps.splice(0, 0, ns);
767
803
  let i = steps.lastIndexOf('$edmJson');
768
804
  if(i > -1) {
769
805
  i = steps.findIndex(s => s.includes('@'), i+1);
@@ -1116,11 +1152,11 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
1116
1152
 
1117
1153
  // found a simple value "val"
1118
1154
  // expected type is dTypeName
1119
- // mappping rule for values:
1155
+ // mapping rule for values:
1120
1156
  // if expected type is ... the expression to be generated is ...
1121
1157
  // floating point type except Edm.Decimal -> Float
1122
1158
  // Edm.Decimal -> Decimal
1123
- // integer tpye -> Int
1159
+ // integer type -> Int
1124
1160
  function handleSimpleValue(value, dTypeName, msg) {
1125
1161
  // these types must be represented as "String" values in XML:
1126
1162
  const castToXmlString = [ 'Edm.PrimitiveType', 'Edm.Stream', 'Edm.Untyped' ];
@@ -51,7 +51,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
51
51
  whatsMyServiceRootName,
52
52
  fallBackSchemaName,
53
53
  options ] = initializeModel(csn, _options, messageFunctions, serviceNames);
54
-
54
+
55
55
  const mergedVocabularies = translate.mergeOdataVocabularies(options, message);
56
56
 
57
57
  const Edm = getEdm(options, messageFunctions);
@@ -404,7 +404,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
404
404
  createTerm);
405
405
  }
406
406
 
407
- // fetch all exising children names in a map
407
+ // fetch all existing children names in a map
408
408
  const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
409
409
  const name = cur._edmAttributes.Name;
410
410
  if(acc[name] === undefined) {
@@ -436,7 +436,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
436
436
 
437
437
  The first sentence in chapter 13 is:
438
438
  Each metadata document used to describe an OData service MUST define exactly one entity container.
439
-
439
+
440
440
  This sentence expresses that an OData SERVICE must contain an entity container, but an EDMX is not required to have a container.
441
441
  Therefore it is absolutely legal and necessary to remove an empty container from the IR!
442
442
  */
@@ -683,7 +683,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
683
683
  const bpType = entityCsn ? fullQualified(entityCsn.name) : undefined;
684
684
  /*
685
685
  Check for binding $self parameter. If available, use this parameter
686
- instead of artifically created binding parameter (hasBindingParameter).
686
+ instead of artificially created binding parameter (hasBindingParameter).
687
687
  The binding parameter remains in the CSN and is rendered as any other
688
688
  parameter (including default value/not null/ etc) and acts as annotation carrier.
689
689
  */
@@ -805,8 +805,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
805
805
  const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
806
806
  if(rt) // add EntitySet attribute only if return type is an entity
807
807
  {
808
- const defintion = schemaCsn.definitions[rt];
809
- if(defintion && defintion.kind === 'entity')
808
+ const definition = schemaCsn.definitions[rt];
809
+ if(definition && definition.kind === 'entity')
810
810
  {
811
811
  functionImport.setEdmAttribute('EntitySet', rt.replace(schemaNamePrefix, ''));
812
812
  }
@@ -883,31 +883,34 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
883
883
  // it is safe to assume that either type or items.type are set
884
884
  const returnsLoc = [ ...loc, 'returns'];
885
885
  const returns = action.returns.items || action.returns;
886
- let type = returns.type;
887
- if (type) {
888
- collectUsedType(action.returns);
889
- if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
890
- message('odata-spec-violation-returns', returnsLoc, { kind: action.kind, version: '2.0' });
891
- }
892
- else if(isBuiltinType(type)) {
893
- type = edmUtils.mapCdsToEdmType(returns, messageFunctions, true);
894
- if(type) {
895
- const td = EdmPrimitiveTypeMap[type];
896
- if(td && !td.v2) {
897
- message('odata-spec-violation-type', returnsLoc,
886
+ let type = returns['@odata.Type'];
887
+ if(!type) {
888
+ type = returns.type;
889
+ if (type) {
890
+ collectUsedType(action.returns);
891
+ if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
892
+ message('odata-spec-violation-returns', returnsLoc, { kind: action.kind, version: '2.0' });
893
+ }
894
+ else if(isBuiltinType(type)) {
895
+ type = edmUtils.mapCdsToEdmType(returns, messageFunctions, true);
896
+ if(type) {
897
+ const td = EdmPrimitiveTypeMap[type];
898
+ if(td && !td.v2) {
899
+ message('odata-spec-violation-type', returnsLoc,
898
900
  { type, version: '2.0', '#': 'incompatible' });
901
+ }
902
+ }
903
+ else {
904
+ message('odata-spec-violation-type-unknown', returnsLoc, { type });
899
905
  }
900
906
  }
901
- else {
902
- message('odata-spec-violation-type-unknown', returnsLoc, { type });
903
- }
907
+ if(action.returns._isCollection)
908
+ type = `Collection(${type})`
909
+ }
910
+ else {
911
+ // type is missing
912
+ message('odata-spec-violation-type', returnsLoc);
904
913
  }
905
- if(action.returns._isCollection)
906
- type = `Collection(${type})`
907
- }
908
- else {
909
- // type is missing
910
- message('odata-spec-violation-type', returnsLoc);
911
914
  }
912
915
  return type;
913
916
  }
@@ -916,7 +919,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
916
919
  /*
917
920
  addAssociation() constructs a V2 association.
918
921
  In V4 all this has been simplified very much, the only thing actually left over is
919
- <ReferentialConstriant> that is then a sub element to <NavigationProperty>.
922
+ <ReferentialConstraint> that is then a sub element to <NavigationProperty>.
920
923
  However, referential constraints are substantially different to its V2 counterpart,
921
924
  so it is better to reimplement proper V4 construction of<NavigationProperty> in a separate
922
925
  function.
@@ -1086,7 +1089,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
1086
1089
  // Only the unknown type warning may be triggered by an unknown @odata.Type override.
1087
1090
  if(td.v2 !== p.v2 && td.v4 !== p.v4)
1088
1091
  message('odata-spec-violation-type', pLoc,
1089
- { type:edmType, version: (p.v4 ? '4.0' : '2.0'), '#': 'incompatible' });
1092
+ { type: edmType, version: (p.v4 ? '4.0' : '2.0'), '#': 'incompatible' });
1090
1093
  EdmTypeFacetNames.forEach(name => {
1091
1094
  const facet = EdmTypeFacetMap[name];
1092
1095
  const optional =
@@ -1101,13 +1104,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
1101
1104
  // node and facet version match
1102
1105
  if(!p._edmAttributes[name] && td[name] && !optional && (p.v2 === facet.v2 || p.v4 === facet.v4)) {
1103
1106
  message('odata-spec-violation-type', pLoc,
1104
- { type:edmType, name, version: (p.v4 ? '4.0' : '2.0'), '#': 'facet' });
1107
+ { type: edmType, name, version: (p.v4 ? '4.0' : '2.0'), '#': 'facet' });
1105
1108
  }
1106
1109
  });
1107
1110
  }
1108
1111
  else {
1109
- message('odata-spec-violation-type-unknown', pLoc,
1110
- { type:edmType });
1112
+ message('odata-spec-violation-type-unknown', pLoc, { type: edmType });
1111
1113
  }
1112
1114
  }
1113
1115
  }
package/lib/edm/edm.js CHANGED
@@ -683,41 +683,44 @@ function getEdm(options, messageFunctions) {
683
683
  if(!(this._edmAttributes[typeName] === 'Edm.Stream' &&
684
684
  !( /*scalarType.type === 'cds.String' ||*/ this._scalarType.type === 'cds.Binary')))
685
685
  edmUtils.addTypeFacets(this, this._scalarType);
686
- // CDXCORE-245:
687
- // map type to @odata.Type
688
- // optionally add @odata { MaxLength, Precision, Scale, SRID } but only in combination with @odata.Type
689
- const odataType = csn['@odata.Type'];
690
- if(odataType)
691
- {
692
- const td = EdmPrimitiveTypeMap[odataType];
693
- // If type is known, it must be available in the current version
694
- // Reason: EDMX Importer may set `@odata.Type: 'Edm.DateTime'` on imported V2 services
695
- // Not filtering out this incompatible type here in case of a V4 rendering would
696
- // produce an unrecoverable error.
697
- if(td && (td.v2 === this.v2 || td.v4 === this.v4)) {
698
- this.setEdmAttribute(typeName, odataType);
699
- EdmTypeFacetNames.forEach(facetName => {
700
- const facet = EdmTypeFacetMap[facetName];
701
- if(facet.remove) {
702
- this.removeEdmAttribute(facetName);
703
- this.removeEdmAttribute(facet.extra);
704
- }
705
- if(td[facetName] !== undefined &&
686
+ }
687
+ else {
688
+ this._edmAttributes[typeName] = typecsn.type;
689
+ }
690
+ }
691
+ // CDXCORE-245:
692
+ // map type to @odata.Type
693
+ // optionally add @odata { MaxLength, Precision, Scale, SRID }
694
+ // but only in combination with @odata.Type
695
+ // Allow to override type only on scalar and undefined types
696
+ if((this._scalarType || typecsn.type == null) && !csn.elements) {
697
+ const odataType = csn['@odata.Type'];
698
+ if(odataType) {
699
+ const td = EdmPrimitiveTypeMap[odataType];
700
+ // If type is known, it must be available in the current version
701
+ // Reason: EDMX Importer may set `@odata.Type: 'Edm.DateTime'` on imported V2 services
702
+ // Not filtering out this incompatible type here in case of a V4 rendering would
703
+ // produce an unrecoverable error.
704
+ if(td && (td.v2 === this.v2 || td.v4 === this.v4)) {
705
+ this.setEdmAttribute(typeName, odataType);
706
+ EdmTypeFacetNames.forEach(facetName => {
707
+ const facet = EdmTypeFacetMap[facetName];
708
+ if(facet.remove) {
709
+ this.removeEdmAttribute(facetName);
710
+ this.removeEdmAttribute(facet.extra);
711
+ }
712
+ if(td[facetName] !== undefined &&
706
713
  (facet.v2 === this.v2 ||
707
714
  facet.v4 === this.v4))
708
715
  {
709
- if(this.v2 && facetName === 'Scale' && csn['@odata.'+facetName] === 'variable')
710
- this.setXml({ [facet.extra]: true });
711
- else
716
+ if(this.v2 && facetName === 'Scale' && csn['@odata.'+facetName] === 'variable')
717
+ this.setXml({ [facet.extra]: true });
718
+ else
712
719
  this.setEdmAttribute(facetName, csn['@odata.'+facetName]);
713
- }
714
- });
715
- }
720
+ }
721
+ });
716
722
  }
717
723
  }
718
- else {
719
- this._edmAttributes[typeName] = typecsn.type;
720
- }
721
724
  }
722
725
  }
723
726
 
@@ -734,7 +737,7 @@ function getEdm(options, messageFunctions) {
734
737
  // store undecorated type for JSON
735
738
  this._type = this._edmAttributes[typeName];
736
739
  // decorate for XML (not for Complex/EntityType)
737
- if(this._isCollection)
740
+ if(this._isCollection && this._edmAttributes[typeName])
738
741
  this._edmAttributes[typeName] = `Collection(${this._edmAttributes[typeName]})`
739
742
  }
740
743
 
@@ -1127,7 +1130,7 @@ function getEdm(options, messageFunctions) {
1127
1130
  and if the csn.containsTarget for this NavigationProperty is true,
1128
1131
  then this is the generated 'Results' association to the underlying entityType.
1129
1132
  Only this special association may have an explicit ContainsTarget attribute.
1130
- See csn2edm.createParmeterizedEntityTypeAndSet() for details
1133
+ See csn2edm.createEntityTypeAndSet() for details
1131
1134
  2) ContainsTarget stems from the @odata.contained annotation
1132
1135
  */
1133
1136
  // eslint-disable-next-line sonarjs/no-redundant-boolean
@@ -59,7 +59,6 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
59
59
  {
60
60
  mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
61
61
  }
62
- mapAnnotationAssignment(element, struct, PDMSemantics());
63
62
  }
64
63
 
65
64
  // etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
@@ -80,28 +79,6 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
80
79
  }
81
80
  }
82
81
 
83
- // nested functions begin
84
- function PDMSemantics()
85
- {
86
- /*
87
- let dict = Object.create(null);
88
-
89
- dict['@PDM.xxx1'] = [ '@sap.pdm-semantics' ];
90
- dict['@PDM.xxx2'] = [ '@sap.pdm-propery' ];
91
- dict['@PDM.xxx3'] = [ '@sap.pdm-display-sq-no' ];
92
- dict['@PDM.xxx4'] = [ '@sap.pdm-record-identifier' ];
93
- dict['@PDM.xxx5'] = [ '@sap.pdm-field-group' ];
94
- dict['@PDM.xxx6'] = [ '@sap.pdm-mask-find-pattern' ];
95
- dict['@PDM.xxx7'] = [ '@sap.pdm-mask-replacement-pattern' ];
96
- dict['@PDM.xxx8'] = [ '@sap.deletable' ];
97
- dict['@PDM.xxx8'] = [ '@sap.updatable' ];
98
-
99
- // respect flattened annotation $value
100
- Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
101
- */
102
- return Object.create(null);
103
- }
104
-
105
82
  function AnalyticalAnnotations()
106
83
  {
107
84
  function mapCommonAttributes(element, struct, prop)
@@ -24,7 +24,7 @@ function inboundQualificationChecks(csn, options, messageFunctions,
24
24
  serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName, csnUtils) {
25
25
  const { message, throwWithError } = messageFunctions;
26
26
 
27
- forEachDefinition(csn, [ attach$path, checkChainedArray ]);
27
+ forEachDefinition(csn, [ attach$path, checkProperArrayUsage ]);
28
28
  checkNestedContextsAndServices();
29
29
  throwWithError();
30
30
 
@@ -37,7 +37,7 @@ function inboundQualificationChecks(csn, options, messageFunctions,
37
37
  }, [ 'definitions', defName ]);
38
38
  }
39
39
 
40
- function checkChainedArray(def, defName) {
40
+ function checkProperArrayUsage(def, defName) {
41
41
  if (!isMyServiceRequested(defName))
42
42
  return;
43
43
  let currPath = ['definitions', defName];
@@ -47,6 +47,11 @@ function inboundQualificationChecks(csn, options, messageFunctions,
47
47
  function checkIfItemsOfItems(construct, _constructName, _prop, path) {
48
48
  const constructType = csnUtils.effectiveType(construct);
49
49
  if (constructType.items) {
50
+ if(constructType.items.target) {
51
+ const isComp = constructType.items.type === 'cds.Composition';
52
+ message('type-invalid-items', path, { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
53
+ return;
54
+ }
50
55
  if (constructType.items.items) {
51
56
  message('chained-array-of', path);
52
57
  return;