@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 +18 -0
- package/lib/base/message-registry.js +4 -0
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/validator.js +2 -1
- package/lib/edm/edm.js +5 -2
- package/lib/edm/edmPreprocessor.js +6 -2
- package/lib/edm/edmUtils.js +8 -8
- package/lib/language/genericAntlrParser.js +6 -0
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/forOdataNew.js +29 -30
- package/package.json +1 -1
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
|
+
};
|
package/lib/checks/validator.js
CHANGED
|
@@ -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(
|
|
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[
|
|
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
|
|
660
|
+
// jump over many items but not if this is an element
|
|
661
|
+
if (env.items) {
|
|
661
662
|
env = env.items;
|
|
662
|
-
|
|
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
|
}
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -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
|
-
|
|
770
|
-
if(elements)
|
|
771
|
-
env =
|
|
772
|
-
|
|
773
|
-
if(type && !isBuiltinType(type) && !(env
|
|
774
|
-
env = csn.definitions[
|
|
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
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
372
|
-
if (
|
|
373
|
-
node
|
|
374
|
-
setAnnotation(node, '@
|
|
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
|
-
|
|
378
|
-
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|