@sap/cds-compiler 2.5.0 → 2.10.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +191 -9
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +33 -3
  4. package/lib/api/main.js +29 -101
  5. package/lib/api/options.js +15 -11
  6. package/lib/api/validate.js +12 -8
  7. package/lib/backends.js +0 -81
  8. package/lib/base/keywords.js +32 -2
  9. package/lib/base/message-registry.js +63 -9
  10. package/lib/base/messages.js +63 -21
  11. package/lib/base/model.js +2 -3
  12. package/lib/checks/defaultValues.js +27 -2
  13. package/lib/checks/elements.js +1 -6
  14. package/lib/checks/foreignKeys.js +0 -6
  15. package/lib/checks/managedWithoutKeys.js +17 -0
  16. package/lib/checks/nonexpandableStructured.js +38 -0
  17. package/lib/checks/onConditions.js +9 -45
  18. package/lib/checks/queryNoDbArtifacts.js +25 -7
  19. package/lib/checks/selectItems.js +25 -2
  20. package/lib/checks/types.js +26 -2
  21. package/lib/checks/unknownMagic.js +38 -0
  22. package/lib/checks/utils.js +61 -0
  23. package/lib/checks/validator.js +60 -7
  24. package/lib/compiler/assert-consistency.js +16 -7
  25. package/lib/compiler/builtins.js +2 -0
  26. package/lib/compiler/checks.js +6 -4
  27. package/lib/compiler/definer.js +99 -42
  28. package/lib/compiler/index.js +73 -27
  29. package/lib/compiler/resolver.js +288 -157
  30. package/lib/compiler/shared.js +31 -11
  31. package/lib/edm/annotations/genericTranslation.js +182 -186
  32. package/lib/edm/csn2edm.js +103 -108
  33. package/lib/edm/edm.js +18 -21
  34. package/lib/edm/edmPreprocessor.js +361 -114
  35. package/lib/edm/edmUtils.js +103 -33
  36. package/lib/gen/Dictionary.json +22 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +12 -1
  39. package/lib/gen/language.tokens +57 -53
  40. package/lib/gen/languageLexer.interp +10 -1
  41. package/lib/gen/languageLexer.js +770 -744
  42. package/lib/gen/languageLexer.tokens +49 -46
  43. package/lib/gen/languageParser.js +4713 -4279
  44. package/lib/json/from-csn.js +103 -45
  45. package/lib/json/to-csn.js +296 -117
  46. package/lib/language/antlrParser.js +4 -3
  47. package/lib/language/errorStrategy.js +1 -0
  48. package/lib/language/genericAntlrParser.js +21 -12
  49. package/lib/language/language.g4 +99 -31
  50. package/lib/main.d.ts +81 -3
  51. package/lib/main.js +30 -7
  52. package/lib/model/api.js +78 -0
  53. package/lib/model/csnRefs.js +329 -142
  54. package/lib/model/csnUtils.js +235 -58
  55. package/lib/model/enrichCsn.js +18 -1
  56. package/lib/model/revealInternalProperties.js +2 -1
  57. package/lib/modelCompare/compare.js +37 -20
  58. package/lib/optionProcessor.js +9 -3
  59. package/lib/render/.eslintrc.json +4 -1
  60. package/lib/render/DuplicateChecker.js +8 -5
  61. package/lib/render/toCdl.js +112 -33
  62. package/lib/render/toHdbcds.js +134 -64
  63. package/lib/render/toSql.js +91 -38
  64. package/lib/render/utils/common.js +8 -13
  65. package/lib/render/utils/sql.js +3 -3
  66. package/lib/sql-identifier.js +6 -1
  67. package/lib/transform/db/assertUnique.js +5 -6
  68. package/lib/transform/db/constraints.js +29 -13
  69. package/lib/transform/db/draft.js +8 -6
  70. package/lib/transform/db/expansion.js +582 -0
  71. package/lib/transform/db/flattening.js +325 -0
  72. package/lib/transform/db/groupByOrderBy.js +2 -2
  73. package/lib/transform/db/transformExists.js +284 -63
  74. package/lib/transform/forHanaNew.js +98 -381
  75. package/lib/transform/forOdataNew.js +21 -22
  76. package/lib/transform/localized.js +37 -10
  77. package/lib/transform/odata/attachPath.js +19 -4
  78. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  79. package/lib/transform/odata/referenceFlattener.js +60 -39
  80. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  81. package/lib/transform/odata/structuralPath.js +72 -0
  82. package/lib/transform/odata/structureFlattener.js +19 -18
  83. package/lib/transform/odata/typesExposure.js +22 -12
  84. package/lib/transform/transformUtilsNew.js +134 -78
  85. package/lib/transform/translateAssocsToJoins.js +17 -14
  86. package/lib/transform/universalCsnEnricher.js +67 -0
  87. package/lib/utils/file.js +0 -11
  88. package/lib/utils/moduleResolve.js +6 -8
  89. package/package.json +1 -1
  90. package/lib/json/walker.js +0 -26
  91. package/lib/transform/sqlite +0 -0
  92. package/lib/utils/string.js +0 -17
