@sap/cds-compiler 2.5.2 → 2.11.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 (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. 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;
@@ -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,16 +301,17 @@ 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
  }
307
+ /** @type {any} */
304
308
  const Schema = new Edm.Schema(v, schema.name, undefined /* unset alias */, schema._csn, /* annotations */ [], schema.container);
305
309
  const EntityContainer = Schema._ec || (LeadSchema && LeadSchema._ec);
306
310
  // now namespace and alias are used to create the fullQualified(name)
307
311
  const schemaNamePrefix = schema.name + '.'
308
312
  const schemaAliasPrefix = schemaNamePrefix;
309
313
  const schemaCsn = schema;
310
- let navigationProperties = [];
314
+ const navigationProperties = [];
311
315
 
312
316
  /* create the entitytypes and sets
313
317
  Do not create an entity set if:
@@ -337,7 +341,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
337
341
  }
338
342
 
339
343
  // fetch all exising children names in a map
340
- let NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
344
+ const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
341
345
  if(acc[cur.Name] === undefined) {
342
346
  acc[cur.Name] = [ cur ];
343
347
  } else {
@@ -359,8 +363,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
359
363
 
360
364
  // remove EntityContainer if empty
361
365
  if(Schema._ec && Schema._ec._children.length === 0) {
362
- let pos = Schema._children.indexOf(Schema._ec);
363
- Schema._children.splice(pos, 1);
366
+ Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
364
367
  }
365
368
  if(Schema._children.length === 0) {
366
369
  // FIXME: Location for sub schemas?
@@ -369,8 +372,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
369
372
 
370
373
  Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
371
374
  if(refs.length > 1) {
372
- let artifactName = `${Schema.Namespace}.${name}`;
373
- error(null, ['definitions', artifactName], { name: Schema.Namespace },
375
+ error(null, ['definitions', `${Schema.Namespace}.${name}`], { name: Schema.Namespace },
374
376
  'Duplicate name in Schema $(NAME)');
375
377
  }
376
378
  });
@@ -379,33 +381,33 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
379
381
 
380
382
  function createEntityTypeAndSet(entityCsn)
381
383
  {
382
- let EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
383
- let EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
384
+ const EntityTypeName = entityCsn.name.replace(schemaNamePrefix, '');
385
+ const EntitySetName = edmUtils.getBaseName(entityCsn.$entitySetName || entityCsn.name);
384
386
 
385
- let [ properties, hasStream ] = createProperties(entityCsn);
387
+ const [ properties, hasStream ] = createProperties(entityCsn);
386
388
 
387
389
  const loc = ['definitions', entityCsn.name];
388
390
  const type = `${schema.name}.${EntityTypeName}`;
389
391
  if(properties.length === 0) {
390
392
  warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no properties');
391
393
  } else if(entityCsn.$edmKeyPaths.length === 0) {
392
- warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no primary key');
394
+ message('odata-spec-violation-no-key', loc);
393
395
  }
394
396
  properties.forEach(p => {
395
397
  const pLoc = [...loc, 'elements', p.Name];
396
398
  if(!p[p._typeName]) {
397
- warning('odata-spec-violation-attribute', pLoc, { name: p.Name, prop: p._typeName });
399
+ message('odata-spec-violation-type', pLoc);
398
400
  }
399
401
  if(p.Name === EntityTypeName) {
400
- warning('odata-spec-violation-property-name', pLoc, { name: p.Name, type: 'EntityType' });
402
+ warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
401
403
  }
402
404
  if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
403
- warning('odata-spec-violation-array-of-v2', pLoc, { literal: 'Property', prop: 'Type', name: 'Collection' });
405
+ warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
404
406
  }
405
407
  });
406
408
 
407
409
  // construct EntityType attributes
408
- let attributes = { Name : EntityTypeName };
410
+ const attributes = { Name : EntityTypeName };
409
411
 
410
412
  // CDXCORE-CDXCORE-173
411
413
  if(options.isV2() && hasStream)
@@ -418,7 +420,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
418
420
  /** @type {object} */
