@sap/cds-compiler 5.1.2 → 5.3.0

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 (68) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/bin/cdsc.js +7 -2
  3. package/bin/cdshi.js +24 -17
  4. package/bin/cdsse.js +17 -18
  5. package/doc/CHANGELOG_BETA.md +9 -4
  6. package/lib/api/main.js +19 -2
  7. package/lib/api/options.js +4 -1
  8. package/lib/api/validate.js +5 -0
  9. package/lib/base/builtins.js +1 -0
  10. package/lib/base/message-registry.js +40 -3
  11. package/lib/base/messages.js +1 -1
  12. package/lib/base/model.js +0 -11
  13. package/lib/checks/actionsFunctions.js +0 -12
  14. package/lib/checks/structuredAnnoExpressions.js +10 -14
  15. package/lib/compiler/assert-consistency.js +21 -13
  16. package/lib/compiler/builtins.js +2 -2
  17. package/lib/compiler/checks.js +25 -6
  18. package/lib/compiler/define.js +27 -31
  19. package/lib/compiler/extend.js +16 -18
  20. package/lib/compiler/generate.js +3 -3
  21. package/lib/compiler/populate.js +22 -16
  22. package/lib/compiler/propagator.js +3 -2
  23. package/lib/compiler/resolve.js +87 -94
  24. package/lib/compiler/shared.js +12 -13
  25. package/lib/compiler/tweak-assocs.js +390 -86
  26. package/lib/compiler/utils.js +41 -33
  27. package/lib/compiler/xpr-rewrite.js +45 -58
  28. package/lib/edm/annotations/genericTranslation.js +17 -13
  29. package/lib/edm/csn2edm.js +28 -4
  30. package/lib/edm/edm.js +68 -28
  31. package/lib/edm/edmInboundChecks.js +5 -8
  32. package/lib/edm/edmPreprocessor.js +66 -40
  33. package/lib/edm/edmUtils.js +1 -1
  34. package/lib/gen/BaseParser.js +778 -0
  35. package/lib/gen/CdlParser.js +4477 -0
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +1 -1
  38. package/lib/gen/languageParser.js +4072 -4024
  39. package/lib/inspect/inspectPropagation.js +1 -1
  40. package/lib/json/from-csn.js +5 -3
  41. package/lib/json/to-csn.js +7 -10
  42. package/lib/language/antlrParser.js +96 -0
  43. package/lib/language/errorStrategy.js +1 -1
  44. package/lib/language/genericAntlrParser.js +32 -4
  45. package/lib/language/multiLineStringParser.js +1 -1
  46. package/lib/main.d.ts +23 -0
  47. package/lib/model/cloneCsn.js +22 -13
  48. package/lib/model/csnUtils.js +2 -0
  49. package/lib/model/revealInternalProperties.js +2 -0
  50. package/lib/modelCompare/utils/filter.js +70 -42
  51. package/lib/optionProcessor.js +16 -10
  52. package/lib/parsers/AstBuildingParser.js +1290 -0
  53. package/lib/parsers/CdlGrammar.g4 +2013 -0
  54. package/lib/parsers/Lexer.js +249 -0
  55. package/lib/render/toCdl.js +46 -45
  56. package/lib/render/toSql.js +5 -5
  57. package/lib/transform/addTenantFields.js +4 -4
  58. package/lib/transform/db/applyTransformations.js +54 -16
  59. package/lib/transform/draft/odata.js +10 -11
  60. package/lib/transform/effective/flattening.js +10 -14
  61. package/lib/transform/forRelationalDB.js +7 -6
  62. package/lib/transform/odata/flattening.js +42 -31
  63. package/lib/transform/odata/toFinalBaseType.js +7 -6
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  65. package/lib/utils/moduleResolve.js +1 -1
  66. package/package.json +2 -2
  67. package/share/messages/redirected-to-ambiguous.md +5 -4
  68. package/share/messages/redirected-to-complex.md +6 -3
