@sap/cds-compiler 3.9.2 → 3.9.6

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,25 @@
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
+
11
+ ## Version 3.9.6 - 2023-07-27
12
+
13
+ ### Fixed
14
+
15
+ - to.edm(x): Revert change introduced with [3.9.0](#version-390---2023-04-20)
16
+ "Correct referential constraint calculation for `[0..1]` backlink associations".
17
+ - for.odata: Process shortcut annotations sequence independent.
18
+
19
+ ## Version 3.9.4 - 2023-06-07
20
+
21
+ ### Fixed
22
+
23
+ - compiler: `USING` empty files were incorrectly marked as "not found".
24
+ - Localized convenience views for projections (not views) did not have references rewritten.
25
+ This only affects CSN, the SQL result was correct.
26
+ - to.edm(x): Render correct EntitySetPath and annotation target path for actions/functions
27
+ with explicit binding parameter.
28
+
10
29
  ## Version 3.9.2 - 2023-04-27
11
30
 
12
31
  ### Fixed
@@ -391,7 +391,7 @@ const centralMessageTexts = {
391
391
  'syntax-deprecated-value': { // Warning
392
392
  std: 'Deprecated representation of the value in property $(PROP)',
393
393
  replace: 'Replace value in $(PROP) by $(VALUE)',
394
- 'zero-parens': 'Deprecated CSN v.0.1.0 representation of expressions in parentheses',
394
+ 'zero-parens': 'Deprecated CSN v0.1.0 representation of expressions in parentheses',
395
395
  'zero-replace': 'Replace CSN v0.1.0 value in $(PROP) by $(VALUE)',
396
396
  },
397
397
  'syntax-deprecated-type-ref': {
@@ -416,9 +416,19 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
416
416
  function relParList() {
417
417
  // we rely on the order of params in the csn being the correct one
418
418
  const params = [];
419
- if (entityNameIfBound && !(cAction.kind === 'function' && cAction.$hasBindingParam)) {
420
- params.push(cAction['@cds.odata.bindingparameter.collection'] ? 'Collection(' + entityNameIfBound + ')' : entityNameIfBound);
419
+ if (entityNameIfBound) {
420
+ // If this is an action and has an explicit binding parameter add it here
421
+ if(cAction.$bindingParam && cAction.kind === 'action') {
422
+ params.push(cAction.$bindingParam.items ? 'Collection(' + entityNameIfBound + ')' : entityNameIfBound);
423
+ }
424
+ // If action/function has no explicit binding parameter add it here
425
+ else if (!cAction.$bindingParam) {
426
+ params.push(cAction['@cds.odata.bindingparameter.collection'] ? 'Collection(' + entityNameIfBound + ')' : entityNameIfBound);
427
+ }
421
428
  }
429
+ // In case this is a function the explicit binding parameter is part of
430
+ // the functions params dictionary. Only for functions all parameters must
431
+ // be listed in the annotation target
422
432
  if (cAction.kind === 'function') {
423
433
  if(cAction.params) {
424
434
  cAction.params && Object.values(cAction.params).forEach(p => {
@@ -687,14 +687,15 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
687
687
  The binding parameter remains in the CSN and is rendered as any other
688
688
  parameter (including default value/not null/ etc) and acts as annotation carrier.
689
689
  */
690
- setProp(actionCsn, '$hasBindingParam', false);
690
+
691
+ let bpName = 'in';
691
692
  if(actionCsn.params) {
692
693
  const entries = Object.entries(actionCsn.params);
693
694
  const firstParam = entries[0][1];
694
695
  const type = firstParam?.items?.type || firstParam?.type;
695
696
  if(type === special$self) {
696
- actionCsn.$hasBindingParam = true;
697
- const bpName = entries[0][0];
697
+ bpName = entries[0][0];
698
+ setProp(actionCsn, '$bindingParam', firstParam);
698
699
  if(bpType) {
699
700
  if(firstParam.items?.type)
700
701
  firstParam.items.type = bpType;
@@ -707,16 +708,15 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
707
708
  }
708
709
 
709
710
  // bpName is eventually used later for EntitySetPath
710
- const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
711
- let bpName = 'in';
712
- if(bpNameAnno != null) {
713
- if(typeof bpNameAnno === 'string')
714
- bpName = bpNameAnno;
715
- if(typeof bpNameAnno === 'object' && bpNameAnno['='])
716
- bpName = bpNameAnno['='];
717
- }
718
711
  // No explicit binding parameter, check (user defined) annotation value)
719
- if(!actionCsn.$hasBindingParam) {
712
+ if(!actionCsn.$bindingParam) {
713
+ const bpNameAnno = actionCsn['@cds.odata.bindingparameter.name'];
714
+ if(bpNameAnno != null) {
715
+ if(typeof bpNameAnno === 'string')
716
+ bpName = bpNameAnno;
717
+ if(typeof bpNameAnno === 'object' && bpNameAnno['='])
718
+ bpName = bpNameAnno['='];
719
+ }
720
720
  if(!edmUtils.isODataSimpleIdentifier(bpName))
721
721
  message('odata-spec-violation-id', [...loc, '@cds.odata.bindingparameter.name'], { id: bpName });
722
722
  if(actionCsn.params && actionCsn.params[bpName]) {
@@ -726,7 +726,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
726
726
  if(entityCsn != undefined)
727
727
  {
728
728
  actionNode.setEdmAttribute('IsBound', true);
729
- if(!actionCsn.$hasBindingParam) {
729
+ if(!actionCsn.$bindingParam) {
730
730
  // Binding Parameter: 'in' at first position in sequence, this is decisive!
731
731
  if(actionCsn['@cds.odata.bindingparameter.collection'])
732
732
  actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection:true/*, Nullable: false*/ } ));
package/lib/edm/edm.js CHANGED
@@ -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
  }
@@ -1881,10 +1881,10 @@ function refSplit( name, prop ) {
1881
1881
  return { path: path.map( id => ({ id, location: location() }) ), location: location() };
1882
1882
  }
1883
1883
 
1884
- function replaceZeroValue( spec, msgVariant, otherprop ) {
1884
+ function replaceZeroValue( spec, msgVariant, newValue ) {
1885
1885
  if (!csnVersionZero && !spec.vZeroFor) {
1886
1886
  warning( 'syntax-deprecated-value', location(true),
1887
- { '#': msgVariant, prop: spec.msgProp, otherprop } );
1887
+ { '#': msgVariant, prop: spec.msgProp, value: newValue } );
1888
1888
  }
1889
1889
  }
1890
1890
 
@@ -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
  }
@@ -346,44 +346,41 @@ 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
- }
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);
361
360
  }
361
+ }
362
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
- }
369
- }
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);
370
367
  }
368
+ }
371
369
  // Only on element level: translate @mandatory
372
- if (name === '@mandatory' && node[name] &&
370
+ if (node['@mandatory'] &&
373
371
  node.kind === undefined && node['@Common.FieldControl'] === undefined) {
374
- setAnnotation(node, '@Common.FieldControl', { '#': 'Mandatory' });
375
- }
372
+ setAnnotation(node, '@Common.FieldControl', { '#': 'Mandatory' });
373
+ }
376
374
 
377
- if (name === '@assert.format' && node[name] !== null)
378
- setAnnotation(node, '@Validation.Pattern', node['@assert.format']);
375
+ if (node['@assert.format'] != null)
376
+ setAnnotation(node, '@Validation.Pattern', node['@assert.format']);
379
377
 
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
- }
378
+ if (node['@assert.range'] != null) {
379
+ if (Array.isArray(node['@assert.range']) && node['@assert.range'].length === 2) {
380
+ setAnnotation(node, '@Validation.Minimum', node['@assert.range'][0]);
381
+ setAnnotation(node, '@Validation.Maximum', node['@assert.range'][1]);
385
382
  }
386
- });
383
+ }
387
384
  }
