@sap/cds-compiler 4.7.6 → 4.9.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 +63 -3
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +28 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +24 -1
- package/lib/api/main.js +119 -46
- package/lib/api/options.js +51 -0
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +116 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +76 -46
- package/lib/base/messages.js +121 -35
- package/lib/base/model.js +4 -7
- package/lib/checks/actionsFunctions.js +3 -3
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +5 -3
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +8 -56
- package/lib/compiler/assert-consistency.js +11 -7
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +105 -29
- package/lib/compiler/define.js +37 -25
- package/lib/compiler/extend.js +35 -12
- package/lib/compiler/index.js +9 -10
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +24 -18
- package/lib/compiler/resolve.js +47 -45
- package/lib/compiler/shared.js +61 -21
- package/lib/compiler/tweak-assocs.js +15 -90
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +149 -71
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +7 -7
- package/lib/edm/edmInboundChecks.js +57 -5
- package/lib/edm/edmPreprocessor.js +54 -25
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +138 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2085 -1989
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +21 -11
- package/lib/json/to-csn.js +8 -4
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +23 -16
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +90 -14
- package/lib/main.js +9 -1
- package/lib/model/cloneCsn.js +21 -9
- package/lib/model/csnRefs.js +153 -42
- package/lib/model/csnUtils.js +14 -11
- package/lib/model/enrichCsn.js +4 -2
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +135 -122
- package/lib/optionProcessor.js +49 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +6 -3
- 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 +34 -1
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +11 -3
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +11 -10
- package/lib/transform/effective/misc.js +45 -14
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +29 -12
- package/lib/transform/forRelationalDB.js +104 -113
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +228 -107
- package/lib/transform/odata/toFinalBaseType.js +10 -26
- package/lib/transform/odata/typesExposure.js +41 -25
- package/lib/transform/parseExpr.js +4 -7
- package/lib/transform/transformUtils.js +50 -43
- package/lib/transform/translateAssocsToJoins.js +48 -48
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- 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/anno-missing-rewrite.md +45 -0
- 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 +2 -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
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- package/lib/utils/.eslintrc.json +0 -7
|
@@ -39,24 +39,8 @@ class XsnName {
|
|
|
39
39
|
location;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
class CsnLocation {
|
|
43
|
-
file;
|
|
44
|
-
line;
|
|
45
|
-
col;
|
|
46
|
-
endLine;
|
|
47
|
-
endCol;
|
|
48
|
-
constructor(file, line, col, endLine, endCol) {
|
|
49
|
-
this.file = file;
|
|
50
|
-
this.line = line;
|
|
51
|
-
this.col = col;
|
|
52
|
-
this.endLine = endLine;
|
|
53
|
-
this.endCol = endCol;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
42
|
module.exports = {
|
|
58
43
|
XsnSource,
|
|
59
44
|
XsnArtifact,
|
|
60
45
|
XsnName,
|
|
61
|
-
CsnLocation,
|
|
62
46
|
};
|
|
@@ -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,29 @@ 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' &&
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
error('odata-anno-xpr-ref', path, {
|
|
105
|
+
if (scopeCheck.scope === 'param' &&
|
|
106
|
+
(!elemref.param ||
|
|
107
|
+
(xpr[0].id || xpr[0]) === '$self' && !def.elements.$self)) {
|
|
108
|
+
error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaparam' });
|
|
109
|
+
// don't try to resolve those paths later on
|
|
110
|
+
delete elemref[prop];
|
|
111
|
+
}
|
|
112
|
+
if (scopeCheck.scope === 'type' && elemref.param) {
|
|
113
|
+
error('odata-anno-xpr-ref', path, { anno: scopeCheck.anno, elemref, '#': 'notaneelement' });
|
|
114
|
+
delete elemref[prop];
|
|
115
|
+
}
|
|
116
|
+
if (scopeCheck.scope === 'param' && elemref.param) {
|
|
117
|
+
// make sure that path is resolvable as element path later on
|
|
118
|
+
delete elemref.param;
|
|
119
|
+
const head = xpr[0].id || xpr[0];
|
|
120
|
+
if (head[0] === '$')
|
|
121
|
+
xpr.unshift('$self');
|
|
122
|
+
}
|
|
108
123
|
},
|
|
109
124
|
};
|
|
110
125
|
const checkDict = (dict, scope) => {
|
|
@@ -119,52 +134,50 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
119
134
|
});
|
|
120
135
|
}
|
|
121
136
|
};
|
|
122
|
-
const
|
|
137
|
+
const checkObj = (obj, scope) => {
|
|
123
138
|
scopeCheck.scope = scope;
|
|
124
|
-
const knownAnnos = filterKnownAnnotations(
|
|
139
|
+
const knownAnnos = filterKnownAnnotations(obj);
|
|
125
140
|
knownAnnos.forEach((pn) => {
|
|
126
141
|
scopeCheck.anno = pn;
|
|
127
|
-
transformExpression(
|
|
142
|
+
transformExpression(obj, pn, scopeCheck, obj.$path);
|
|
128
143
|
});
|
|
129
144
|
};
|
|
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
|
-
|
|
160
|
-
delete object._origin[attr];
|
|
161
|
-
}
|
|
145
|
+
if (def.$isParamEntity && def._origin) {
|
|
146
|
+
// check for correct paths
|
|
147
|
+
if (def._origin.$paramsAnnoProxies) {
|
|
148
|
+
def.$elementsAnnoProxies = def._origin.$paramsAnnoProxies;
|
|
149
|
+
checkDict(def.$elementsAnnoProxies, 'param');
|
|
150
|
+
}
|
|
151
|
+
checkDict(def._origin.$elementsAnnoProxies, 'type');
|
|
152
|
+
checkDict(def.elements, 'param');
|
|
153
|
+
checkDict(def._origin.elements, 'type');
|
|
154
|
+
|
|
155
|
+
scopeCheck.scope = 'param';
|
|
156
|
+
Object.keys(def._origin).forEach((attr) => {
|
|
157
|
+
if (attr[0] === '@') {
|
|
158
|
+
scopeCheck.anno = attr;
|
|
159
|
+
const [ prefix, innerAnnotation ] = attr.split('.@');
|
|
160
|
+
const ns = whatsMyTermNamespace(prefix);
|
|
161
|
+
if (ns) {
|
|
162
|
+
const steps = prefix.replace(`@${ns}.`, '').split('.');
|
|
163
|
+
const paramAnnoParts = steps[0].split('#$parameters');
|
|
164
|
+
const dictTerm = getDictTerm(`${ns}.${paramAnnoParts[0]}`, options);
|
|
165
|
+
if (paramAnnoParts.length > 1 ||
|
|
166
|
+
[ 'Singleton', 'EntitySet' ].some(y => dictTerm?.AppliesTo?.includes(y))) {
|
|
167
|
+
steps[0] = `@${ns}.${paramAnnoParts.join('')}`;
|
|
168
|
+
let newAnno = steps.join('.');
|
|
169
|
+
if (innerAnnotation)
|
|
170
|
+
newAnno += `.@${innerAnnotation}`;
|
|
171
|
+
edmUtils.assignAnnotation(def, newAnno, def._origin[attr]);
|
|
172
|
+
transformExpression(def._origin, attr, scopeCheck, def._origin.$path);
|
|
173
|
+
if (paramAnnoParts.length > 1)
|
|
174
|
+
delete def._origin[attr];
|
|
162
175
|
}
|
|
163
176
|
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
checkObj(def._origin, 'type');
|
|
180
|
+
}
|
|
168
181
|
}
|
|
169
182
|
|
|
170
183
|
/*
|
|
@@ -179,12 +192,14 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
179
192
|
There is one exception (Schema), see below
|
|
180
193
|
|
|
181
194
|
carrier = service
|
|
182
|
-
the target is the EntityContainer, unless the annotation has an "AppliesTo"
|
|
183
|
-
|
|
195
|
+
the target is the EntityContainer, unless the annotation has an "AppliesTo"
|
|
196
|
+
where only Schema is given, but not EntityContainer then the <Annotation ...>
|
|
197
|
+
is directly put into <Schema ...> without an enclosing <Annotations ...>
|
|
184
198
|
|
|
185
199
|
carrier = entity (incl. view/projection)
|
|
186
|
-
the target is the corresponding EntityType, unless the annotation has an
|
|
187
|
-
|
|
200
|
+
the target is the corresponding EntityType, unless the annotation has an
|
|
201
|
+
"AppliesTo" where only EntitySet is given, but not EntityType then the target
|
|
202
|
+
is the corresponding EntitySet
|
|
188
203
|
|
|
189
204
|
carrier = structured type
|
|
190
205
|
the target is the corresponding ComplexType
|
|
@@ -208,10 +223,11 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
208
223
|
// definition bound annotations
|
|
209
224
|
handleAnnotations(defName, def, location);
|
|
210
225
|
// definition bound element annotations
|
|
211
|
-
if (def.$
|
|
212
|
-
Object.entries(def.$
|
|
226
|
+
if (def.$elementsAnnoProxies) {
|
|
227
|
+
Object.entries(def.$elementsAnnoProxies).forEach(([ elemPath, element ]) => {
|
|
213
228
|
const edmTargetName = `${defName}/${elemPath}`;
|
|
214
|
-
handleAnnotations(edmTargetName, element, element.$path
|
|
229
|
+
handleAnnotations(edmTargetName, element, element.$path,
|
|
230
|
+
[ ...location, '$elementsAnnoProxies', elemPath ]);
|
|
215
231
|
});
|
|
216
232
|
}
|
|
217
233
|
// element bound annotations
|
|
@@ -269,15 +285,31 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
269
285
|
handleAnnotations(actionName, cAction, location);
|
|
270
286
|
|
|
271
287
|
if (cAction.params) {
|
|
288
|
+
if (cAction.$paramsAnnoProxies) {
|
|
289
|
+
Object.entries(cAction.$paramsAnnoProxies).forEach(([ paramPath, param ]) => {
|
|
290
|
+
const edmTargetName = `${actionName}/${paramPath}`;
|
|
291
|
+
handleAnnotations(edmTargetName, param, param.$path,
|
|
292
|
+
[ ...location, '$paramsAnnoProxies', paramPath ]);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
272
295
|
Object.entries(cAction.params).forEach(([ n, p ]) => {
|
|
273
296
|
const edmTargetName = `${actionName}/${n}`;
|
|
274
|
-
handleAnnotations(edmTargetName, p,
|
|
297
|
+
handleAnnotations(edmTargetName, p,
|
|
298
|
+
[ ...location, 'params', n ]);
|
|
275
299
|
});
|
|
276
300
|
}
|
|
277
301
|
if (cAction.returns) {
|
|
302
|
+
if (cAction.$returnsAnnoProxies) {
|
|
303
|
+
Object.entries(cAction.$returnsAnnoProxies).forEach(([ returnsPath, returns ]) => {
|
|
304
|
+
const edmTargetName = `${actionName}/${returnsPath}`;
|
|
305
|
+
handleAnnotations(edmTargetName, returns, returns.$path,
|
|
306
|
+
[ ...location, '$returnsAnnoProxies', returnsPath ]);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
278
309
|
const edmTargetName = `${actionName}/$ReturnType`;
|
|
279
310
|
setProp(cAction.returns, '$appliesToReturnType', true);
|
|
280
|
-
handleAnnotations(edmTargetName, cAction.returns,
|
|
311
|
+
handleAnnotations(edmTargetName, cAction.returns,
|
|
312
|
+
[ ...location, 'returns' ]);
|
|
281
313
|
delete cAction.returns.$appliesToReturnType;
|
|
282
314
|
}
|
|
283
315
|
|
|
@@ -286,12 +318,19 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
286
318
|
const params = [];
|
|
287
319
|
if (entityNameIfBound) {
|
|
288
320
|
// If this is an action and has an explicit binding parameter add it here
|
|
289
|
-
if (cAction.$bindingParam && cAction.kind === 'action')
|
|
290
|
-
params.push(
|
|
291
|
-
|
|
321
|
+
if (cAction.$bindingParam && cAction.kind === 'action') {
|
|
322
|
+
params.push(
|
|
323
|
+
cAction.$bindingParam.items
|
|
324
|
+
? `Collection(${entityNameIfBound})`
|
|
325
|
+
: entityNameIfBound
|
|
326
|
+
);
|
|
327
|
+
}
|
|
292
328
|
// If action/function has no explicit binding parameter add it here
|
|
293
|
-
else if (!cAction.$bindingParam)
|
|
294
|
-
params.push(cAction['@cds.odata.bindingparameter.collection']
|
|
329
|
+
else if (!cAction.$bindingParam) {
|
|
330
|
+
params.push(cAction['@cds.odata.bindingparameter.collection']
|
|
331
|
+
? `Collection(${entityNameIfBound})`
|
|
332
|
+
: entityNameIfBound);
|
|
333
|
+
}
|
|
295
334
|
}
|
|
296
335
|
// In case this is a function the explicit binding parameter is part of
|
|
297
336
|
// the functions params dictionary. Only for functions all parameters must
|
|
@@ -324,7 +363,7 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
324
363
|
// edmTargetName : string, name of the target in edm
|
|
325
364
|
// carrier: object, the annotated cds thing, contains all the annotations
|
|
326
365
|
// as properties with names starting with @
|
|
327
|
-
function handleAnnotations( edmTargetName, carrier, location ) {
|
|
366
|
+
function handleAnnotations( edmTargetName, carrier, location, csnPathResolutionLocation = location ) {
|
|
328
367
|
// collect the names of the carrier's annotation properties
|
|
329
368
|
// keep only those annotations that - start with a known vocabulary name
|
|
330
369
|
// - have a value other than null
|
|
@@ -351,12 +390,49 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
351
390
|
if (knownAnnos.length === 0)
|
|
352
391
|
return;
|
|
353
392
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
393
|
+
|
|
394
|
+
knownAnnos.forEach((knownAnno) => {
|
|
395
|
+
if (knownAnno.search(/\.\$edmJson\./g) < 0) {
|
|
396
|
+
transformExpression(carrier, knownAnno, {
|
|
397
|
+
ref: (elemref, prop, xpr, csnPath) => {
|
|
398
|
+
if (options.isV2() && elemref.$bparam) {
|
|
399
|
+
error('odata-anno-xpr-ref', location, {
|
|
400
|
+
elemref, anno: knownAnno, version: '2.0', '#': 'bparam_v2',
|
|
401
|
+
});
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const { links, scope } = reqDefsUtils.inspectRef(csnPath);
|
|
405
|
+
let i = scope === '$self' ? 1 : 0;
|
|
406
|
+
if (scope === '$magic') {
|
|
407
|
+
error('odata-anno-xpr-ref', location, {
|
|
408
|
+
elemref, anno: knownAnno, '#': 'magic',
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
let stop = false;
|
|
413
|
+
for (; i < links.length && !stop; i++) {
|
|
414
|
+
if (!isEdmPropertyRendered(links[i].art, csnPath)) {
|
|
415
|
+
error('odata-anno-xpr-ref', location, {
|
|
416
|
+
count: i + 1, elemref, anno: knownAnno, '#': 'notrendered',
|
|
417
|
+
});
|
|
418
|
+
stop = true;
|
|
419
|
+
}
|
|
420
|
+
if (links[i].art?._target?.$proxy && i < links.length - 1) {
|
|
421
|
+
const proxy = links[i].art?._target;
|
|
422
|
+
const eltName = links[i + 1].art?.name;
|
|
423
|
+
if (!proxy.elements[eltName]) {
|
|
424
|
+
error('odata-anno-xpr-ref', location, {
|
|
425
|
+
count: i + 2, elemref, anno: knownAnno, '#': 'notrendered',
|
|
426
|
+
});
|
|
427
|
+
stop = true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
}, csnPathResolutionLocation);
|
|
433
|
+
xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
360
436
|
|
|
361
437
|
const prefixTree = createPrefixTree();
|
|
362
438
|
|
|
@@ -1358,6 +1434,8 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
|
|
|
1358
1434
|
dict.xrefs[myServiceRoot] = { $myServiceRoot: myServiceRoot, used: false };
|
|
1359
1435
|
const edmType = new Edm.TypeBase(options.v, {}, annoDef);
|
|
1360
1436
|
dictDef = edmType._edmAttributes;
|
|
1437
|
+
if (dictDef.Type?.startsWith('Edm.Int'))
|
|
1438
|
+
dictDef.Type = 'Edm.Int';
|
|
1361
1439
|
dictDef.$myServiceRoot = myServiceRoot;
|
|
1362
1440
|
let val = annoDef['@odata.term.AppliesTo'];
|
|
1363
1441
|
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,
|
|
@@ -19,11 +19,11 @@ function getEdm( options, messageFunctions ) {
|
|
|
19
19
|
*/
|
|
20
20
|
constructor(version, attributes = Object.create(null), csn = undefined) {
|
|
21
21
|
if (!attributes || typeof attributes !== 'object')
|
|
22
|
-
error(null, '
|
|
22
|
+
error(null, 'Debug me: attributes must be a dictionary');
|
|
23
23
|
if (!Array.isArray(version))
|
|
24
|
-
error(null, `
|
|
24
|
+
error(null, `Debug me: v is either undefined or not an array: ${version}`);
|
|
25
25
|
if (version.filter(v => v).length !== 1)
|
|
26
|
-
error(null, '
|
|
26
|
+
error(null, 'Debug me: exactly one version must be set');
|
|
27
27
|
|
|
28
28
|
// Common attributes of JSON and XML.
|
|
29
29
|
// Note: Can't assign attributes directly, due to the input object being modified.
|
|
@@ -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
|
|
|
@@ -1108,7 +1108,7 @@ function getEdm( options, messageFunctions ) {
|
|
|
1108
1108
|
json.$OnDelete = c._edmAttributes.Action;
|
|
1109
1109
|
break;
|
|
1110
1110
|
default:
|
|
1111
|
-
error(null, `
|
|
1111
|
+
error(null, `Debug me: Unhandled NavProp child: ${c.kind}`);
|
|
1112
1112
|
}
|
|
1113
1113
|
});
|
|
1114
1114
|
// TODO Annotations
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
forEachDefinition, forEachMemberRecursively,
|
|
5
|
+
forEachDefinition, forEachMemberRecursively, getUtils,
|
|
6
|
+
transformExpression, findAnnotationExpression,
|
|
6
7
|
} = require('../model/csnUtils');
|
|
8
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
7
9
|
const { assignAnnotation } = require('./edmUtils.js');
|
|
8
10
|
|
|
9
11
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -49,6 +51,17 @@ function inboundQualificationChecks( csn, options, messageFunctions,
|
|
|
49
51
|
// check items.items
|
|
50
52
|
checkIfItemsOfItems(def, undefined, undefined, location);
|
|
51
53
|
forEachMemberRecursively(def, checkIfItemsOfItems, location);
|
|
54
|
+
checkIfItemsOfItems(def.returns, undefined, undefined, location.concat('returns'));
|
|
55
|
+
if (def.actions) {
|
|
56
|
+
Object.entries(def.actions).forEach(([ n, action ]) => {
|
|
57
|
+
const aLoc = location.concat('actions', n);
|
|
58
|
+
checkIfItemsOfItems(action, undefined, undefined, aLoc);
|
|
59
|
+
markBindingParamPaths(action, aLoc);
|
|
60
|
+
forEachMemberRecursively(action, checkIfItemsOfItems, aLoc);
|
|
61
|
+
checkIfItemsOfItems(action.returns, undefined, undefined, aLoc.concat('returns'));
|
|
62
|
+
markBindingParamPaths(action, aLoc);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
52
65
|
|
|
53
66
|
// decorate UUID keys with @Core.ComputedDefaultValue and complain
|
|
54
67
|
// on named type UUID elements that have no such annotation
|
|
@@ -88,14 +101,23 @@ function inboundQualificationChecks( csn, options, messageFunctions,
|
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
function checkIfItemsOfItems( member, _memberName, _prop, path ) {
|
|
104
|
+
if (!member)
|
|
105
|
+
return;
|
|
91
106
|
const memberType = csnUtils.effectiveType(member);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
let { items } = memberType;
|
|
108
|
+
if (items) {
|
|
109
|
+
if (items.target) {
|
|
110
|
+
const isComp = items.type === 'cds.Composition';
|
|
95
111
|
message('type-invalid-items', path, { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
96
112
|
return;
|
|
97
113
|
}
|
|
98
|
-
|
|
114
|
+
let i = 1;
|
|
115
|
+
while (items) {
|
|
116
|
+
items = items.items;
|
|
117
|
+
if (items)
|
|
118
|
+
i++;
|
|
119
|
+
}
|
|
120
|
+
if (i > 1) {
|
|
99
121
|
message('chained-array-of', path);
|
|
100
122
|
return;
|
|
101
123
|
}
|
|
@@ -105,6 +127,36 @@ function inboundQualificationChecks( csn, options, messageFunctions,
|
|
|
105
127
|
message('chained-array-of', path);
|
|
106
128
|
}
|
|
107
129
|
}
|
|
130
|
+
|
|
131
|
+
// we need to know if the first path step is the bindind param
|
|
132
|
+
// for the rejection of V2 paths where the BP is ignored
|
|
133
|
+
function markBindingParamPaths( action, loc ) {
|
|
134
|
+
const special$self = !csn?.definitions?.$self && '$self';
|
|
135
|
+
if (action.params) {
|
|
136
|
+
const params = Object.entries(action.params);
|
|
137
|
+
const firstParam = params[0][1];
|
|
138
|
+
const type = firstParam?.items?.type || firstParam?.type;
|
|
139
|
+
if (type === special$self) {
|
|
140
|
+
const bindingParamName = params[0][0];
|
|
141
|
+
const markBindingParam = {
|
|
142
|
+
ref: (parent, prop, xpr) => {
|
|
143
|
+
if ((xpr[0].id || xpr[0]) === bindingParamName)
|
|
144
|
+
parent.$bparam = true;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
let exprAnnos = Object.keys(action).filter(pn => findAnnotationExpression(action, pn));
|
|
148
|
+
exprAnnos.forEach((pn) => {
|
|
149
|
+
transformExpression(action, pn, markBindingParam, loc);
|
|
150
|
+
});
|
|
151
|
+
forEachMemberRecursively(action, (member, _memberName, _prop, path, _parent) => {
|
|
152
|
+
exprAnnos = Object.keys(member).filter(pn => findAnnotationExpression(member, pn));
|
|
153
|
+
exprAnnos.forEach((pn) => {
|
|
154
|
+
transformExpression(member, pn, markBindingParam, path);
|
|
155
|
+
});
|
|
156
|
+
}, loc);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
108
160
|
}
|
|
109
161
|
|
|
110
162
|
function checkNestedContextsAndServices() {
|