@@ -212,9 +212,11 @@ function storeExtension( elem, name, prop, parent, block ) {
212
212
  /** @type {(a: any, b: any) => boolean} */
213
213
  const testFunctionPlaceholder = () => true;
214
214
 
215
- // Return path step if the path navigates along an association whose final type
216
- // satisfies function `test`; "navigates along" = last path item not considered
217
- // without truthy optional argument `alsoTestLast`.
215
+ /**
216
+ * Return path step if the path navigates along an association whose final type
217
+ * satisfies function `test`; "navigates along" = last path item not considered
218
+ * without truthy optional argument `alsoTestLast`.
219
+ */
218
220
  function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
219
221
  for (const item of ref.path || []) {
220
222
  const art = item && item._artifact; // item can be null with parse error
@@ -435,7 +437,7 @@ function forEachJoinOn( query, from, callback ) {
435
437
  }
436
438
 
437
439
  function forEachExprArray( query, array, refContext, exprContext, callback ) {
438
- for (const expr of array ) {
440
+ for (const expr of array) {
439
441
  if (expr)
440
442
  callback( expr, (expr.path ? refContext : exprContext), query );
441
443
  }
@@ -512,8 +514,10 @@ function traverseQueryExtra( main, callback ) {
512
514
  }
513
515
  }
514
516
 
515
- // Returns what was available at view._from[0] before:
516
- // (think first whether to really use this function)
517
+ /**
518
+ * Returns what was available at view._from[0] before:
519
+ * (think first whether to really use this function)
520
+ */
517
521
  function viewFromPrimary( view ) {
518
522
  let query = view.$queries?.[0];
519
523
  while (query?._origin?.kind === 'select') // sub query in from
@@ -521,24 +525,26 @@ function viewFromPrimary( view ) {
521
525
  return dictFirst( query?.$tableAliases );
522
526
  }
523
527
 
524
- // About Helper property $expand for faster the XSN-to-CSN transformation
525
- // - null/undefined: artifact, member, items does not contain expanded members
526
- // - 'origin': all expanded (sub) elements have no new target/on and no new annotations
527
- // that value is only on elements, types, and params -> no other members
528
- // when set, only on elem/art with expanded elements
529
- // - 'target': all expanded (sub) elements might only have new target/on, but
530
- // no individual annotations on any (sub) member
531
- // when set, traverse all parents where the value has been 'origin' before
532
- // - 'annotate': at least one inferred (sub) member has an individual annotation,
533
- // not counting propagated ones; set up to the definition (main artifact)
534
- // (only set with anno on $inferred elem), annotate “beats” target
535
- // Usage according to CSN flavor:
536
- // - gensrc: do not render inferred elements (including expanded elements),
537
- // collect annotate statements with value 'annotate'
538
- // - client: do not render expanded sub elements if artifact/member is no type, has a type,
539
- // has $expand = 'origin', and all its _origin also have $expand = 'origin'
540
- // (might sometimes render the elements unnecessarily, which is not wrong)
541
- // - universal: do not render expanded sub elements if $expand = 'origin'
528
+ /**
529
+ * About Helper property $expand for faster the XSN-to-CSN transformation
530
+ * - null/undefined: artifact, member, items does not contain expanded members
531
+ * - 'origin': all expanded (sub) elements have no new target/on and no new annotations
532
+ * that value is only on elements, types, and params -> no other members
533
+ * when set, only on elem/art with expanded elements
534
+ * - 'target': all expanded (sub) elements might only have new target/on, but
535
+ * no individual annotations on any (sub) member
536
+ * when set, traverse all parents where the value has been 'origin' before
537
+ * - 'annotate': at least one inferred (sub) member has an individual annotation,
538
+ * not counting propagated ones; set up to the definition (main artifact)
539
+ * (only set with anno on $inferred elem), annotate “beats” target
540
+ * Usage according to CSN flavor:
541
+ * - gensrc: do not render inferred elements (including expanded elements),
542
+ * collect annotate statements with value 'annotate'
543
+ * - client: do not render expanded sub elements if artifact/member is no type, has a type,
544
+ * has $expand = 'origin', and all its _origin also have $expand = 'origin'
545
+ * (might sometimes render the elements unnecessarily, which is not wrong)
546
+ * - universal: do not render expanded sub elements if $expand = 'origin'
547
+ */
542
548
  function setExpandStatus( elem, status ) {
543
549
  // set on element
544
550
  while (elem._main) {
@@ -607,7 +613,7 @@ function pathStartsWithSelf( ref ) {
607
613
  }
608
614
 
609
615
  function columnRefStartsWithSelf( col ) {
610
- for (; col; col = col._pathHead) {
616
+ for (; col; col = col._columnParent) {
611
617
  const ref = col.value;
612
618
  const head = ref && !ref.scope && ref.path?.[0];
613
619
  if (head?._navigation?.kind === '$self')
@@ -618,12 +624,14 @@ function columnRefStartsWithSelf( col ) {
618
624
  return false;
619
625
  }
620
626
 
621
- // Remark: this function is based on an early check that no target element is
622
- // covered more than once by a foreign key: then…
623
- // we only need to check that all foreign key references are primary keys and
624
- // that the number of foreign and primary keys are the same.
627
+ /**
628
+ * Remark: this function is based on an early check that no target element is
629
+ * covered more than once by a foreign key: then…
630
+ * we only need to check that all foreign key references are primary keys and
631
+ * that the number of foreign and primary keys are the same.
632
+ */
625
633
  function isAssocToPrimaryKeys( assoc ) {
626
- let fkeys = 0;
634
+ let keyCount = 0;
627
635
  const { foreignKeys } = assoc;
628
636
  if (!foreignKeys)
629
637
  return undefined;
@@ -634,7 +642,7 @@ function isAssocToPrimaryKeys( assoc ) {
634
642
  return undefined;
635
643
  if (!elem.key?.val)
636
644
  return false;
637
- ++fkeys;
645
+ ++keyCount;
638
646
  }
639
647
 
640
648
  const elements = assoc.target._artifact?.elements;
@@ -642,9 +650,9 @@ function isAssocToPrimaryKeys( assoc ) {
642
650
  return undefined;
643
651
  for (const name in elements) {
644
652
  if (elements[name].key?.val)
645
- --fkeys;
653
+ --keyCount;
646
654
  }
647
- return fkeys === 0;
655
+ return keyCount === 0;
648
656
  }
649
657
 
650
658
  // only if _effectiveType has been computed:
@@ -174,7 +174,8 @@ function xprRewriteFns( model ) {
174
174
  resolvePath,
175
175
  navigationEnv,
176
176
  resolvePathRoot,
177
- firstProjectionForPath,
177
+ cachedRedirectionChain,
178
+ findRewriteTarget,
178
179
  } = model.$functions;
179
180
 
180
181
  return {
@@ -355,7 +356,7 @@ function xprRewriteFns( model ) {
355
356
  // On select items, use navigation elements or table alias
356
357
  // TODO: Expand/inline paths don't have a `_navigation` property on their last
357
358
  // path step, yet. We need to implement expand/inline.
358
- const isSimpleSelectItem = target.value?.path && target._main?.query && !target._pathHead;
359
+ const isSimpleSelectItem = target.value?.path && target._main?.query && !target._columnParent;
359
360
  if (isSimpleSelectItem) {
360
361
  const isSelfPath = (expr.path[0]?._navigation?.kind === '$self');
361
362
  if (isSelfPath) {
@@ -393,21 +394,21 @@ function xprRewriteFns( model ) {
393
394
  */
394
395
  function rewriteGenericAnnoPath( expr, config, refCtx ) {
395
396
  const isAbsolute = isAnnoPathAbsolute( expr );
396
- const rootIndex = isAbsolute ? 1 : 0;
397
+ const startIndex = isAbsolute ? 1 : 0;
397
398
 
398
399
  // We get the root environment now, even though below we resolve the root item
399
400
  // again if it was absolute (e.g. $self). We do so, because for queries, we
400
401
  // want to respect the select item's corresponding table alias.
401
402
  const rootEnv = getRootEnv( expr, config );
402
403
 
403
- // reset artifact link; we'll set it again
404
+ // reset artifact link; we'll set it again if there are no errors
404
405
  setArtifactLink( expr, null );
405
406
 
406
- // Adapt root path, as it isn't rewritten in rewriteItem
407
- const rootItem = expr.path[0];
408
407
  if (isAbsolute) {
409
- delete rootItem._artifact;
410
- delete rootItem._navigation;
408
+ // Adapt absolute root path, as it isn't rewritten in rewriteItem
409
+ // The path-prefix was already adapted in rewriteAnnoExpr().
410
+ delete expr.path[0]._artifact;
411
+ delete expr.path[0]._navigation;
411
412
  // TODO: What about `up_`? Shouldn't we set `_navigation` as well?
412
413
  // TODO: Can we handle `$self` of anonymous-composition-of-aspect?
413
414
  const root = resolvePathRoot( expr, refCtx, config.target );
@@ -415,23 +416,46 @@ function xprRewriteFns( model ) {
415
416
  return reportAnnoRewriteError( expr, config );
416
417
  }
417
418
 
419
+ // Store the original artifact, so that we can use it to
420
+ // calculate a redirection chain later on.
421
+ expr.path.forEach((item) => {
422
+ if (item._artifact)
423
+ setLink( item, '_originalArtifact', item._artifact );
424
+ });
425
+
418
426
  let env = rootEnv;
419
- let art = rootItem._artifact;
420
- for (let i = rootIndex; i < expr.path.length; ++i) {
427
+ let art = expr.path[0]._artifact;
428
+
429
+ for (let i = startIndex; i < expr.path.length; ++i) {
430
+ if (i > startIndex && art.target) {
431
+ // if the current artifact is an association, we need to respect the redirection
432
+ // chain from original target to new one.
433
+ // FIXME: Won't work with associations in projected structures.
434
+ const origTarget = expr.path[i - 1]?._originalArtifact?.target?._artifact;
435
+ const chain = cachedRedirectionChain( art, origTarget );
436
+ if (!chain)
437
+ return reportAnnoRewriteError( expr, config );
438
+ for (const alias of chain) {
439
+ art = rewriteItem( expr, config, alias, i );
440
+ if (!art)
441
+ return reportAnnoRewriteError( expr, config );
442
+ }
443
+ }
421
444
  art = rewriteItem( expr, config, env, i );
422
445
  if (!art)
423
446
  return reportAnnoRewriteError( expr, config );
447
+ // target, items, …
424
448
  env = navigationEnv( art, null, null, 'nav' );
425
449
  }
426
450
  setArtifactLink( expr, art );
427
451
 
428
- if (rootIndex === 0 && rootItem.id.startsWith('$')) {
452
+ if (startIndex === 0 && expr.path[0].id.startsWith('$')) {
429
453
  if (config.isInFilter) {
430
454
  // In filters, we must not prepend `$self`, as that would change its meaning.
431
455
  // We must reject it. See #11775
432
456
  return reportAnnoRewriteError( expr, config );
433
457
  }
434
- // After rewriting, an element starts with `$` -> add root prefix
458
+ // After rewriting, if an element starts with `$` -> add root prefix
435
459
  prependRootPath( config.origin, config.targetRoot, expr );
436
460
  }
437
461
 
@@ -640,62 +664,25 @@ function xprRewriteFns( model ) {
640
664
  * @returns {*|null}
641
665
  */
642
666
  function rewriteItem( expr, config, env, index ) {
643
- const item = expr.path[index];
644
667
  const rewriteTarget = findRewriteTarget( expr, index, env, config.target );
645
- const found = setArtifactLink( item, rewriteTarget[0] );
668
+ const found = setArtifactLink( expr.path[index], rewriteTarget[0] );
646
669
  if (!found)
647
670
  return null;
648
671
 
649
- if (item.id !== found.name.id) {
650
- // Path was rewritten; original token text string is no longer accurate
651
- config.tokenExpr.$tokenTexts = true;
652
- item.id = found.name.id;
653
- }
654
-
655
- if (rewriteTarget[1] > index)
672
+ if (rewriteTarget[1] > index) {
673
+ // we keep the last segment, in case it has non-enumerable properties
674
+ expr.path[index] = expr.path[rewriteTarget[1]];
656
675
  expr.path.splice(index + 1, rewriteTarget[1] - index);
657
-
658
- return rewriteTarget[0];
659
- }
660
-
661
- function findRewriteTarget( expr, index, env, target ) {
662
- if (env.kind === '$navElement' || env.kind === '$tableAlias') {
663
- const r = firstProjectionForPath( expr.path, index, env, target );
664
- return [ r.elem, r.index ];
665
676
  }
666
677
 
667
678
  const item = expr.path[index];
668
- // Not a query -> no $navElement -> use `elements`
669
- if (!env.query && env.kind !== 'select') {
670
- if (env.elements?.[item.id])
671
- return [ env.elements[item.id], index ];
672
- return [ null, expr.path.length ];
673
- }
674
- const items = (env._leadingQuery || env)._combined?.[item.id];
675
- const allNavs = !items || Array.isArray(items) ? items : [ items ];
676
-
677
- // If the annotation target itself has a table alias, require projections of that
678
- // table alias. Of course, that only works if we're talking about the same query.
679
- const tableAlias = (target._main?._origin === item._artifact._main &&
680
- target.value?.path[0]?._navigation?.kind === '$tableAlias')
681
- ? target.value.path[0]._navigation : null;
682
-
683
- // Look at all table aliase that could project `item` and only select
684
- // those that have actual projections.
685
- const navs = allNavs?.filter(p => p._origin === item._artifact &&
686
- (!tableAlias || tableAlias === p._parent));
687
- if (!navs || navs.length === 0)
688
- return [ null, expr.path.length ];
689
-
690
- // If there are multiple navigations for the element, just use the first that matches.
691
- // In case of table aliases, it's just one.
692
- for (const nav of navs) {
693
- const r = firstProjectionForPath( expr.path, index, nav._parent, target );
694
- if (r.elem)
695
- return [ r.elem, r.index ];
679
+ if (item.id !== found.name.id || (rewriteTarget[1] - index) !== 0) {
680
+ // Path was rewritten; original token text string is no longer accurate
681
+ config.tokenExpr.$tokenTexts = true;
682
+ item.id = found.name.id;
696
683
  }
697
684
 
698
- return [ null, expr.path.length ];
685
+ return setArtifactLink( expr.path[index], found );
699
686
  }
700
687
  }
701
688
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { isEdmPropertyRendered, transformExpression } = require('../../model/csnUtils');
3
+ const { isEdmPropertyRendered, transformAnnotationExpression } = require('../../model/csnUtils');
4
4
  const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
5
5
  const edmUtils = require('../edmUtils.js');
6
6
  const oDataDictionary = require('../../gen/Dictionary.json');
@@ -131,7 +131,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
131
131
  const knownAnnos = filterKnownAnnotations(carrier);
132
132
  knownAnnos.forEach((pn) => {
133
133
  scopeCheck.anno = pn;
134
- transformExpression(carrier, pn, scopeCheck, carrier.$path);
134
+ transformAnnotationExpression(carrier, pn, scopeCheck, carrier.$path);
135
135
  });
136
136
  });
137
137
  }
@@ -141,7 +141,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
141
141
  const knownAnnos = filterKnownAnnotations(obj);
142
142
  knownAnnos.forEach((pn) => {
143
143
  scopeCheck.anno = pn;
144
- transformExpression(obj, pn, scopeCheck, obj.$path);
144
+ transformAnnotationExpression(obj, pn, scopeCheck, obj.$path);
145
145
  });
146
146
  };
147
147
  if (def.$isParamEntity && def._origin) {
@@ -171,7 +171,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
171
171
  if (innerAnnotation)
172
172
  newAnno += `.@${innerAnnotation}`;
173
173
  edmUtils.assignAnnotation(def, newAnno, def._origin[attr]);
174
- transformExpression(def._origin, attr, scopeCheck, def._origin.$path);
174
+ transformAnnotationExpression(def._origin, attr, scopeCheck, def._origin.$path);
175
175
  if (paramAnnoParts.length > 1)
176
176
  delete def._origin[attr];
177
177
  }
@@ -419,7 +419,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
419
419
 
420
420
  knownAnnos.forEach((knownAnno) => {
421
421
  if (knownAnno.search(/\.\$edmJson\./g) < 0) {
422
- transformExpression(carrier, knownAnno, {
422
+ transformAnnotationExpression(carrier, knownAnno, {
423
423
  ref: (elemref, prop, xpr, csnPath) => {
424
424
  if (options.isV2() && elemref.$bparam) {
425
425
  error('odata-anno-xpr-ref', ctx.location, {
@@ -842,8 +842,8 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
842
842
  * */
843
843
  let newAnno;
844
844
  const omissions = { 'Aggregation.default': 1 };
845
- const nullList = { 'Core.OperationAvailable': 1 };
846
- if (annoValue !== null && !omissions[termName] || nullList[termName]) {
845
+ const nullList = { 'Core.OperationAvailable': 1, 'Core.OptionalParameter': 1 };
846
+ if (annoValue != null && !omissions[termName] || nullList[termName]) {
847
847
  // termName may contain a qualifier: @UI.FieldGroup#shippingStatus
848
848
  // -> remove qualifier from termName and set Qualifier attribute in newAnno
849
849
  const i = termName.indexOf('#');
@@ -932,7 +932,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
932
932
  anno: msg.anno(),
933
933
  type: dTypeName,
934
934
  value: `"#${enumSymbol}"`,
935
- rawvalues: Object.keys(typeDef.$Allowed.Symbols).map(m => `"#${m}"`),
935
+ rawvalues: Object.keys(typeDef.$Allowed.Symbols).map(m => `#${m}`),
936
936
  '#': 'enum',
937
937
  });
938
938
  }
@@ -989,6 +989,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
989
989
  else {
990
990
  const res = handleSimpleValue(cAnnoValue, dTypeName, msg);
991
991
  if (((oTermName === 'Core.OperationAvailable' && dTypeName === 'Edm.Boolean') ||
992
+ (oTermName === 'Core.OptionalParameter' && dTypeName === 'Edm.String') ||
992
993
  (oTermName === 'Validation.AllowedValues' && dTypeName === 'Edm.PrimitiveType')) &&
993
994
  cAnnoValue === null) {
994
995
  oTarget.append(new Edm.ValueThing(v, 'Null'));
@@ -1023,7 +1024,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1023
1024
  anno: msg.anno(),
1024
1025
  type: dTypeName,
1025
1026
  value: `"#${value}"`,
1026
- rawvalues: expectedType.Members.map(m => `"#${m}"`),
1027
+ rawvalues: expectedType.Members.map(m => `#${m}`),
1027
1028
  '#': 'enum',
1028
1029
  });
1029
1030
  }
@@ -1059,7 +1060,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1059
1060
  anno: msg.anno(),
1060
1061
  type: dTypeName,
1061
1062
  value: value['='] || value,
1062
- rawvalues: type.Members.map(m => `"#${m}"`),
1063
+ rawvalues: type.Members.map(m => `#${m}`),
1063
1064
  '#': 'enum',
1064
1065
  });
1065
1066
  }
@@ -1119,7 +1120,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1119
1120
 
1120
1121
  if (isEnumType(resolvedType)) {
1121
1122
  const type = getDictType(resolvedType);
1122
- const expected = type.Members.map(m => `"#${m}"`);
1123
+ const expected = type.Members.map(m => `#${m}`);
1123
1124
  message('odata-anno-value', msg.location,
1124
1125
  {
1125
1126
  anno: msg.anno(),
@@ -1230,7 +1231,10 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1230
1231
  }
1231
1232
  }
1232
1233
  else if (value === null) {
1233
- if ((resolvedType == null || resolvedType === 'Edm.PrimitiveType') && typeName === 'String') {
1234
+ if ((resolvedType == null ||
1235
+ resolvedType === 'Edm.PrimitiveType' ||
1236
+ resolvedType === 'Edm.String') &&
1237
+ typeName === 'String') {
1234
1238
  resolvedType = 'Edm.String';
1235
1239
  }
1236
1240
  else {
@@ -1589,7 +1593,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1589
1593
 
1590
1594
  function filterKnownAnnotations( carrier ) {
1591
1595
  const annoNames = Object.keys(carrier).filter( x => x[0] === '@' );
1592
- const nullWhitelist = [ '@Core.OperationAvailable' ];
1596
+ const nullWhitelist = [ '@Core.OperationAvailable', '@Core.OptionalParameter.DefaultValue' ];
1593
1597
  const knownAnnosP = annoNames.filter((n) => {
1594
1598
  const tns = whatsMyTermNamespace(n);
1595
1599
  return tns &&
@@ -266,8 +266,19 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
266
266
  });
267
267
  }
268
268
  });
269
+ if (!options.odataNoCreator) {
270
+ // remove unqualified @Core.Links and #CAP
271
+ Object.keys(serviceCsn).forEach((key) => {
272
+ if (key === '@Core.Links' || key.startsWith('@Core.Links.') ||
273
+ key === '@Core.Links#CAP' || key.startsWith('@Core.Links#CAP.'))
274
+ delete serviceCsn[key];
275
+ });
276
+ }
277
+
269
278
  // Create annotations and distribute into Schemas, merge vocabulary cross refs into xServiceRefs
270
- addAnnotations2XServiceRefs();
279
+ addAnnotationsAndXServiceRefs();
280
+ if (!options.odataNoCreator)
281
+ LeadSchema.prepend(waterMark());
271
282
 
272
283
  // Finally add cross service references into the EDM and extract the targetSchemaNames
273
284
  // for the type cross check
@@ -308,6 +319,18 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
308
319
 
309
320
  return edm;
310
321
 
322
+ function waterMark() {
323
+ const rel = new Edm.PropertyValue(v, 'rel');
324
+ rel._xmlOnlyAttributes.String = 'author';
325
+ rel._jsonOnlyAttributes['Edm.String'] = 'author';
326
+ const href = new Edm.PropertyValue(v, 'href');
327
+ href._xmlOnlyAttributes.String = 'https://cap.cloud.sap';
328
+ href._jsonOnlyAttributes['Edm.String'] = 'https://cap.cloud.sap';
329
+ const watermark = new Edm.Annotation(v, 'Core.Links', new Edm.Collection(v, new Edm.Record(v, rel, href)));
330
+ // watermark._edmAttributes['Qualifier'] = 'CAP';
331
+ return watermark;
332
+ }
333
+
311
334
  // Sort definitions into their schema container
312
335
  function populateSchemas( schemas ) {
313
336
  forEach(reqDefs.definitions, ( fqName, art ) => {
@@ -713,8 +736,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
713
736
  attributes.IsComposable = false;
714
737
 
715
738
  /** @type {object} */
716
- const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
717
- : new Edm.FunctionDefinition(v, attributes);
739
+ const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes, actionCsn)
740
+ : new Edm.FunctionDefinition(v, attributes, actionCsn);
718
741
 
719
742
  const bpType = entityCsn ? fullQualified(entityCsn.name) : undefined;
720
743
  /*
@@ -1099,8 +1122,9 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
1099
1122
  }
1100
1123
 
1101
1124
  // generate the Edm.Annotations tree and append it to the corresponding schema
1102
- function addAnnotations2XServiceRefs( ) {
1125
+ function addAnnotationsAndXServiceRefs( ) {
1103
1126
  options.getFinalTypeInfo = csnUtils.getFinalTypeInfo;
1127
+
1104
1128
  const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csnUtils, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
1105
1129
  // distribute edm:Annotations into the schemas
1106
1130
  // Distribute each anno into Schema