@@ -3,8 +3,8 @@
3
3
  /* eslint max-lines:off */
4
4
  /* eslint max-statements-per-line:off */
5
5
 
6
- let NAVPROP_TRENNER = '_';
7
- let VALUELIST_NAVPROP_PREFIX = '';
6
+ const NAVPROP_TRENNER = '_';
7
+ const VALUELIST_NAVPROP_PREFIX = '';
8
8
 
9
9
  const edmUtils = require('./edmUtils.js')
10
10
  const { initializeModel } = require('./edmPreprocessor.js');
@@ -25,11 +25,11 @@ function csn2edm(_csn, serviceName, _options) {
25
25
 
26
26
  function csn2edmAll(_csn, _options, serviceNames=undefined) {
27
27
  // get us a fresh model copy that we can work with
28
- let csn = cloneCsn(_csn, _options);
28
+ const csn = cloneCsn(_csn, _options);
29
29
 
30
30
  // use original options for messages; cloned CSN for semantic location
31
31
  const messageFunctions = makeMessageFunction(csn, _options, 'to.edmx');
32
- const { info, warning, error, throwWithError } = messageFunctions;
32
+ const { info, warning, error, message, throwWithError } = messageFunctions;
33
33
  checkCSNVersion(csn, _options);
34
34
 
35
35
  let rc = Object.create(null);
@@ -42,10 +42,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
42
42
  setProp(csn.meta, 'options', _csn.meta.options);
43
43
  }
44
44
 
45
- let [ allServices, allSchemas, whatsMyServiceRootName, options ] = initializeModel(csn, _options);
45
+ let [ allServices,
46
+ allSchemas,
47
+ whatsMyServiceRootName,
48
+ options ] = initializeModel(csn, _options, messageFunctions);
49
+
46
50
  const Edm = require('./edm.js')(options, error);
47
51
 
48
- let v = options.v;
52
+ const v = options.v;
49
53
  if(Object.keys(allServices).length === 0) {
50
54
  info(null, null, `No Services in model`);
51
55
  return rc;
@@ -138,7 +142,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
138
142
  definitions: Object.create(null)
139
143
  }
140
144
  };
141
-
145
+
142
146
  if(options.isV4()) {
143
147
  // tunnel schema xref and servicename in options to edm.Typebase to rectify
144
148
  // type references that are eventually also prefixed with the service schema name.
@@ -222,7 +226,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
222
226
  }));
223
227
  });
224
228
  // Create annotations and distribute into Schemas
225
- translate.csn2annotationEdm(csn, edm, serviceCsn.name, options);
229
+ addAnnotations();
226
230
  return edm
227
231
 
228
232
  // Sort definitions into their schema container
@@ -282,8 +286,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
282
286
 
