@sap/cds-compiler 4.7.4 → 4.8.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.
- package/CHANGELOG.md +47 -2
- package/bin/cdsc.js +15 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +5 -1
- package/lib/api/main.js +61 -23
- package/lib/api/options.js +40 -0
- package/lib/base/builtins.js +89 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +50 -33
- package/lib/base/messages.js +71 -16
- package/lib/base/model.js +0 -2
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +6 -22
- package/lib/compiler/assert-consistency.js +3 -5
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +61 -11
- package/lib/compiler/define.js +3 -3
- package/lib/compiler/extend.js +2 -2
- package/lib/compiler/index.js +9 -9
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +3 -0
- package/lib/compiler/resolve.js +6 -20
- package/lib/compiler/shared.js +1 -1
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +113 -55
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +3 -3
- package/lib/edm/edmInboundChecks.js +24 -5
- package/lib/edm/edmPreprocessor.js +46 -20
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +9 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1941 -1850
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +8 -7
- package/lib/json/to-csn.js +12 -7
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +9 -10
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/main.js +8 -1
- package/lib/model/cloneCsn.js +15 -6
- package/lib/model/csnRefs.js +141 -35
- package/lib/model/csnUtils.js +1 -4
- package/lib/model/enrichCsn.js +1 -1
- package/lib/modelCompare/compare.js +106 -92
- package/lib/optionProcessor.js +23 -1
- package/lib/render/toCdl.js +3 -2
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/draft/odata.js +16 -17
- package/lib/transform/effective/main.js +6 -3
- package/lib/transform/effective/misc.js +18 -8
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +8 -7
- package/lib/transform/forRelationalDB.js +103 -112
- package/lib/transform/odata/flattening.js +82 -44
- package/lib/transform/odata/toFinalBaseType.js +9 -25
- package/lib/transform/odata/typesExposure.js +28 -15
- package/lib/transform/parseExpr.js +0 -3
- package/lib/transform/transformUtils.js +12 -8
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
EdmTypeFacetNames,
|
|
8
8
|
EdmPrimitiveTypeMap,
|
|
9
9
|
} = require('../EdmPrimitiveTypeDefinitions.js');
|
|
10
|
-
const { isBuiltinType, isAnnotationExpression } = require('../../
|
|
10
|
+
const { isBuiltinType, isAnnotationExpression } = require('../../base/builtins');
|
|
11
11
|
const { transformExpression } = require('../../transform/db/applyTransformations.js');
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -270,7 +270,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
270
270
|
evalArgs({ exact: 2 }, xpr, prop);
|
|
271
271
|
transformExpression(xpr, undefined, transform);
|
|
272
272
|
delete parent[prop];
|
|
273
|
-
parentparent[parentprop].$Apply =
|
|
273
|
+
parentparent[parentprop].$Apply = xpr;
|
|
274
|
+
parentparent[parentprop].$Function = 'odata.concat';
|
|
274
275
|
};
|
|
275
276
|
//----------------------------------
|
|
276
277
|
// ARITHMETICAL AND UNARY
|
|
@@ -305,8 +306,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
305
306
|
};
|
|
306
307
|
transform.ref = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
|
|
307
308
|
if (xpr.some(ps => ps.args || ps.where)) {
|
|
308
|
-
error('odata-anno-xpr-
|
|
309
|
-
anno, elemref: parent, '#': '
|
|
309
|
+
error('odata-anno-xpr-ref', location, {
|
|
310
|
+
anno, elemref: parent, '#': 'args',
|
|
310
311
|
});
|
|
311
312
|
}
|
|
312
313
|
const [ head, ...tail ] = xpr;
|
|
@@ -742,7 +743,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
742
743
|
}
|
|
743
744
|
else {
|
|
744
745
|
evalArgs(argDef, parent.args, xpr);
|
|
745
|
-
parentparent[parentprop].$Apply = [
|
|
746
|
+
parentparent[parentprop].$Apply = [ ...(parent.args || []) ];
|
|
747
|
+
parentparent[parentprop].$Function = funcName;
|
|
746
748
|
delete parentparent[parentprop].func;
|
|
747
749
|
delete parentparent[parentprop].args;
|
|
748
750
|
transformExpression(parentparent, parentprop, transform);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isEdmPropertyRendered,
|
|
3
|
+
const { isEdmPropertyRendered, transformExpression } = require('../../model/csnUtils');
|
|
4
|
+
const { isBuiltinType } = require('../../base/builtins');
|
|
4
5
|
const edmUtils = require('../edmUtils.js');
|
|
5
6
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
6
7
|
const preprocessAnnotations = require('./preprocessAnnotations.js');
|
|
7
8
|
const { forEachDefinition } = require('../../model/csnUtils');
|
|
8
|
-
// const { csnRefs } = require('../../model/csnRefs');
|
|
9
9
|
const { isBetaEnabled, setProp } = require('../../base/model.js');
|
|
10
10
|
const { xpr2edmJson, getEdmJsonHandler } = require('./edmJson.js');
|
|
11
11
|
const { vocabularyDefinitions } = require('./vocabularyDefinitions.js');
|
|
@@ -18,7 +18,7 @@ const { EdmPathTypeMap } = require('../EdmPrimitiveTypeDefinitions.js');
|
|
|
18
18
|
* v - array with two boolean entries, first is for v2, second is for v4
|
|
19
19
|
* dictReplacement: for test purposes, replaces the standard oDataDictionary
|
|
20
20
|
*/
|
|
21
|
-
function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
21
|
+
function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
22
22
|
Edm, options, messageFunctions, mergedVocDefs = vocabularyDefinitions ) {
|
|
23
23
|
const gAnnosArray = []; // global variable where we store all the generated annotations
|
|
24
24
|
const usedExperimentalTerms = {}; // take note of all experimental annos that have been used
|
|
@@ -27,7 +27,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
27
27
|
const { v } = options;
|
|
28
28
|
const { message, error } = messageFunctions;
|
|
29
29
|
const { handleEdmJson } = getEdmJsonHandler(Edm, options, messageFunctions, handleTerm);
|
|
30
|
-
// const { inspectRef } = csnRefs(reqDefs);
|
|
31
30
|
|
|
32
31
|
const [ userDefinedTermDict, allKnownVocabularies ] = createUserDefinedTermDictionary();
|
|
33
32
|
|
|
@@ -57,8 +56,10 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
57
56
|
{ name: serviceName, '#': 'service' } );
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
forEachDefinition(reqDefs, (def, defName) => {
|
|
60
|
+
if (defName.startsWith(`${serviceName}.`))
|
|
61
|
+
assignParameterAnnotations(def);
|
|
62
|
+
});
|
|
62
63
|
// Crawl over the csn and trigger the annotation translation for all kinds
|
|
63
64
|
// of annotated things.
|
|
64
65
|
// Note: only works for single service
|
|
@@ -96,15 +97,23 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
96
97
|
return v && v[0];
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
function assignParameterAnnotations() {
|
|
100
|
+
function assignParameterAnnotations( def ) {
|
|
100
101
|
// Copy annotations from origin to parameter entity if it's
|
|
101
102
|
// qualified with #$parameters or if its applicable to an EntitySet or Singleton
|
|
102
103
|
const scopeCheck = {
|
|
103
104
|
ref: (elemref, prop, xpr, path) => {
|
|
104
|
-
if (scopeCheck.scope === 'param' && (!elemref.param || (xpr[0].id || xpr[0]) === '$self'))
|
|
105
|
-
error('odata-anno-xpr-ref', path, {
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
if (scopeCheck.scope === 'param' && (!elemref.param || (xpr[0].id || xpr[0]) === '$self')) {
|
|
106
|
+
error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaparam' });
|
|
107
|
+
// don't try to resolve those paths later on
|
|
108
|
+
delete elemref[prop];
|
|
109
|
+
}
|
|
110
|
+
if (scopeCheck.scope === 'type' && elemref.param) {
|
|
111
|
+
error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaneelement' });
|
|
112
|
+
delete elemref[prop];
|
|
113
|
+
}
|
|
114
|
+
if (scopeCheck.scope === 'param' && elemref.param)
|
|
115
|
+
// make sure that path is resolvable as element path later on
|
|
116
|
+
delete elemref.param;
|
|
108
117
|
},
|
|
109
118
|
};
|
|
110
119
|
const checkDict = (dict, scope) => {
|
|
@@ -119,52 +128,49 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
119
128
|
});
|
|
120
129
|
}
|
|
121
130
|
};
|
|
122
|
-
const
|
|
131
|
+
const checkObj = (obj, scope) => {
|
|
123
132
|
scopeCheck.scope = scope;
|
|
124
|
-
const knownAnnos = filterKnownAnnotations(
|
|
133
|
+
const knownAnnos = filterKnownAnnotations(obj);
|
|
125
134
|
knownAnnos.forEach((pn) => {
|
|
126
135
|
scopeCheck.anno = pn;
|
|
127
|
-
transformExpression(
|
|
136
|
+
transformExpression(obj, pn, scopeCheck, obj.$path);
|
|
128
137
|
});
|
|
129
138
|
};
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (paramAnnoParts.length > 1)
|
|
160
|
-
delete object._origin[attr];
|
|
161
|
-
}
|
|
139
|
+
if (def.$isParamEntity && def._origin) {
|
|
140
|
+
// check for correct paths
|
|
141
|
+
if (def._origin.$paramsAnnoProxies) {
|
|
142
|
+
def.$elementsAnnoProxies = def._origin.$paramsAnnoProxies;
|
|
143
|
+
checkDict(def.$elementsAnnoProxies, 'param');
|
|
144
|
+
}
|
|
145
|
+
checkDict(def._origin.$elementsAnnoProxies, 'type');
|
|
146
|
+
checkDict(def.elements, 'param');
|
|
147
|
+
checkDict(def._origin.elements, 'type');
|
|
148
|
+
|
|
149
|
+
scopeCheck.scope = 'param';
|
|
150
|
+
Object.keys(def._origin).forEach((attr) => {
|
|
151
|
+
if (attr[0] === '@') {
|
|
152
|
+
scopeCheck.anno = attr;
|
|
153
|
+
const [ prefix, innerAnnotation ] = attr.split('.@');
|
|
154
|
+
const ns = whatsMyTermNamespace(prefix);
|
|
155
|
+
if (ns) {
|
|
156
|
+
const steps = prefix.replace(`@${ns}.`, '').split('.');
|
|
157
|
+
const paramAnnoParts = steps[0].split('#$parameters');
|
|
158
|
+
const dictTerm = getDictTerm(`${ns}.${paramAnnoParts[0]}`, options);
|
|
159
|
+
if (paramAnnoParts.length > 1 || [ 'Singleton', 'EntitySet' ].some(y => dictTerm?.AppliesTo?.includes(y))) {
|
|
160
|
+
steps[0] = `@${ns}.${paramAnnoParts.join('')}`;
|
|
161
|
+
let newAnno = steps.join('.');
|
|
162
|
+
if (innerAnnotation)
|
|
163
|
+
newAnno += `.@${innerAnnotation}`;
|
|
164
|
+
edmUtils.assignAnnotation(def, newAnno, def._origin[attr]);
|
|
165
|
+
transformExpression(def._origin, attr, scopeCheck, def._origin.$path);
|
|
166
|
+
if (paramAnnoParts.length > 1)
|
|
167
|
+
delete def._origin[attr];
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
checkObj(def._origin, 'type');
|
|
173
|
+
}
|
|
168
174
|
}
|
|
169
175
|
|
|
170
176
|
/*
|
|
@@ -208,10 +214,10 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
208
214
|
// definition bound annotations
|
|
209
215
|
handleAnnotations(defName, def, location);
|
|
210
216
|
// definition bound element annotations
|
|
211
|
-
if (def.$
|
|
212
|
-
Object.entries(def.$
|
|
217
|
+
if (def.$elementsAnnoProxies) {
|
|
218
|
+
Object.entries(def.$elementsAnnoProxies).forEach(([ elemPath, element ]) => {
|
|
213
219
|
const edmTargetName = `${defName}/${elemPath}`;
|
|
214
|
-
handleAnnotations(edmTargetName, element, element.$path);
|
|
220
|
+
handleAnnotations(edmTargetName, element, element.$path, [ ...location, '$elementsAnnoProxies', elemPath ]);
|
|
215
221
|
});
|
|
216
222
|
}
|
|
217
223
|
// element bound annotations
|
|
@@ -269,12 +275,24 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
269
275
|
handleAnnotations(actionName, cAction, location);
|
|
270
276
|
|
|
271
277
|
if (cAction.params) {
|
|
278
|
+
if (cAction.$paramsAnnoProxies) {
|
|
279
|
+
Object.entries(cAction.$paramsAnnoProxies).forEach(([ paramPath, param ]) => {
|
|
280
|
+
const edmTargetName = `${actionName}/${paramPath}`;
|
|
281
|
+
handleAnnotations(edmTargetName, param, param.$path, [ ...location, '$paramsAnnoProxies', paramPath ]);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
272
284
|
Object.entries(cAction.params).forEach(([ n, p ]) => {
|
|
273
285
|
const edmTargetName = `${actionName}/${n}`;
|
|
274
286
|
handleAnnotations(edmTargetName, p, [ ...location, 'params', n ]);
|
|
275
287
|
});
|
|
276
288
|
}
|
|
277
289
|
if (cAction.returns) {
|
|
290
|
+
if (cAction.$returnsAnnoProxies) {
|
|
291
|
+
Object.entries(cAction.$returnsAnnoProxies).forEach(([ returnsPath, returns ]) => {
|
|
292
|
+
const edmTargetName = `${actionName}/${returnsPath}`;
|
|
293
|
+
handleAnnotations(edmTargetName, returns, returns.$path, [ ...location, '$returnsAnnoProxies', returnsPath ]);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
278
296
|
const edmTargetName = `${actionName}/$ReturnType`;
|
|
279
297
|
setProp(cAction.returns, '$appliesToReturnType', true);
|
|
280
298
|
handleAnnotations(edmTargetName, cAction.returns, [ ...location, 'returns' ]);
|
|
@@ -324,7 +342,7 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
324
342
|
// edmTargetName : string, name of the target in edm
|
|
325
343
|
// carrier: object, the annotated cds thing, contains all the annotations
|
|
326
344
|
// as properties with names starting with @
|
|
327
|
-
function handleAnnotations( edmTargetName, carrier, location ) {
|
|
345
|
+
function handleAnnotations( edmTargetName, carrier, location, csnPathResolutionLocation = location ) {
|
|
328
346
|
// collect the names of the carrier's annotation properties
|
|
329
347
|
// keep only those annotations that - start with a known vocabulary name
|
|
330
348
|
// - have a value other than null
|
|
@@ -353,8 +371,46 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
353
371
|
}
|
|
354
372
|
if (isBetaEnabled(options, 'odataAnnotationExpressions')) {
|
|
355
373
|
knownAnnos.forEach((knownAnno) => {
|
|
356
|
-
if (knownAnno.search(/\.\$edmJson\./g) < 0)
|
|
374
|
+
if (knownAnno.search(/\.\$edmJson\./g) < 0) {
|
|
375
|
+
if (isBetaEnabled(options, 'odataPathsInAnnotationExpressions')) {
|
|
376
|
+
transformExpression(carrier, knownAnno, {
|
|
377
|
+
ref: (elemref, prop, xpr, csnPath) => {
|
|
378
|
+
const { links, scope } = reqDefsUtils.inspectRef(csnPath);
|
|
379
|
+
let i = scope === '$self' ? 1 : 0;
|
|
380
|
+
const getProp = propName => (links[i].art ? (links[i].art[propName] || reqDefsUtils.effectiveType(links[i].art)[propName]) : undefined);
|
|
381
|
+
if (scope === '$magic')
|
|
382
|
+
return;
|
|
383
|
+
let stop = false;
|
|
384
|
+
for (; i < links.length && !stop; i++) {
|
|
385
|
+
const cardinality = getProp('cardinality');
|
|
386
|
+
if ((cardinality && cardinality.max !== 1) || getProp('items')) {
|
|
387
|
+
error('odata-anno-xpr-ref', location, {
|
|
388
|
+
count: i + 1, elemref, anno: knownAnno, '#': 'tomany',
|
|
389
|
+
});
|
|
390
|
+
stop = true;
|
|
391
|
+
}
|
|
392
|
+
if (!isEdmPropertyRendered(links[i].art, csnPath)) {
|
|
393
|
+
error('odata-anno-xpr-ref', location, {
|
|
394
|
+
count: i + 1, elemref, anno: knownAnno, '#': 'notrendered',
|
|
395
|
+
});
|
|
396
|
+
stop = true;
|
|
397
|
+
}
|
|
398
|
+
if (links[i].art?._target?.$proxy && i < links.length - 1) {
|
|
399
|
+
const proxy = links[i].art?._target;
|
|
400
|
+
const eltName = links[i + 1].art?.name;
|
|
401
|
+
if (!proxy.elements[eltName]) {
|
|
402
|
+
error('odata-anno-xpr-ref', location, {
|
|
403
|
+
count: i + 2, elemref, anno: knownAnno, '#': 'notrendered',
|
|
404
|
+
});
|
|
405
|
+
stop = true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
}, csnPathResolutionLocation);
|
|
411
|
+
}
|
|
357
412
|
xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
|
|
413
|
+
}
|
|
358
414
|
});
|
|
359
415
|
}
|
|
360
416
|
|
|
@@ -1358,6 +1414,8 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
1358
1414
|
dict.xrefs[myServiceRoot] = { $myServiceRoot: myServiceRoot, used: false };
|
|
1359
1415
|
const edmType = new Edm.TypeBase(options.v, {}, annoDef);
|
|
1360
1416
|
dictDef = edmType._edmAttributes;
|
|
1417
|
+
if (dictDef.Type?.startsWith('Edm.Int'))
|
|
1418
|
+
dictDef.Type = 'Edm.Int';
|
|
1361
1419
|
dictDef.$myServiceRoot = myServiceRoot;
|
|
1362
1420
|
let val = annoDef['@odata.term.AppliesTo'];
|
|
1363
1421
|
if (val != null)
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -11,8 +11,9 @@ const { initializeModel } = require('./edmPreprocessor.js');
|
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
12
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
13
13
|
const {
|
|
14
|
-
isEdmPropertyRendered,
|
|
14
|
+
isEdmPropertyRendered, getUtils, findAnnotationExpression,
|
|
15
15
|
} = require('../model/csnUtils');
|
|
16
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
16
17
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
17
18
|
const {
|
|
18
19
|
EdmTypeFacetMap,
|
|
@@ -86,7 +87,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
86
87
|
return rc;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
// refresh csn cache after preprocessor model augmentation with parameters, types, proxies etc
|
|
91
|
+
const csnUtils = getUtils(csn);
|
|
90
92
|
|
|
91
93
|
if (serviceNames === undefined)
|
|
92
94
|
serviceNames = options.serviceNames;
|
|
@@ -744,17 +746,31 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
744
746
|
// bpName is eventually used later for EntitySetPath
|
|
745
747
|
// No explicit binding parameter, check (user defined) annotation value)
|
|
746
748
|
if (!actionCsn.$bindingParam) {
|
|
747
|
-
const
|
|
749
|
+
const bpnAnnoName = '@cds.odata.bindingparameter.name';
|
|
750
|
+
const bpnAnnoLoc = [ ...location, bpnAnnoName ];
|
|
751
|
+
const bpNameAnno = actionCsn[bpnAnnoName];
|
|
748
752
|
if (bpNameAnno != null) {
|
|
749
753
|
if (typeof bpNameAnno === 'string')
|
|
750
754
|
bpName = bpNameAnno;
|
|
751
|
-
if (typeof bpNameAnno === 'object' && bpNameAnno['='])
|
|
752
|
-
|
|
755
|
+
if (typeof bpNameAnno === 'object' && bpNameAnno['=']) {
|
|
756
|
+
if (findAnnotationExpression(actionCsn, bpnAnnoName)) {
|
|
757
|
+
if (!bpNameAnno.ref)
|
|
758
|
+
message('odata-anno-xpr', bpnAnnoLoc, { anno: bpnAnnoName, '#': 'unexpected' });
|
|
759
|
+
else if (bpNameAnno.ref.length !== 1)
|
|
760
|
+
error('odata-anno-xpr-ref', bpnAnnoLoc, { anno: bpnAnnoName, elemref: bpNameAnno, '#': 'invalid' });
|
|
761
|
+
else
|
|
762
|
+
bpName = bpNameAnno.ref[0];
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
bpName = bpNameAnno['='];
|
|
766
|
+
}
|
|
767
|
+
}
|
|
753
768
|
}
|
|
769
|
+
|
|
754
770
|
if (!edmUtils.isODataSimpleIdentifier(bpName))
|
|
755
|
-
message('odata-spec-violation-id',
|
|
771
|
+
message('odata-spec-violation-id', bpnAnnoLoc, { id: bpName });
|
|
756
772
|
if (actionCsn.params && actionCsn.params[bpName])
|
|
757
|
-
error('duplicate-definition',
|
|
773
|
+
error('duplicate-definition', bpnAnnoLoc, { '#': 'param', name: bpName });
|
|
758
774
|
}
|
|
759
775
|
if (entityCsn) {
|
|
760
776
|
actionNode.setEdmAttribute('IsBound', true);
|
|
@@ -1072,8 +1088,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
|
|
|
1072
1088
|
|
|
1073
1089
|
// generate the Edm.Annotations tree and append it to the corresponding schema
|
|
1074
1090
|
function addAnnotations2XServiceRefs( ) {
|
|
1075
|
-
options.getFinalTypeInfo =
|
|
1076
|
-
const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
|
|
1091
|
+
options.getFinalTypeInfo = csnUtils.getFinalTypeInfo;
|
|
1092
|
+
const { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csnUtils, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions, mergedVocabularies);
|
|
1077
1093
|
// distribute edm:Annotations into the schemas
|
|
1078
1094
|
// Distribute each anno into Schema
|
|
1079
1095
|
annos.forEach((anno) => {
|
package/lib/edm/edm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const edmUtils = require('./edmUtils
|
|
4
|
-
const { isBuiltinType } = require('../
|
|
3
|
+
const edmUtils = require('./edmUtils');
|
|
4
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
5
5
|
const { forEach } = require('../utils/objectUtils');
|
|
6
6
|
const {
|
|
7
7
|
EdmTypeFacetMap,
|
|
@@ -782,7 +782,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
782
782
|
super(version, attributes, csn);
|
|
783
783
|
const appliesTo = csn['@odata.term.AppliesTo'];
|
|
784
784
|
if (appliesTo)
|
|
785
|
-
this.setEdmAttribute('AppliesTo', Array.isArray(appliesTo) ? appliesTo.
|
|
785
|
+
this.setEdmAttribute('AppliesTo', Array.isArray(appliesTo) ? appliesTo.join(' ') : appliesTo);
|
|
786
786
|
}
|
|
787
787
|
}
|
|
788
788
|
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
forEachDefinition, forEachMemberRecursively,
|
|
5
|
+
forEachDefinition, forEachMemberRecursively, getUtils,
|
|
6
6
|
} = require('../model/csnUtils');
|
|
7
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
7
8
|
const { assignAnnotation } = require('./edmUtils.js');
|
|
8
9
|
|
|
9
10
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -49,6 +50,15 @@ function inboundQualificationChecks( csn, options, messageFunctions,
|
|
|
49
50
|
// check items.items
|
|
50
51
|
checkIfItemsOfItems(def, undefined, undefined, location);
|
|
51
52
|
forEachMemberRecursively(def, checkIfItemsOfItems, location);
|
|
53
|
+
checkIfItemsOfItems(def.returns, undefined, undefined, location.concat('returns'));
|
|
54
|
+
if (def.actions) {
|
|
55
|
+
Object.entries(def.actions).forEach(([ n, action ]) => {
|
|
56
|
+
const aLoc = location.concat('actions', n);
|
|
57
|
+
checkIfItemsOfItems(action, undefined, undefined, aLoc);
|
|
58
|
+
forEachMemberRecursively(action, checkIfItemsOfItems, aLoc);
|
|
59
|
+
checkIfItemsOfItems(action.returns, undefined, undefined, aLoc.concat('returns'));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
52
62
|
|
|
53
63
|
// decorate UUID keys with @Core.ComputedDefaultValue and complain
|
|
54
64
|
// on named type UUID elements that have no such annotation
|
|
@@ -88,14 +98,23 @@ function inboundQualificationChecks( csn, options, messageFunctions,
|
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
function checkIfItemsOfItems( member, _memberName, _prop, path ) {
|
|
101
|
+
if (!member)
|
|
102
|
+
return;
|
|
91
103
|
const memberType = csnUtils.effectiveType(member);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
let { items } = memberType;
|
|
105
|
+
if (items) {
|
|
106
|
+
if (items.target) {
|
|
107
|
+
const isComp = items.type === 'cds.Composition';
|
|
95
108
|
message('type-invalid-items', path, { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
96
109
|
return;
|
|
97
110
|
}
|
|
98
|
-
|
|
111
|
+
let i = 1;
|
|
112
|
+
while (items) {
|
|
113
|
+
items = items.items;
|
|
114
|
+
if (items)
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
if (i > 1) {
|
|
99
118
|
message('chained-array-of', path);
|
|
100
119
|
return;
|
|
101
120
|
}
|
|
@@ -5,9 +5,10 @@ const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model')
|
|
|
5
5
|
const {
|
|
6
6
|
forEachDefinition, forEachGeneric, forEachMemberRecursively,
|
|
7
7
|
isEdmPropertyRendered, getUtils,
|
|
8
|
-
|
|
9
|
-
cardinality2str,
|
|
8
|
+
applyTransformations, transformExpression, findAnnotationExpression,
|
|
9
|
+
cardinality2str,
|
|
10
10
|
} = require('../model/csnUtils');
|
|
11
|
+
const { isBuiltinType, isMagicVariable } = require('../base/builtins');
|
|
11
12
|
const edmUtils = require('./edmUtils.js');
|
|
12
13
|
const edmAnnoPreproc = require('./edmAnnoPreprocessor.js');
|
|
13
14
|
const { inboundQualificationChecks } = require('./edmInboundChecks.js');
|
|
@@ -540,7 +541,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
540
541
|
// preserve the original target for constraint calculation
|
|
541
542
|
setProp(entityCsn.$sources[n], '_originalTarget', entityCsn.$sources[n]._target);
|
|
542
543
|
entityCsn.$sources[n]._target = parameterCsn;
|
|
543
|
-
entityCsn.$sources[n].target = parameterCsn.name;
|
|
544
544
|
});
|
|
545
545
|
}
|
|
546
546
|
|
|
@@ -1905,6 +1905,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1905
1905
|
markCollection(member.returns);
|
|
1906
1906
|
mapCdsToEdmProp(member.returns);
|
|
1907
1907
|
annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
|
|
1908
|
+
rewriteAnnotationExpressions(member.returns);
|
|
1908
1909
|
}
|
|
1909
1910
|
}, defLocation);
|
|
1910
1911
|
// mark members that need to be rendered as collections
|
|
@@ -2038,11 +2039,12 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2038
2039
|
|
|
2039
2040
|
if (edmUtils.isNavigable(assoc) && isMyServiceRequested(containee.name) || containee.$proxy) {
|
|
2040
2041
|
const localAssocPath = path.join('.');
|
|
2041
|
-
|
|
2042
|
+
const laprefix = prefix.concat(localAssocPath).join('.');
|
|
2043
|
+
let navPropEntry = localRestrictions.find(p => p.NavigationProperty && p.NavigationProperty['='] === laprefix);
|
|
2042
2044
|
const hasEntry = !!navPropEntry;
|
|
2043
2045
|
|
|
2044
2046
|
if (!hasEntry)
|
|
2045
|
-
navPropEntry = { NavigationProperty: { '=':
|
|
2047
|
+
navPropEntry = { NavigationProperty: { '=': laprefix } };
|
|
2046
2048
|
|
|
2047
2049
|
|
|
2048
2050
|
const props = Object.entries(containee);
|
|
@@ -2172,8 +2174,8 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2172
2174
|
// rewrite annotation expression paths such that they are defined against the definition
|
|
2173
2175
|
const absPath = $path2path(carrier.$path);
|
|
2174
2176
|
let isSubTreeSpan = true;
|
|
2175
|
-
const rootPrefix = absPath.slice(1, absPath.length - 1);
|
|
2176
2177
|
|
|
2178
|
+
let rootPrefix = absPath.slice(1, absPath.length - 1);
|
|
2177
2179
|
const isExprAnno = (obj, pn) => isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
|
|
2178
2180
|
|
|
2179
2181
|
const subTreeSpan = {
|
|
@@ -2181,8 +2183,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2181
2183
|
const head = xpr[0].id || xpr[0];
|
|
2182
2184
|
if (head === '$self' || parent.param) {
|
|
2183
2185
|
let j = parent.param ? 0 : 1;
|
|
2186
|
+
const k = parent.param ? 1 : 0;
|
|
2187
|
+
isSubTreeSpan = isSubTreeSpan && (absPath.length - k <= xpr.length);
|
|
2184
2188
|
for (let i = 1; i < absPath.length - 1 && isSubTreeSpan; i++, j++)
|
|
2185
|
-
isSubTreeSpan = (xpr[j].id || xpr[j]) === absPath[i];
|
|
2189
|
+
isSubTreeSpan = isSubTreeSpan && (xpr[j].id || xpr[j]) === absPath[i];
|
|
2186
2190
|
}
|
|
2187
2191
|
},
|
|
2188
2192
|
};
|
|
@@ -2197,10 +2201,13 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2197
2201
|
for (let i = 1; i < absPath.length - 1 && absPathPrefixEqual; i++, j++)
|
|
2198
2202
|
absPathPrefixEqual = (xpr[j].id || xpr[j]) === absPath[i];
|
|
2199
2203
|
|
|
2200
|
-
if (absPathPrefixEqual)
|
|
2204
|
+
if (absPathPrefixEqual) {
|
|
2201
2205
|
// remove prefix between $self and leaf element name
|
|
2202
2206
|
// or starting with the parameter name
|
|
2203
2207
|
xpr.splice(parent.param ? 0 : 1, absPath.length - 2);
|
|
2208
|
+
if (parent.param)
|
|
2209
|
+
parent.param = null;
|
|
2210
|
+
}
|
|
2204
2211
|
}
|
|
2205
2212
|
},
|
|
2206
2213
|
};
|
|
@@ -2224,6 +2231,29 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2224
2231
|
acc[isExprAnno(carrier, pn) ? 0 : 1].push(pn);
|
|
2225
2232
|
return acc;
|
|
2226
2233
|
}, [ [], [] ]);
|
|
2234
|
+
|
|
2235
|
+
let scope = carrier.$path[2];
|
|
2236
|
+
let def = csn.definitions[carrier.$path[1]];
|
|
2237
|
+
// unbound
|
|
2238
|
+
if (scope === 'returns') {
|
|
2239
|
+
absPath[1] = '$ReturnType';
|
|
2240
|
+
rootPrefix = absPath.slice(3, absPath.length - 1);
|
|
2241
|
+
}
|
|
2242
|
+
let eltPath = absPath.slice(1).join('/');
|
|
2243
|
+
// bound action
|
|
2244
|
+
if (scope === 'actions' && def[scope][carrier.$path[3]]) {
|
|
2245
|
+
def = def[scope][carrier.$path[3]];
|
|
2246
|
+
scope = carrier.$path[4];
|
|
2247
|
+
if (scope === 'params')
|
|
2248
|
+
rootPrefix = absPath.slice(2, absPath.length - 1);
|
|
2249
|
+
if (scope === 'returns') {
|
|
2250
|
+
absPath[2] = '$ReturnType';
|
|
2251
|
+
rootPrefix = absPath.slice(3, absPath.length - 1);
|
|
2252
|
+
eltPath = absPath.slice(2).join('/');
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
const proxyDict = `$${scope}AnnoProxies`;
|
|
2256
|
+
|
|
2227
2257
|
xprANames.forEach((xprAName) => {
|
|
2228
2258
|
isSubTreeSpan = true;
|
|
2229
2259
|
transformExpression(carrier, xprAName, subTreeSpan);
|
|
@@ -2231,18 +2261,14 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2231
2261
|
transformExpression(carrier, xprAName, relativize);
|
|
2232
2262
|
}
|
|
2233
2263
|
else {
|
|
2234
|
-
const scope = carrier.$path[2];
|
|
2235
2264
|
absolutize.scope = scope;
|
|
2236
2265
|
transformExpression(carrier, xprAName, absolutize);
|
|
2237
|
-
const def = csn.definitions[absPath[0]];
|
|
2238
|
-
const eltPath = absPath.slice(1).join('/');
|
|
2239
|
-
const proxyDict = scope === 'elements' ? '$eltAnnoProxies' : '$paramAnnoProxies';
|
|
2240
2266
|
|
|
2241
2267
|
if (!def[proxyDict])
|
|
2242
2268
|
setProp(def, proxyDict, Object.create(null));
|
|
2243
|
-
let
|
|
2244
|
-
if (!
|
|
2245
|
-
|
|
2269
|
+
let proxyCarrier = def[proxyDict][eltPath];
|
|
2270
|
+
if (!proxyCarrier) {
|
|
2271
|
+
proxyCarrier = Object.create(null);
|
|
2246
2272
|
// these attributes are needed to test for
|
|
2247
2273
|
// Property, Parameter, NavigationProperty, Collection
|
|
2248
2274
|
// Applicability
|
|
@@ -2255,18 +2281,18 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
2255
2281
|
'@cds.api.ignore',
|
|
2256
2282
|
'@odata.navigable' ].forEach((prop) => {
|
|
2257
2283
|
if (carrier[prop] != null)
|
|
2258
|
-
setProp(
|
|
2284
|
+
setProp(proxyCarrier, prop, carrier[prop]);
|
|
2259
2285
|
});
|
|
2260
2286
|
|
|
2261
2287
|
Object.keys(carrier).filter(pn => pn[0] !== '@').forEach((pn) => {
|
|
2262
|
-
|
|
2288
|
+
proxyCarrier[pn] = carrier[pn];
|
|
2263
2289
|
});
|
|
2264
|
-
def[proxyDict][eltPath] =
|
|
2290
|
+
def[proxyDict][eltPath] = proxyCarrier;
|
|
2265
2291
|
}
|
|
2266
|
-
|
|
2292
|
+
proxyCarrier[xprAName] = carrier[xprAName];
|
|
2267
2293
|
carrier[xprAName] = null;
|
|
2268
2294
|
nxprANames.filter(an => an.startsWith(`${xprAName}.`)).forEach((nxprAName) => {
|
|
2269
|
-
|
|
2295
|
+
proxyCarrier[nxprAName] = carrier[nxprAName];
|
|
2270
2296
|
carrier[nxprAName] = null;
|
|
2271
2297
|
});
|
|
2272
2298
|
}
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp
|
|
3
|
+
const { setProp } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
|
|
5
|
+
isEdmPropertyRendered, applyTransformations,
|
|
6
6
|
} = require('../model/csnUtils');
|
|
7
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
7
8
|
const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
|
|
8
9
|
const { CompilerAssertion } = require('../base/error');
|
|
9
10
|
const { cloneAnnotationValue } = require('../model/cloneCsn');
|
|
@@ -31,20 +32,6 @@ function validateOptions( _options ) {
|
|
|
31
32
|
options.isV4 = () => v4;
|
|
32
33
|
|
|
33
34
|
options.pathDelimiter = options.isStructFormat ? '/' : '_';
|
|
34
|
-
|
|
35
|
-
if (isBetaEnabled(options, 'v5preview')) {
|
|
36
|
-
if (!options.severities)
|
|
37
|
-
options.severities = {};
|
|
38
|
-
options.severities['odata-spec-violation-array'] ??= 'Error';
|
|
39
|
-
options.severities['odata-spec-violation-assoc'] ??= 'Error';
|
|
40
|
-
options.severities['odata-spec-violation-namespace'] ??= 'Error';
|
|
41
|
-
options.severities['odata-spec-violation-param'] ??= 'Error';
|
|
42
|
-
options.severities['odata-spec-violation-returns'] ??= 'Error';
|
|
43
|
-
options.severities['odata-spec-violation-type-unknown'] ??= 'Error';
|
|
44
|
-
options.severities['odata-spec-violation-no-key'] ??= 'Error';
|
|
45
|
-
options.severities['odata-spec-violation-key-type'] ??= 'Error';
|
|
46
|
-
options.severities['odata-spec-violation-property-name'] ??= 'Error';
|
|
47
|
-
}
|
|
48
35
|
return options;
|
|
49
36
|
}
|
|
50
37
|
return _options;
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -3030,6 +3030,15 @@
|
|
|
3030
3030
|
"ValueListProperty": "Edm.String"
|
|
3031
3031
|
}
|
|
3032
3032
|
},
|
|
3033
|
+
"Common.ValueListParameterConstants": {
|
|
3034
|
+
"$kind": "ComplexType",
|
|
3035
|
+
"BaseType": "Common.ValueListParameter",
|
|
3036
|
+
"Properties": {
|
|
3037
|
+
"Constants": "Collection(Edm.PrimitiveType)",
|
|
3038
|
+
"ValueListProperty": "Edm.String"
|
|
3039
|
+
},
|
|
3040
|
+
"$experimental": true
|
|
3041
|
+
},
|
|
3033
3042
|
"Common.ValueListParameterInOut": {
|
|
3034
3043
|
"$kind": "ComplexType",
|
|
3035
3044
|
"BaseType": "Common.ValueListParameter",
|