388
385
 
389
386
  // Apply default type facets to each type definition and every member
@@ -511,7 +511,7 @@ function _addLocalizationViews(csn, options, useJoins, config) {
511
511
  // For view convenience views (i.e. transitive views) we need to rewrite `from`
512
512
  // references as well as need to handle `mixin` elements.
513
513
  // a.k.a 'LOCALIZED-VERTICAL'
514
- forAllQueries(art.query || art.projection, (query) => {
514
+ forAllQueries(art.query || { SELECT: art.projection }, (query) => {
515
515
  query = query.SELECT || query.SET || query;
516
516
  if (query.from)
517
517
  rewriteFrom(query.from);
package/lib/utils/file.js CHANGED
@@ -140,12 +140,12 @@ function cdsFs( fileCache, enableTrace ) {
140
140
  traceFS( 'ISFILE:cache:', filename, body );
141
141
  if (body instanceof Error)
142
142
  cb( body ); // no need for process.nextTick( cb, body ) with moduleResolve
143
- else
144
- cb( null, !!body );
143
+ else // body could be empty string
144
+ cb( null, !!body || typeof body === 'string');
145
145
  }
146
146
  else {
147
147
  traceFS( 'ISFILE:start:', filename, body );
148
- // in the future (if we do module resolve ourself with just readFile),
148
+ // in the future (if we do module resolve ourselves with just readFile),
149
149
  // we avoid parallel readFile by storing having an array of `cb`s in
150
150
  // fileCache[ filename ] before starting fs.readFile().
151
151
  try {
@@ -213,7 +213,7 @@ function resolveCDS( moduleName, options, callback ) {
213
213
  options.realpath(resolvedBaseDir, (realPathErr, realPath) => {
214
214
  // There may be an error in resolving the symlink.
215
215
  // We ignore the error and simply use the original path.
216
- // Otherwise cds-lsp tests would fail because they don't have real
216
+ // Otherwise, cds-lsp tests would fail because they don't have real
217
217
  // files in their tests.
218
218
  if (!realPathErr)
219
219
  resolvedBaseDir = realPath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "3.9.2",
3
+ "version": "3.9.6",
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)",