419
421
  let containerEntry;
420
422
 
421
- if(isSingleton(entityCsn)) {
423
+ if(edmUtils.isSingleton(entityCsn, options.isV4())) {
422
424
  containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
423
425
  if(entityCsn['@odata.singleton.nullable'])
424
426
  containerEntry.Nullable= true;
@@ -428,18 +430,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
428
430
  }
429
431
 
430
432
  // 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
-
433
+ if(options.isV4()) {
434
+ entityCsn.$edmNPBs.forEach(npb => {
435
+ containerEntry.append(new Edm.NavigationPropertyBinding(v, npb))
436
+ });
437
+ }
443
438
  EntityContainer.register(containerEntry);
444
439
  }
445
440
 
@@ -448,38 +443,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
448
443
  (options.isV4()) ? createActionV4(a, n, entityCsn)
449
444
  : createActionV2(a, n, entityCsn)
450
445
  });
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
446
  }
458
447
 
459
448
  // add bound/unbound actions/functions for V4
460
449
  function createActionV4(actionCsn, name, entityCsn=undefined)
461
450
  {
462
- let iAmAnAction = actionCsn.kind === 'action';
451
+ const iAmAnAction = actionCsn.kind === 'action';
463
452
 
464
- let actionName = edmUtils.getBaseName(actionCsn.name);
453
+ const actionName = edmUtils.getBaseName(actionCsn.name);
465
454
 
466
- let attributes = { Name: actionName, IsBound : false };
455
+ const attributes = { Name: actionName, IsBound : false };
467
456
 
468
457
  if(!iAmAnAction)
469
458
  attributes.IsComposable = false;
470
459
 
471
460
  /** @type {object} */
472
- let actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
461
+ const actionNode = (iAmAnAction) ? new Edm.Action(v, attributes)
473
462
  : new Edm.FunctionDefinition(v, attributes);
474
463
 
475
464
  // bpName is eventually used later for EntitySetPath
476
- let bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
477
- let bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
465
+ const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
466
+ const bpName = bpNameAnno !== undefined ? (bpNameAnno['='] || bpNameAnno) : 'in';
478
467
 
479
468
  if(entityCsn != undefined)
480
469
  {
481
470
  actionNode.IsBound = true;
482
- let bpType = fullQualified(entityCsn.name);
471
+ const bpType = fullQualified(entityCsn.name);
483
472
  // Binding Parameter: 'in' at first position in sequence, this is decisive!
484
473
  if(actionCsn['@cds.odata.bindingparameter.collection'])
485
474
  actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true } ));
@@ -489,14 +478,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
489
478
  else if(EntityContainer)// unbound => produce Action/FunctionImport