283
287
  return Object.entries(allSchemas).reduce((references, [fqName, art]) => {
284
288
  // add references
285
- let mySchemaName = whatsMySchemaName(fqName);
286
- if(art.kind === 'reference' && mySchemaName && serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
289
+ if(art.kind === 'reference' && whatsMySchemaName(fqName) && serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
287
290
  fqSchemaXRef.push(fqName);
288
291
  references.push(art);
289
292
  }
@@ -298,8 +301,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
298
301
  // Same check for alias (if supported by us)
299
302
  const reservedNames = ['Edm', 'odata', 'System', 'Transient'];
300
303
  if(reservedNames.includes(schema.name)) {
301
- warning('odata-spec-violation-namespace-name', [ 'definitions', schema.name ], { name: schema.name, names: reservedNames },
302
- 'The EDM namespace $(NAME) must not be one of the reserved values $(NAMES)');
304
+ warning('odata-spec-violation-namespace',
305
+ [ 'definitions', schema.name ], { names: reservedNames });
303
306
  }
304
307
  const Schema = new Edm.Schema(v, schema.name, undefined /* unset alias */, schema._csn, /* annotations */ [], schema.container);
305
308
  const EntityContainer = Schema._ec || (LeadSchema && LeadSchema._ec);
@@ -307,7 +310,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
307
310
  const schemaNamePrefix = schema.name + '.'
308
311
  const schemaAliasPrefix = schemaNamePrefix;
309
312
  const schemaCsn = schema;
310
- let navigationProperties = [];
313
+ const navigationProperties = [];
311
314
 
312
315
  /* create the entitytypes and sets
313
316
  Do not create an entity set if:
@@ -337,7 +340,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
337
340
  }
338
341
 
339
342
  // fetch all exising children names in a map
340
- let NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
343
+ const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
341
344
  if(acc[cur.Name] === undefined) {
342
345
  acc[cur.Name] = [ cur ];
343
346
  } else {
@@ -359,8 +362,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
359
362
 
360
363
  // remove EntityContainer if empty
361
364
  if(Schema._ec && Schema._ec._children.length === 0) {
362
- let pos = Schema._children.indexOf(Schema._ec);
363
- Schema._children.splice(pos, 1);
365
+ Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
364
366
  }
365
367
  if(Schema._children.length === 0) {
366
368
  // FIXME: Location for sub schemas?
@@ -369,8 +371,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
369
371
 
370
372
  Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
371
373
  if(refs.length > 1) {
372
- let artifactName = `${Schema.Namespace}.${name}`;
373
- error(null, ['definitions', artifactName], { name: Schema.Namespace },
374
+ error(null, ['definitions', `${Schema.Namespace}.${name}`], { name: Schema.Namespace },
374
375
  'Duplicate name in Schema $(NAME)');
375
376
  }
376
377
  });
@@ -379,10 +380,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
379
380
 
380
381
  function createEntityTypeAndSet(entityCsn)
381
382
  {
382
- let EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
383
- let EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
383
+ const EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
384
+ const EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
384
385
 
385
- let [ properties, hasStream ] = createProperties(entityCsn);
386
+ const [ properties, hasStream ] = createProperties(entityCsn);
386
387
 
387
388
  const loc = ['definitions', entityCsn.name];
388
389
  const type = `${schema.name}.${EntityTypeName}`;
@@ -394,18 +395,18 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
394
395
  properties.forEach(p => {
395
396
  const pLoc = [...loc, 'elements', p.Name];
396
397
  if(!p[p._typeName]) {
397
- warning('odata-spec-violation-attribute', pLoc, { name: p.Name, prop: p._typeName });
398
+ message('odata-spec-violation-type', pLoc);
398
399
  }
399
400
  if(p.Name === EntityTypeName) {
400
- warning('odata-spec-violation-property-name', pLoc, { name: p.Name, type: 'EntityType' });
401
+ warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
401
402
  }
402
403
  if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
403
- warning('odata-spec-violation-array-of-v2', pLoc, { literal: 'Property', prop: 'Type', name: 'Collection' });
404
+ warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
404
405
  }
405
406
  });
406
407
 
407
408
  // construct EntityType attributes
408
- let attributes = { Name : EntityTypeName };
409
+ const attributes = { Name : EntityTypeName };
409
410
 
410
411
  // CDXCORE-CDXCORE-173
411
412
  if(options.isV2() && hasStream)
@@ -418,7 +419,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
418
419
  /** @type {object} */
419
420
  let containerEntry;
420
421
 
421
- if(isSingleton(entityCsn)) {
422
+ if(edmUtils.isSingleton(entityCsn, options.isV4())) {
422
423
  containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
423
424
  if(entityCsn['@odata.singleton.nullable'])
424
425
  containerEntry.Nullable= true;
@@ -428,18 +429,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
428
429
  }
429
430
 
430
431
  // V4: Create NavigationPropertyBinding in EntitySet
431
- if(options.isV4())
432
- properties.filter(np =>
433
- np instanceof Edm.NavigationProperty &&
434
- // @ts-ignore TypeScript does not recognize these properties on type NavigationProperty
435
- !np.isContainment() &&
436
- !edmUtils.isContainee(np._targetCsn) &&
437
- !np._targetCsn.$proxy &&
438
- !np._targetCsn.$externalRef &&
439
- !(np._isCollection && isSingleton(np._targetCsn))
440
- ). forEach(np =>
441
- containerEntry.append(np.createNavigationPropertyBinding(schemaNamePrefix)));
442
-
432
+ if(options.isV4()) {
433
+ entityCsn.$edmNPBs.forEach(npb => {
434
+ containerEntry.append(new Edm.NavigationPropertyBinding(v, npb))
435
+ });
436
+ }
443
437
  EntityContainer.register(containerEntry);
444
438
  }
445
439
 
@@ -448,38 +442,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
448
442
  (options.isV4()) ? createActionV4(a, n, entityCsn)
449
443
  : createActionV2(a, n, entityCsn)
450
444
  });
451
-
452
- function isSingleton(entityCsn) {
453
- const singleton = entityCsn['@odata.singleton'];
454
- const hasNullable = entityCsn['@odata.singleton.nullable'] !== undefined && entityCsn['@odata.singleton.nullable'] !== null;
455
- return options.isV4() && singleton || ((singleton === undefined || singleton === null) && hasNullable);
456
- }
457
445
  }
