@sap/cds-compiler 3.9.4 → 3.9.8

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 3.9.8 - 2023-08-03
11
+
12
+ ### Fixed
13
+
14
+ - to.edm(x):
15
+ + Don't expand `@mandatory` if element has an annotation with prefix `@Common.FieldControl.`.
16
+ + Fix a bug when referencing nested many structures, especially referring to a managed association via
17
+ `$self` comparison.
18
+ - to.sql/hdi/hdbcds: Detect navigation into arrayed structures and raise helpful errors instead of running into internal errors.
19
+
20
+ ## Version 3.9.6 - 2023-07-27
21
+
22
+ ### Fixed
23
+
24
+ - to.edm(x): Revert change introduced with [3.9.0](#version-390---2023-04-20)
25
+ "Correct referential constraint calculation for `[0..1]` backlink associations".
26
+ - for.odata: Process shortcut annotations sequence independent.
27
+
10
28
  ## Version 3.9.4 - 2023-06-07
11
29
 
12
30
  ### Fixed
@@ -116,6 +116,7 @@ const centralMessages = {
116
116
  'type-ambiguous-target': { severity: 'Warning' },
117
117
 
118
118
  'ref-autoexposed': { severity: 'Error', configurableFor: 'deprecated' },
119
+ 'ref-unexpected-many-navigation': { severity: 'Error' },
119
120
  // Published! Used in @sap/cds-lsp; if renamed, add to oldMessageIds and contact colleagues
120
121
  'ref-undefined-art': { severity: 'Error' },
121
122
  'ref-undefined-def': { severity: 'Error' },
@@ -521,6 +522,9 @@ const centralMessageTexts = {
521
522
  std: '$(ART) can\'t be extended because it originates from an include',
522
523
  elements: '$(ART) can\'t be extended by elements/enums because it originates from an include',
523
524
  },
525
+ 'ref-unexpected-many-navigation': {
526
+ std: 'Unexpected navigation into arrayed structure',
527
+ },
524
528
  'ref-unexpected-scope': {
525
529
  std: 'Unexpected parameter reference',
526
530
  calc: 'Calculated elements can\'t use parameter references',
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const { applyTransformationsOnNonDictionary } = require('../model/csnUtils');
4
+
5
+ /**
6
+ * Check all refs in the given parent for the traversal of paths
7
+ * into `.items`
8
+ *
9
+ * @param {object} parent Object with the expression as a property
10
+ * @param {string} propOnParent Name of the expression property on parent
11
+ * @param {Array} e Expression to check - see module.exports
12
+ * @param {CSN.Path} path
13
+ */
14
+ function navigationIntoMany( parent, propOnParent, e, path ) {
15
+ applyTransformationsOnNonDictionary(parent, propOnParent, {
16
+ ref: (_parent, _prop, ref, _path) => {
17
+ const itemNavigationIndex = _parent._links?.findIndex(l => l.art.items);
18
+ if (itemNavigationIndex !== -1 && _parent.ref.length > itemNavigationIndex + 1)
19
+ this.message('ref-unexpected-many-navigation', _path);
20
+ },
21
+ }, { skipStandard: { type: true } }, path);
22
+ }
23
+
24
+ module.exports = {
25
+ columns: navigationIntoMany,
26
+ from: navigationIntoMany,
27
+ on: navigationIntoMany,
28
+ having: navigationIntoMany,
29
+ groupBy: navigationIntoMany,
30
+ orderBy: navigationIntoMany,
31
+ where: navigationIntoMany,
32
+ xpr: navigationIntoMany,
33
+ };
@@ -11,6 +11,7 @@ const enrich = require('./enricher');
11
11
  const { validateSelectItems } = require('./selectItems');
12
12
  const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
13
13
  const validateCdsPersistenceAnnotation = require('./cdsPersistence');
14
+ const navigationIntoMany = require('./manyNavigations');
14
15
  const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
15
16
  const validateHasPersistedElements = require('./hasPersistedElements');
16
17
  const checkForHanaTypes = require('./checkForTypes');
@@ -71,7 +72,7 @@ const forRelationalDBArtifactValidators
71
72
  checkSqlAnnotationOnArtifact,
72
73
  ];
73
74
 
74
- const forRelationalDBCsnValidators = [ nonexpandableStructuredInExpression ];
75
+ const forRelationalDBCsnValidators = [ nonexpandableStructuredInExpression, navigationIntoMany ];
75
76
  /**
76
77
  * @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
77
78
  */
package/lib/edm/edm.js CHANGED
@@ -1118,7 +1118,7 @@ function getEdm(options, messageFunctions) {
1118
1118
  : undefined;
1119
1119
  if(partner && partner['@odata.navigable'] !== false && this._csn._edmParentCsn.kind !== 'type') {
1120
1120
  // $abspath[0] is main entity
1121
- this._edmAttributes.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
1121
+ this._edmAttributes.Partner = partner.$abspath.slice(1).join('/');
1122
1122
  }
1123
1123
 
1124
1124
  /*
@@ -1217,13 +1217,16 @@ function getEdm(options, messageFunctions) {
1217
1217
  // V4 referential constraints!
1218
1218
  addReferentialConstraintNodes()
1219
1219
  {
1220
+ // flip the constrains if this is a $self partner
1220
1221
  let _constraints = this._csn._constraints;
1222
+ let [i,j] = [0,1];
1221
1223
  if(this._csn._constraints._partnerCsn) {
1222
1224
  _constraints = this._csn._constraints._partnerCsn._constraints;
1225
+ [i,j] = [1,0];
1223
1226
  }
1224
1227
  _constraints.constraints && Object.values(_constraints.constraints).forEach(c =>
1225
1228
  this.append(new ReferentialConstraint(this._v,
1226
- { Property: c[0].join(options.pathDelimiter), ReferencedProperty: c[1].join(options.pathDelimiter) } ) )
1229
+ { Property: c[i].join(options.pathDelimiter), ReferencedProperty: c[j].join(options.pathDelimiter) } ) )
1227
1230
  );
1228
1231
  }
1229
1232
  }
@@ -657,9 +657,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
657
657
  env = env[ps];
658
658
  if (env && env.constructor === Object) {
659
659
  path.push(ps);
660
- if(env.items)
660
+ // jump over many items but not if this is an element
661
+ if (env.items) {
661
662
  env = env.items;
662
- if(env.type && !isBuiltinType(env.type) && !env.elements)
663
+ if (p[i + 1] === 'items')
664
+ i++;
665
+ }
666
+ if (env.type && !isBuiltinType(env.type) && !env.elements)
663
667
  env = csn.definitions[env.type];
664
668
  }
665
669
  }
@@ -764,14 +764,14 @@ function getBaseName(name) {
764
764
  }
765
765
 
766
766
  // This is a poor mans path resolver for $self partner paths only
767
- function resolveOriginAssoc(csn, env, path) {
768
- for(const segment of path) {
769
- let elements = (env.items && env.items.elements || env.elements);
770
- if(elements)
771
- env = env.elements[segment];
772
- let type = (env.items && env.items.type || env.type);
773
- if(type && !isBuiltinType(type) && !(env.items && env.items.elements || env.elements))
774
- env = csn.definitions[env.type];
767
+ function resolveOriginAssoc( csn, env, path ) {
768
+ for (const segment of path) {
769
+ const elements = (env?.items?.elements || env?.elements);
770
+ if (elements)
771
+ env = elements[segment];
772
+ const type = (env?.items?.type || env?.type);
773
+ if (type && !isBuiltinType(type) && !(env?.items?.elements || env?.elements))
774
+ env = csn.definitions[type];
775
775
  }
776
776
  return env;
777
777
  }
@@ -413,6 +413,12 @@ function checkExtensionDict( dict ) {
413
413
  def[prop] = dup[prop]; // continuation semantics: last wins
414
414
  }
415
415
  }
416
+ if (dup.$annotations) { // update deprecated $annotations for cds-lsp / annotation modeler
417
+ if (def.$annotations)
418
+ def.$annotations.push( ...dup.$annotations );
419
+ else
420
+ def.$annotations = dup.$annotations;
421
+ }
416
422
  }
417
423
  def.$duplicates = null;
418
424
  }
@@ -52,7 +52,7 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
52
52
  * The customTransformers are applied here (and only here).
53
53
  *
54
54
  * @param {object | Array} _parent the thing that has _prop
55
- * @param {string|number} _prop the name of the current property
55
+ * @param {string|number} _prop the name of the current property or index
56
56
  * @param {object} node The value of node[_prop]
57
57
  */
58
58
  function standard( _parent, _prop, node ) {
@@ -346,44 +346,43 @@ function transform4odataWithCsn(inputModel, options) {
346
346
  setAnnotation(node, name.replace(setPrefix, setMappings[setPrefix]), node[name]);
347
347
  }
348
348
  }
349
-
349
+ });
350
350
  // Special case: '@readonly' becomes a triplet of capability restrictions for entities,
351
351
  // but '@Core.Immutable' for everything else.
352
- if (!(node['@readonly'] && node['@insertonly'])) {
353
- if (name === '@readonly' && node[name]) {
354
- if (node.kind === 'entity' || node.kind === 'aspect') {
355
- setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
356
- setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
357
- setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
358
- } else {
359
- setAnnotation(node, '@Core.Computed', true);
360
- }
361
- }
362
- // @insertonly is effective on entities/queries only
363
- else if (name === '@insertonly' && node[name]) {
364
- if (node.kind === 'entity' || node.kind === 'aspect') {
365
- setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
366
- setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
367
- setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
368
- }
352
+ if (!(node['@readonly'] && node['@insertonly'])) {
353
+ if (node['@readonly']) {
354
+ if (node.kind === 'entity' || node.kind === 'aspect') {
355
+ setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
356
+ setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
357
+ setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
358
+ } else {
359
+ setAnnotation(node, '@Core.Computed', true);
369
360
  }
370
361
  }
371
- // Only on element level: translate @mandatory
372
- if (name === '@mandatory' && node[name] &&
373
- node.kind === undefined && node['@Common.FieldControl'] === undefined) {
374
- setAnnotation(node, '@Common.FieldControl', { '#': 'Mandatory' });
362
+ // @insertonly is effective on entities/queries only
363
+ if (node['@insertonly'] && (node.kind === 'entity' || node.kind === 'aspect')) {
364
+ setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
365
+ setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
366
+ setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
375
367
  }
368
+ }
376
369
 
377
- if (name === '@assert.format' && node[name] !== null)
378
- setAnnotation(node, '@Validation.Pattern', node['@assert.format']);
370
+ // Only on element level: translate @mandatory
371
+ if (node['@mandatory'] &&
372
+ node.kind === undefined &&
373
+ !Object.entries(node).some(([k,v]) => k === '@Common.FieldControl' || k.startsWith('@Common.FieldControl.') && v != null)) {
374
+ setAnnotation(node, '@Common.FieldControl', { '#': 'Mandatory' });
375
+ }
379
376
 
380
- if (name === '@assert.range' && node[name] !== null) {
381
- if (Array.isArray(node['@assert.range']) && node['@assert.range'].length === 2) {
382
- setAnnotation(node, '@Validation.Minimum', node['@assert.range'][0]);
383
- setAnnotation(node, '@Validation.Maximum', node['@assert.range'][1]);
384
- }
377
+ if (node['@assert.format'] != null)
378
+ setAnnotation(node, '@Validation.Pattern', node['@assert.format']);
379
+
380
+ if (node['@assert.range'] != null) {
381
+ if (Array.isArray(node['@assert.range']) && node['@assert.range'].length === 2) {
382
+ setAnnotation(node, '@Validation.Minimum', node['@assert.range'][0]);
383
+ setAnnotation(node, '@Validation.Maximum', node['@assert.range'][1]);
385
384
  }
386
- });
385
+ }
387
386
  }
388
387
 
389
388
  // Apply default type facets to each type definition and every member
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "3.9.4",
3
+ "version": "3.9.8",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",