490
479
  {
491
480
  /** @type {object} */
492
- let actionImport = iAmAnAction
481
+ const actionImport = iAmAnAction
493
482
  ? new Edm.ActionImport(v, { Name: actionName, Action : fullQualified(actionName) })
494
483
  : new Edm.FunctionImport(v, { Name: actionName, Function : fullQualified(actionName) });
495
484
 
496
- let rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
485
+ const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
497
486
  if(rt) // add EntitySet attribute only if return type is a non abstract entity
498
487
  {
499
- let definition = schemaCsn.definitions[rt];
488
+ const definition = schemaCsn.definitions[rt];
500
489
  if(definition && definition.kind === 'entity' && !definition.abstract)
501
490
  {
502
491
  actionImport.EntitySet = edmUtils.getBaseName(rt);
@@ -525,7 +514,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
525
514
  function createActionV2(actionCsn, name, entityCsn=undefined)
526
515
  {
527
516
  /** @type {object} */
528
- let functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
517
+ const functionImport = new Edm.FunctionImport(v, { Name: name.replace(schemaNamePrefix, '') } );
529
518
 
530
519
  // inserted now to maintain attribute order with old odata generator...
531
520
  /*
@@ -539,10 +528,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
539
528
  */
540
529
 
541
530
  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);
531
+ const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
543
532
  if(rt) // add EntitySet attribute only if return type is an entity
544
533
  {
545
- let defintion = schemaCsn.definitions[rt];
534
+ const defintion = schemaCsn.definitions[rt];
546
535
  if(defintion && edmUtils.isEntity(defintion))
547
536
  {
548
537
  functionImport.EntitySet = rt.replace(schemaNamePrefix, '');
@@ -586,10 +575,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
586
575
  const paramLoc = [...actLoc, 'params', parameterName];
587
576
  const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
588
577
  if(!param._type.startsWith('Edm.') && !edmUtils.isStructuredType(csn.definitions[param._type])) {
589
- warning('odata-spec-violation-param-v2', paramLoc);
578
+ warning('odata-spec-violation-param', paramLoc, { api: 'OData V2' });
590
579
  }
591
580
  if(param._isCollection) {
592
- warning('odata-spec-violation-array-of-v2', paramLoc, { literal: 'FunctionImport Parameter', prop: 'Type', name: 'Collection' });
581
+ warning('odata-spec-violation-array', paramLoc, { api: 'OData V2' });
593
582
  }
594
583
  functionImport.append(param);
595
584
  });
@@ -600,12 +589,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
600
589
  function getReturnType(action)
601
590
  {
602
591
  // it is safe to assume that either type or items.type are set
603
- let returns = action.returns.items || action.returns;
592
+ const returns = action.returns.items || action.returns;
604
593
  let type = returns.type;
605
594
  if(type){
606
595
  if(!isBuiltinType(type) && !['entity', 'view', 'type'].includes(csn.definitions[type].kind)){
607
596
  const returnsLoc = [ ...actLoc, 'returns'];
608
- warning('odata-spec-violation-returns-v2', returnsLoc);
597
+ warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
609
598
  }
610
599
  type = edmUtils.mapCdsToEdmType(returns, messageFunctions, options.isV2());
611
600
  }
@@ -618,19 +607,20 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
618
607
  }
619
608
 
620
609
  /**
621
- * @param {object} parentCsn
610
+ * @param {object} elementsCsn
611
+ * @param {object} edmParentCsn
622
612
  * @returns {[object[], boolean]} Returns a [ [ Edm Properties ], boolean hasStream ]:
623
613
  * array of Edm Properties
624
614
  * boolean hasStream : true if at least one element has @Core.MediaType assignment
625
615
  */
626
- function createProperties(parentCsn)
616
+ function createProperties(elementsCsn, edmParentCsn=elementsCsn)
627
617
  {
628
- let props = [];
618
+ const props = [];
629
619
  let hasStream = false;
630
- edmUtils.forAll(parentCsn.elements, (elementCsn, elementName) =>
620
+ edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
631
621
  {
632
- if(elementCsn._parent == undefined)
633
- setProp(elementCsn, '_parent', parentCsn);
622
+ if(elementCsn._edmParentCsn == undefined)
623
+ setProp(elementCsn, '_edmParentCsn', edmParentCsn);
634
624
 
635
625
  if(!elementCsn._ignore) {
636
626
  if(edmUtils.isAssociationOrComposition(elementCsn))
@@ -644,11 +634,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
644
634
  // (undefined !== false) still evaluates to true
645
635
  if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
646
636
  {
647
- let navProp = new Edm.NavigationProperty(v, {
637
+ const navProp = new Edm.NavigationProperty(v, {
648
638
  Name: elementName,
649
639
  Type: elementCsn._target.name
650
640
  }, elementCsn);
651
-
652
641
  props.push(navProp);
653
642
  // save the navProp in the global array for late constraint building
654
643
  navigationProperties.push(navProp);
@@ -667,7 +656,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
667
656
  // V2: don't render element but add attribute 'm:HasStream="true' to EntityType
668
657
  // V4: render property type 'Edm.Stream'
669
658
  hasStream = true;
670
- info(null, ['definitions', parentCsn.name], { name: parentCsn.name, id: elementName, anno: '@Core.MediaType' },
659
+ info(null, ['definitions', elementsCsn.name], { name: elementsCsn.name, id: elementName, anno: '@Core.MediaType' },
671
660
  '$(NAME): Property $(ID) annotated with $(ANNO) is removed from EDM in OData V2');
672
661
 
673
662
  } else {
@@ -683,11 +672,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
683
672
  function createComplexType(structuredTypeCsn)
684
673
  {
685
674
  // V4 attributes: Name, BaseType, Abstract, OpenType
686
- let attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
675
+ const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
687
676
 
688
- let complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
689
- let elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
690
- let properties = createProperties(elementsCsn)[0];
677
+ const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
678
+ const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
679
+ const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
691
680
  const loc = ['definitions', structuredTypeCsn.name];
692
681
 
693
682
  if(properties.length === 0) {
@@ -697,17 +686,17 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
697
686
  properties.forEach(p => {
698
687
  const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p.Name ];
699
688
  if(!p[p._typeName]) {
700
- warning('odata-spec-violation-attribute', pLoc, { name: p.Name, prop: p._typeName });
689
+ message('odata-spec-violation-type', pLoc);
701
690
  }
702
691
  if(p.Name === complexType.Name) {
703
- warning('odata-spec-violation-property-name', pLoc, { name: p.Name, type: complexType.kind });
692
+ warning('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
704
693
  }
705
694
  if(options.isV2()) {
706
695
  if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn)) {
707
- warning('odata-spec-violation-array-of-v2', pLoc, { literal: 'Property' ,prop: 'Type', name: 'Collection' });
696
+ warning('odata-spec-violation-array', pLoc, { api: 'OData V2' });
708
697
  }
709
698
  if(edmUtils.isAssociationOrComposition(p._csn)) {
710
- warning('odata-spec-violation-assoc-v2', pLoc, { name: structuredTypeCsn.name });
699
+ warning('odata-spec-violation-assoc', pLoc, { api: 'OData V2' });
711
700
  }
712
701
  }
713
702
  });
@@ -722,7 +711,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
722
711
  function createTypeDefinition(typeCsn)
723
712
  {
724
713
  // derived types are already resolved to base types
725
- let props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
714
+ const props = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
726
715
  const typeDef = new Edm.TypeDefinition(v, props, typeCsn );
727
716
  Schema.append(typeDef);
728
717
  }
@@ -744,7 +733,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
744
733
  function addAssociation(navigationProperty)
745
734
  {
746
735
  let constraints = navigationProperty._csn._constraints;
747
- let parentName = navigationProperty._csn._parent.name.replace(schemaNamePrefix, '');
736
+ let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
748
737
  let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
749
738
  let assocName = plainAssocName;
750
739
  let i = 1;
@@ -760,7 +749,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
760
749
 
761
750
  // The entity set name may not be the same as the type name (parameterized entities have
762
751
  // differing set names (<T>Parameters => <T>, <T>Type => <T>Set)
763
- let fromEntitySet = ( navigationProperty._csn._parent.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
752
+ let fromEntitySet = ( navigationProperty._csn._edmParentCsn.$entitySetName || fromEntityType).replace(schemaNamePrefix, '');
764
753
  let toEntitySet = (navigationProperty._targetCsn.$entitySetName || toEntityType).replace(schemaNamePrefix, '');
765
754
 
766
755
  // from and to roles must be distinguishable (in case of self association entity E { toE: association to E; ... })
@@ -802,7 +791,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
802
791
  [ fromEntityType, toEntityType ] = [ toEntityType, fromEntityType ];
803
792
  [ fromEntitySet, toEntitySet ] = [ toEntitySet, fromEntitySet ];
804
793
 
805
- parentName = forwardAssocCsn._parent.name.replace(schemaNamePrefix, '');
794
+ parentName = forwardAssocCsn._edmParentCsn.name.replace(schemaNamePrefix, '');
806
795
  assocName = plainAssocName = parentName + NAVPROP_TRENNER + forwardAssocCsn.name.replace(VALUELIST_NAVPROP_PREFIX, '');
807
796
  i = 1;
808
797
  while(NamesInSchemaXRef[assocName] !== undefined && !(NamesInSchemaXRef[assocName][0] instanceof Edm.Association)) {
@@ -817,56 +806,37 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
817
806
  }
818
807
 
819
808
  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
809
  return;
839
- }
840
810
 
841
811
  // Create Association and AssociationSet if this is not a backlink association.
842
812
  // Store association at navigation property because in case the Ends must be modified
843
813
  // later by the partner (backlink) association
844
- navigationProperty._edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
814
+ const edmAssociation = new Edm.Association(v, { Name: assocName }, navigationProperty,
845
815
  [ fromRole, fullQualified(fromEntityType) ],
846
816
  [ toRole, fullQualified(toEntityType) ],
847
817
  constraints._multiplicity );
848
818
  if(NamesInSchemaXRef[assocName] === undefined) {
849
- NamesInSchemaXRef[assocName] = [ navigationProperty._edmAssociation ];
819
+ NamesInSchemaXRef[assocName] = [ edmAssociation ];
850
820
  }
851
821
  else {
852
- navigationProperty._edmAssociation.push(navigationProperty._edmAssociation);
822
+ NamesInSchemaXRef[assocName].push(edmAssociation);
853
823
  }
854
824
  // Add ReferentialConstraints if any
855
825
  if(!navigationProperty._isCollection && Object.keys(constraints.constraints).length > 0) {
856
826
  // A managed composition is treated as association
857
827
  if(navigationProperty._csn.type === 'cds.Composition' && navigationProperty._csn.on) {
858
- navigationProperty._edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
828
+ edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
859
829
  toRole, fromRole, constraints.constraints));
860
830
  }
861
831
  else {
862
- navigationProperty._edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
832
+ edmAssociation.append(Edm.ReferentialConstraint.createV2(v,
863
833
  fromRole, toRole, constraints.constraints));
864
834
  }
865
835
  }
866
836
 
867
- Schema.append(navigationProperty._edmAssociation);
837
+ Schema.append(edmAssociation);
868
838
  if(EntityContainer && !navigationProperty._targetCsn.$proxy) {
869
- let assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
839
+ const assocSet = new Edm.AssociationSet(v, { Name: assocName, Association: fullQualified(assocName) },
870
840
  fromRole, toRole, fromEntitySet, toEntitySet);
871
841
  if(navigationProperty._csn._SetAttributes)
872
842
  assocSet.setSapVocabularyAsAttributes(navigationProperty._csn._SetAttributes);
@@ -880,6 +850,32 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
880
850
  return schemaAliasPrefix + name.replace(schemaNamePrefix, '')
881
851
  }
882
852
  }
853
+
854
+ // generate the Edm.Annotations tree and append it to the corresponding schema
855
+ function addAnnotations() {
856
+ let { annos, usedVocabularies } = translate.csn2annotationEdm(csn, serviceCsn.name, Edm, options, messageFunctions);
857
+ // distribute edm:Annotations into the schemas
858
+ // Distribute each anno into Schema
859
+ annos.forEach(anno => {
860
+ let targetSchema = whatsMySchemaName(anno.Target);
861
+ // if no target schema has been found, it's a service annotation that applies to the service schema
862
+ if(targetSchema === undefined)
863
+ targetSchema = serviceCsn.name;
864
+ if(targetSchema) {
865
+ if(targetSchema !== serviceCsn.name) {
866
+ anno.Target = anno.Target.replace(serviceCsn.name + '.', '');
867
+ }
868
+ edm._service._schemas[targetSchema]._annotations.push(anno);
869
+ }
870
+ });
871
+ annos = [];
872
+ // add references for the used vocabularies
873
+ usedVocabularies.forEach(voc => {
874
+ let r = new Edm.Reference(v, voc.ref);
875
+ r.append(new Edm.Include(v, voc.inc))
876
+ edm._defaultRefs.push(r);
877
+ })
878
+ }
883
879
  }
884
880
  }
885
881
  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)