458
446
 
459
447
  // add bound/unbound actions/functions for V4
460
448
  function createActionV4(actionCsn, name, entityCsn=undefined)
461
449
  {
462
- let iAmAnAction = actionCsn.kind === 'action';
450
+ const iAmAnAction = actionCsn.kind === 'action';
463
451
 
464
- let actionName = edmUtils.getBaseName(actionCsn.name);
452
+ const actionName = edmUtils.getBaseName(actionCsn.name);
465
453
 
466
- let attributes = { Name: actionName, IsBound : false };
454
+ const attributes = { Name: actionName, IsBound : false };
467
455
 
468
456
  if(!iAmAnAction)
469
457
  attributes.IsComposable = false;
470
458
 
471
459
  /** @type {object} */
472
- let actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
460
+ const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
473
461
  : new Edm.FunctionDefinition(v, attributes);
474
462
 
475
463
  // bpName is eventually used later for EntitySetPath
476
- let bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
477
- let bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
464
+ const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
465
+ const bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
478
466
 
479
467
  if(entityCsn != undefined)
480
468
  {
481
469
  actionNode.IsBound = true;
482
- let bpType = fullQualified(entityCsn.name);
470
+ const bpType = fullQualified(entityCsn.name);
483
471
  // Binding Parameter: 'in' at first position in sequence, this is decisive!
484
472
  if(actionCsn['@cds.odata.bindingparameter.collection'])
485
473
  actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true } ));
@@ -489,14 +477,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
489
477
  else if(EntityContainer)// unbound => produce Action/FunctionImport
490
478
  {
491
479
  /** @type {object} */
492
- let actionImport = iAmAnAction
480
+ const actionImport = iAmAnAction
493
481
  ? new Edm.ActionImport(v, { Name: actionName, Action : fullQualified(actionName) })
494
482
  : new Edm.FunctionImport(v, { Name: actionName, Function : fullQualified(actionName) });
495
483
 
496
- let rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
484
+ const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
497
485
  if(rt) // add EntitySet attribute only if return type is a non abstract entity
498
486
  {
499
- let definition = schemaCsn.definitions[rt];
487
+ const definition = schemaCsn.definitions[rt];
500
488
  if(definition && definition.kind === 'entity' && !definition.abstract)
501
489
  {
502
490
  actionImport.EntitySet = edmUtils.getBaseName(rt);
@@ -525,7 +513,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
525
513
  function createActionV2(actionCsn, name, entityCsn=undefined)
526
514
  {
527
515
  /** @type {object} */
528
- let functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
516
+ const functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
529
517
 
530
518
  // inserted now to maintain attribute order with old odata generator...
531
519
  /*
@@ -539,10 +527,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
539
527
  */
540
528
 
541
529
  const actLoc = ['definitions', ...(entityCsn ? [entityCsn.name, 'actions', actionCsn.name] : [actionCsn.name])];
542
- let rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
530
+ const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
543
531
  if(rt) // add EntitySet attribute only if return type is an entity
544
532
  {
545
- let defintion = schemaCsn.definitions[rt];
533
+ const defintion = schemaCsn.definitions[rt];
546
534
  if(defintion && edmUtils.isEntity(defintion))
547
535
  {
548
536
  functionImport.EntitySet = rt.replace(schemaNamePrefix, '');
@@ -586,10 +574,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
586
574
  const paramLoc = [...actLoc, 'params', parameterName];
587
575
  const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
588
576
  if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
589
- warning('odata-spec-violation-param-v2', paramLoc);
577
+ warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
590
578
  }
591
579
  if(param._isCollection) {
592
- warning('odata-spec-violation-array-of-v2', paramLoc, { literal: 'FunctionImport Parameter', prop: 'Type', name: 'Collection' });
580
+ warning('odata-spec-violation-array', paramLoc, { api: 'OData V2' });
593
581
  }
594
582
  functionImport.append(param);
595
583
  });
@@ -600,12 +588,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
600
588
  function getReturnType(action)
601
589
  {
602
590
  // it is safe to assume that either type or items.type are set
603
- let returns = action.returns.items || action.returns;
591
+ const returns = action.returns.items || action.returns;
604
592
  let type = returns.type;
605
593
  if(type){
606
594
  if(!isBuiltinType(type) && !['entity', 'view', 'type'].includes(csn.definitions[type].kind)){
607
595
  const returnsLoc = [ ...actLoc, 'returns'];
608
- warning('odata-spec-violation-returns-v2', returnsLoc);
596
+ warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
609
597
  }
610
598
  type = edmUtils.mapCdsToEdmType(returns, messageFunctions, options.isV2());
611
599
  }
@@ -618,19 +606,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
618
606
  }
619
607
 
620
608
  /**
621
- * @param {object} parentCsn
609
+ * @param {object} elementsCsn
610
+ * @param {object} edmParentCsn
622
611
  * @returns {[object[], boolean]} Returns a [ [ Edm Properties ], boolean hasStream ]:
623
612
  * array of Edm Properties
624
613
  * boolean hasStream : true if at least one element has @Core.MediaType assignment
625
614
  */
626
- function createProperties(parentCsn)
615
+ function createProperties(elementsCsn, edmParentCsn=elementsCsn)
627
616
  {
628
- let props = [];
617
+ const props = [];
629
618
  let hasStream = false;
630
- edmUtils.forAll(parentCsn.elements, (elementCsn, elementName) =>
619
+ edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
631
620
  {
632
- if(elementCsn._parent == undefined)
633
- setProp(elementCsn, '_parent', parentCsn);
621
+ if(elementCsn._edmParentCsn == undefined)
622
+ setProp(elementCsn, '_edmParentCsn', edmParentCsn);
634
623
 
635
624
  if(!elementCsn._ignore) {
636
625
  if(edmUtils.isAssociationOrComposition(elementCsn))
@@ -644,11 +633,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
644
633
  // (undefined !== false) still evaluates to true
645
634
  if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
646
635
  {
647
- let navProp = new Edm.NavigationProperty(v, {
636
+ const navProp = new Edm.NavigationProperty(v, {
648
637
  Name: elementName,
649
638
  Type: elementCsn._target.name
650
639
  }, elementCsn);
651
-
652
640
  props.push(navProp);
653
641
  // save the navProp in the global array for late constraint building
654
642
  navigationProperties.push(navProp);
@@ -667,7 +655,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
667
655
  // V2: don't render element but add attribute 'm:HasStream="true' to EntityType
668
656
  // V4: render property type 'Edm.Stream'
669
657
  hasStream = true;
670
- info(null, ['definitions', parentCsn.name], { name: parentCsn.name, id: elementName, anno: '@Core.MediaType' },
658
+ info(null, ['definitions', elementsCsn.name], { name: elementsCsn.name, id: elementName, anno: '@Core.MediaType' },
671
659
  '$(NAME): Property $(ID) annotated with $(ANNO) is removed from EDM in OData V2');
672
660
 
673
661
  } else {
@@ -683,11 +671,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
683
671
  function createComplexType(structuredTypeCsn)
684
672
  {
685
673
  // V4 attributes: Name, BaseType, Abstract, OpenType
686
- let attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
674
+ const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
687
675
 
688
- let complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
689
- let elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
690
- let properties = createProperties(elementsCsn)[0];
676
+ const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
677
+ const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
678
+ const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
691
679
  const loc = ['definitions', structuredTypeCsn.name];
692
680
 
693
681
  if(properties.length === 0) {
@@ -697,17 +685,17 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
697
685
  properties.forEach(p => {
698
686
  const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p.Name ];
699
687
  if(!p[p._typeName]) {
700
- warning('odata-spec-violation-attribute', pLoc, { name: p.Name, prop: p._typeName });
688
+ message('odata-spec-violation-type', pLoc);
701
689
  }
702
690
  if(p.Name === complexType.Name) {
703
- warning('odata-spec-violation-property-name', pLoc, { name: p.Name, type: complexType.kind });
691
+ warning('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
704
692
  }
705
693
  if(options.isV2()) {
706
694
  if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
707
- warning('odata-spec-violation-array-of-v2', pLoc, { literal: 'Property' ,prop: 'Type', name: 'Collection' });
695
+ warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
708
696
  }
709
697
  if(edmUtils.isAssociationOrComposition(p._csn)) {
710
- warning('odata-spec-violation-assoc-v2', pLoc, { name: structuredTypeCsn.name });
698
+ warning('odata-spec-violation-assoc', pLoc, { api: 'OData V2' });
711
699
  }
712
700
  }
713
701
  });
@@ -722,7 +710,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
722
710
  function createTypeDefinition(typeCsn)
723
711
  {
724
712
  // derived types are already resolved to base types
725
- let props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
713
+ const props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
726
714
  const typeDef = new Edm.TypeDefinition(v, props, typeCsn );
727
715
  Schema.append(typeDef);
728
716
  }
@@ -744,7 +732,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
744
732
  function addAssociation(navigationProperty)
745
733
  {
746
734
  let constraints = navigationProperty._csn._constraints;
747
- let parentName = navigationProperty._csn._parent.name.replace(schemaNamePrefix, '');
735
+ let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
748
736
  let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
749
737
  let assocName = plainAssocName;
750
738
  let i = 1;
@@ -760,7 +748,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
760
748
 
761
749
  // The entity set name may not be the same as the type name (parameterized entities have
762
750
  // differing set names (<T>Parameters => <T>, <T>Type => <T>Set)
763
- let fromEntitySet = ( navigationProperty._csn._parent.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
751
+ let fromEntitySet = ( navigationProperty._csn._edmParentCsn.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
764
752
  let toEntitySet = (navigationProperty._targetCsn.$entitySetName || toEntityType).replace(schemaNamePrefix, '');
765
753
 
766
754
  // from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })
@@ -802,7 +790,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
802
790
  [ fromEntityType, toEntityType ] = [ toEntityType, fromEntityType ];
803
791
  [ fromEntitySet, toEntitySet ] = [ toEntitySet, fromEntitySet ];
804
792
 
805
- parentName = forwardAssocCsn._parent.name.replace(schemaNamePrefix, '');
793
+ parentName = forwardAssocCsn._edmParentCsn.name.replace(schemaNamePrefix, '');
806
794
  assocName = plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
807
795
  i = 1;
808
796
  while(NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association)) {
@@ -817,56 +805,37 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
817
805
  }
818
806
 
819
807
  if(reuseAssoc)
820
- {
821
- // Example:
822
- // entity E { key id: Integer; toF: association to F; };
823
- // entity F { key id: Integer; toE: composition of E on toE.toF = $self; };
824
- //
825
- // Consider we're in NavigationProperty 'toE' which is the backlink to F.
826
- // Then forwardAssocCsn is 'E_toF' with two Ends: E, F.
827
- // Backlink F.toE is a composition, making E existentially dependant on F.
828
- // So End E of Association E_toF (which is End[0]) receives Edm.OnDelete.
829
- // Depending on the order of the navigation properties it might be that the
830
- // forward Edm.Association has not yet been produced. In this case Edm.OnDelete
831
- // is parked at the forward NavigationProperty.
832
-
833
- if(!forwardAssocCsn._NavigationProperty._edmAssociation && navigationProperty._csn.type === 'cds.Composition')
834
- {
835
- // TODO: to be specified via @sap.on.delete
836
- forwardAssocCsn._NavigationProperty.set( { _OnDelete: new Edm.OnDelete(v, { Action: 'Cascade' }) } );
837
- }
838
808
  return;
839
- }
840
809
 
841
810
  // Create Association and AssociationSet if this is not a backlink association.
842
811
  // Store association at navigation property because in case the Ends must be modified
843
812
  // later by the partner (backlink) association
844
- navigationProperty._edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
813
+ const edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
845
814
  [ fromRole, fullQualified(fromEntityType) ],
846
815
  [ toRole, fullQualified(toEntityType) ],
847
816
  constraints._multiplicity );
848
817
  if(NamesInSchemaXRef[assocName] === undefined) {
849
- NamesInSchemaXRef[assocName] = [ navigationProperty._edmAssociation ];
818
+ NamesInSchemaXRef[assocName] = [ edmAssociation ];
850
819
  }
851
820
  else {
852
- navigationProperty._edmAssociation.push(navigationProperty._edmAssociation);
821
+ NamesInSchemaXRef[assocName].push(edmAssociation);
853
822
  }
854
823
  // Add ReferentialConstraints if any
855
824
  if(!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
856
825
  // A managed composition is treated as association
857
826
  if(navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
858
- navigationProperty._edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
827
+ edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
859
828
  toRole, fromRole, constraints.constraints));
860
829
  }
861
830
  else {
862
- navigationProperty._edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
831
+ edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
863
832
  fromRole, toRole, constraints.constraints));
864
833
  }
865
834
  }
866
835
 
867
- Schema.append(navigationProperty._edmAssociation);
836
+ Schema.append(edmAssociation);
868
837
  if(EntityContainer && !navigationProperty._targetCsn.$proxy) {
869
- let assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
838
+ const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
870
839
  fromRole, toRole, fromEntitySet, toEntitySet);
871
840
  if(navigationProperty._csn._SetAttributes)
872
841
  assocSet.setSapVocabularyAsAttributes(navigationProperty._csn._SetAttributes);
@@ -880,6 +849,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
880
849
  return schemaAliasPrefix + name.replace(schemaNamePrefix, '')
881
850
  }
882
851
  }
852
+
853
+ // generate the Edm.Annotations tree and append it to the corresponding schema
854
+ function addAnnotations() {
855
+ let { annos, usedVocabularies } = translate.csn2annotationEdm(csn, serviceCsn.name, Edm, options, messageFunctions);
856
+ // distribute edm:Annotations into the schemas
857
+ // Distribute each anno into Schema
858
+ annos.forEach(anno => {
859
+ let targetSchema = whatsMySchemaName(anno.Target);
860
+ // if no target schema has been found, it's a service annotation that applies to the service schema
861
+ if(targetSchema === undefined)
862
+ targetSchema = serviceCsn.name;
863
+ if(targetSchema) {
864
+ if(targetSchema !== serviceCsn.name) {
865
+ anno.Target = anno.Target.replace(serviceCsn.name + '.', '');
866
+ }
867
+ edm._service._schemas[targetSchema]._annotations.push(anno);
868
+ }
869
+ });
870
+ annos = [];
871
+ // add references for the used vocabularies
872
+ usedVocabularies.forEach(voc => {
873
+ let r = new Edm.Reference(v, voc.ref);
874
+ r.append(new Edm.Include(v, voc.inc))
875
+ edm._defaultRefs.push(r);
876
+ })
877
+ }
883
878
  }
884
879
  }
885
880
  module.exports = { csn2edm, csn2edmAll };
package/lib/edm/edm.js CHANGED
@@ -288,7 +288,8 @@ module.exports = function (options, error) {
288
288
  });
289
289
  let json_Annotations = Object.create(null);
290
290
  this._annotations.filter(a => a.Target).forEach(a => json_Annotations[a.Target] = a.toJSON());
291
- json['$Annotations'] = json_Annotations;
291
+ if(Object.keys(json_Annotations).length)
292
+ json['$Annotations'] = json_Annotations;
292
293
  }
293
294
  edmUtils.forAll(this._actions, (actionArray, actionName) => {
294
295
  json[actionName] = [];
@@ -610,7 +611,12 @@ module.exports = function (options, error) {
610
611
  if(scalarType) {
611
612
  this[typeName] = csn._edmType;
612
613
  // CDXCORE-CDXCORE-173 ignore type facets for Edm.Stream
613
- if(this[typeName] !== 'Edm.Stream')
614
+ // cds-compiler/issues/7835: Only set length for Binary as long as it is
615
+ // unclear how many bytes a string character represents.
616
+ // We can't calculate an unambiguous byte stream length for DB dependent
617
+ // multi-byte characters.
618
+ if(!(this[typeName] === 'Edm.Stream' &&
619
+ ![ /*'cds.String',*/ 'cds.Binary'].includes(scalarType.type)))
614
620
  edmUtils.addTypeFacets(this, scalarType);
615
621
  }
616
622
  else {
@@ -996,9 +1002,11 @@ module.exports = function (options, error) {
996
1002
  // values are !null (with other words: a collection must never return [1,2,null,3])
997
1003
  delete this.Nullable;
998
1004
  }
999
- let partner = (csn._selfReferences.length > 0) ? csn._selfReferences[0] : csn._constraints._partnerCsn;
1005
+ // we have exactly one selfReference or the default partner
1006
+ let partner = (!csn.$noPartner && csn._selfReferences.length === 1) ? csn._selfReferences[0] : csn._constraints._partnerCsn;
1000
1007
  if(partner && partner['@odata.navigable'] !== false) {
1001
- this.Partner = partner.name;
1008
+ // $abspath[0] is main entity
1009
+ this.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
1002
1010
  }
1003
1011
 
1004
1012
  /*
@@ -1084,13 +1092,6 @@ module.exports = function (options, error) {
1084
1092
  return json;
1085
1093
  }
1086
1094
 
1087
- createNavigationPropertyBinding(namespace)
1088
- {
1089
- return new NavigationPropertyBinding(this._v,
1090
- { Path: this.Name, Target: this._csn._target.name.replace(namespace, '') }
1091
- );
1092
- }
1093
-
1094
1095
  // V4 referential constraints!
1095
1096
  addReferentialConstraintNodes()
1096
1097
  {
@@ -1486,17 +1487,13 @@ module.exports = function (options, error) {
1486
1487
  new End(v, { Role: fromRole[0], Type: fromRole[1], Multiplicity: multiplicity[0] } ),
1487
1488
  new End(v, { Role: toRole[0], Type: toRole[1], Multiplicity: multiplicity[1] } ) );
1488
1489
 
1489
- // if eventually a backlink is a composition and this (Forward-)Association has not yet been
1490
- // produced, add the OnDelete property now to the target end.
1491
- // undefined values are not appended
1492
- if(navProp._OnDelete)
1493
- this._end[1].append(navProp._OnDelete);
1490
+ // set Delete:Cascade on composition end
1491
+ if(navProp._csn.type === 'cds.Composition')
1492
+ this._end[0].append(new OnDelete(v, { Action: 'Cascade' }));
1494
1493
 
1495
- // if the NavProp is a composition, add the OnDelete to the source end
1496
- if(navProp._csn.type === 'cds.Composition') {
1497
- // TODO: to be specified via @sap.on.delete
1498
- this._end[0].append(new OnDelete(v, { Action: 'Cascade' } ) );
1499
- }
1494
+ if(navProp._csn._selfReferences && navProp._csn._selfReferences.length &&
1495
+ navProp._csn._selfReferences[0].type === 'cds.Composition')
1496
+ this._end[1].append(new OnDelete(v, { Action: 'Cascade' }));
1500
1497
  }
1501
1498
 
1502
1499
  innerXML(indent)