@sap/cds-compiler 4.8.0 → 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 +29 -4
- 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 +14 -1
- package/doc/CHANGELOG_BETA.md +19 -0
- package/lib/api/main.js +59 -24
- package/lib/api/options.js +12 -1
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +27 -0
- package/lib/base/message-registry.js +32 -19
- package/lib/base/messages.js +50 -19
- package/lib/base/model.js +4 -5
- package/lib/checks/actionsFunctions.js +2 -2
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/queryNoDbArtifacts.js +3 -2
- package/lib/checks/validator.js +2 -34
- package/lib/compiler/assert-consistency.js +8 -2
- package/lib/compiler/checks.js +44 -18
- package/lib/compiler/define.js +34 -22
- package/lib/compiler/extend.js +33 -10
- package/lib/compiler/index.js +0 -1
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/propagator.js +21 -18
- package/lib/compiler/resolve.js +44 -28
- package/lib/compiler/shared.js +60 -20
- package/lib/compiler/tweak-assocs.js +13 -88
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/edm/annotations/genericTranslation.js +80 -60
- package/lib/edm/edm.js +4 -4
- package/lib/edm/edmInboundChecks.js +33 -0
- package/lib/edm/edmPreprocessor.js +9 -6
- package/lib/gen/Dictionary.json +129 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1523 -1518
- package/lib/json/from-csn.js +13 -4
- package/lib/json/to-csn.js +10 -11
- package/lib/language/genericAntlrParser.js +14 -6
- package/lib/main.d.ts +67 -14
- package/lib/main.js +1 -0
- package/lib/model/cloneCsn.js +6 -3
- package/lib/model/csnRefs.js +12 -7
- package/lib/model/csnUtils.js +13 -7
- package/lib/model/enrichCsn.js +3 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +33 -34
- package/lib/optionProcessor.js +27 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +3 -1
- package/lib/transform/db/applyTransformations.js +33 -0
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +8 -3
- 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/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +6 -8
- package/lib/transform/effective/misc.js +31 -10
- package/lib/transform/forOdata.js +23 -7
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +189 -106
- package/lib/transform/odata/toFinalBaseType.js +1 -1
- package/lib/transform/odata/typesExposure.js +15 -12
- package/lib/transform/parseExpr.js +4 -4
- package/lib/transform/transformUtils.js +40 -37
- package/lib/transform/translateAssocsToJoins.js +47 -47
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/message-explanations.json +1 -0
- 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
|
@@ -5,7 +5,7 @@ const { forEachDefinition,
|
|
|
5
5
|
transformExpression, findAnnotationExpression } = require('../../model/csnUtils');
|
|
6
6
|
const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
|
|
7
7
|
const transformUtils = require('../transformUtils');
|
|
8
|
-
const { setProp
|
|
8
|
+
const { setProp } = require('../../base/model');
|
|
9
9
|
const { applyTransformationsOnDictionary,
|
|
10
10
|
applyTransformationsOnNonDictionary } = require('../db/applyTransformations.js');
|
|
11
11
|
const { linkForeignKeyAnnotationExtensionsToAssociation,
|
|
@@ -14,30 +14,25 @@ const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
14
14
|
const { forEach } = require('../../utils/objectUtils');
|
|
15
15
|
|
|
16
16
|
function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options) {
|
|
17
|
-
|
|
18
|
-
const isExprAnno = (obj, pn) => {
|
|
19
|
-
return isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
forEachDefinition(csn, (def, defName) => {
|
|
23
|
-
if(def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
|
|
18
|
+
if (def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
|
|
24
19
|
['elements', 'params'].forEach(dictName => {
|
|
25
20
|
const dict = def[dictName];
|
|
26
|
-
if(dict) {
|
|
21
|
+
if (dict) {
|
|
27
22
|
const csnPath = ['definitions', defName, dictName];
|
|
28
23
|
const orderedElementList = [];
|
|
29
24
|
|
|
30
25
|
forEach(dict, (childName, child) => {
|
|
31
26
|
const location = [ ...csnPath, childName ];
|
|
32
|
-
|
|
27
|
+
const rootPrefix = [ defName ];
|
|
33
28
|
let resolvedElt = child;
|
|
34
29
|
let typeIdx = 0;
|
|
35
|
-
if(child.type && !child.elements) {
|
|
30
|
+
if (child.type && !child.elements) {
|
|
36
31
|
resolvedElt = csnUtils.getFinalTypeInfo(child.type);
|
|
37
|
-
if(resolvedElt.elements)
|
|
32
|
+
if (resolvedElt.elements)
|
|
38
33
|
typeIdx = rootPrefix.length + 1;
|
|
39
34
|
}
|
|
40
|
-
if(resolvedElt.elements) {
|
|
35
|
+
if (resolvedElt.elements) {
|
|
41
36
|
const flattenedSubTree = recurseIntoElement(dictName, child, resolvedElt,
|
|
42
37
|
!!child.notNull, location, [ ...rootPrefix, childName ], typeIdx);
|
|
43
38
|
|
|
@@ -57,10 +52,10 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
57
52
|
});
|
|
58
53
|
|
|
59
54
|
const flatDict = orderedElementList.reduce((elements, [ flatEltName, flatElt ]) => {
|
|
60
|
-
if(flatElt.items) {
|
|
55
|
+
if (flatElt.items) {
|
|
61
56
|
// rewrite annotation paths inside items.elements
|
|
62
57
|
forEachMemberRecursively(flatElt.items, (elt, _eltName, _prop, path) => {
|
|
63
|
-
const exprAnnos = Object.keys(elt).filter(pn =>
|
|
58
|
+
const exprAnnos = Object.keys(elt).filter(pn => findAnnotationExpression(elt, pn));
|
|
64
59
|
flattenAndPrefixExprPaths(elt, exprAnnos, elt.$path, path, 0, true);
|
|
65
60
|
}, [ flatEltName ], true, { pathWithoutProp: true } );
|
|
66
61
|
}
|
|
@@ -71,18 +66,74 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
71
66
|
setProp(def, `$flat${dictName}`, flatDict);
|
|
72
67
|
}
|
|
73
68
|
});
|
|
69
|
+
// entity annotations
|
|
74
70
|
const flatAnnos = Object.create(null);
|
|
75
|
-
const annoNames = copyAnnotations(def, flatAnnos).filter(an =>
|
|
71
|
+
const annoNames = copyAnnotations(def, flatAnnos).filter(an => findAnnotationExpression(def, an));
|
|
76
72
|
flattenAndPrefixExprPaths(flatAnnos, annoNames, [ 'definitions', defName ], [ defName ], 0);
|
|
77
73
|
setProp(def, '$flatAnnotations', flatAnnos);
|
|
74
|
+
// explicit binding parameter of bound action
|
|
75
|
+
if (def.actions) {
|
|
76
|
+
const special$self = !csn?.definitions?.$self && '$self';
|
|
77
|
+
Object.entries(def.actions).forEach(([actionName, action]) => {
|
|
78
|
+
if (action.params) {
|
|
79
|
+
const params = Object.entries(action.params);
|
|
80
|
+
const firstParam = params[0][1];
|
|
81
|
+
const type = firstParam?.items?.type || firstParam?.type;
|
|
82
|
+
if (type === special$self) {
|
|
83
|
+
|
|
84
|
+
const bindingParamName = params[0][0];
|
|
85
|
+
const markBindingParam = {
|
|
86
|
+
ref: (parent, prop, xpr) => {
|
|
87
|
+
if ((xpr[0].id || xpr[0]) === bindingParamName)
|
|
88
|
+
setProp(parent, '$bparam', true)
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
const refCheck = {
|
|
92
|
+
ref: (elemref, prop, xpr, path) => {
|
|
93
|
+
const { art, scope } = inspectRef(path);
|
|
94
|
+
if (scope !== '$magic' && art) {
|
|
95
|
+
const ft = csnUtils.getFinalTypeInfo(art.type);
|
|
96
|
+
if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
|
|
97
|
+
error('odata-anno-xpr-ref', path, { anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const flatAnnos = Object.create(null);
|
|
104
|
+
const annoNames = copyAnnotations(action, flatAnnos).filter(an => findAnnotationExpression(action, an));
|
|
105
|
+
annoNames.forEach((an) => {
|
|
106
|
+
refCheck.anno = an;
|
|
107
|
+
transformExpression(flatAnnos, an,
|
|
108
|
+
[ markBindingParam, refCheck, refFlattener ],
|
|
109
|
+
[ 'definitions', defName, 'actions', actionName ]);
|
|
110
|
+
adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
|
|
111
|
+
adaptRefs.length = 0;
|
|
112
|
+
});
|
|
113
|
+
setProp(action, '$flatAnnotations', flatAnnos);
|
|
114
|
+
|
|
115
|
+
forEachMemberRecursively(action, (member, memberName, prop, path, _parent) => {
|
|
116
|
+
const exprAnnos = Object.keys(member).filter(pn => findAnnotationExpression(member, pn));
|
|
117
|
+
exprAnnos.forEach((pn) => {
|
|
118
|
+
refCheck.anno = pn;
|
|
119
|
+
transformExpression(member, pn, [ markBindingParam, refCheck, refFlattener ], path);
|
|
120
|
+
adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
|
|
121
|
+
adaptRefs.length = 0;
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
[ 'definitions', defName, 'actions', actionName ]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
78
129
|
}
|
|
79
130
|
});
|
|
80
131
|
|
|
81
132
|
function recurseIntoElement(scope, elt, resolvedElt, rootPathIsNotNull, location, rootPrefix = [], typeIdx = 0) {
|
|
82
133
|
const eltName = rootPrefix[rootPrefix.length-1];
|
|
83
|
-
if(!resolvedElt.elements) {
|
|
134
|
+
if (!resolvedElt.elements) {
|
|
84
135
|
const flatElt = cloneElt(scope, elt, location, rootPrefix, typeIdx);
|
|
85
|
-
if(rootPathIsNotNull)
|
|
136
|
+
if (rootPathIsNotNull)
|
|
86
137
|
flatElt.notNull = true;
|
|
87
138
|
else
|
|
88
139
|
delete flatElt.notNull;
|
|
@@ -92,9 +143,9 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
92
143
|
let flattenedSubTree = [];
|
|
93
144
|
forEach(resolvedElt.elements, (childName, child) => {
|
|
94
145
|
resolvedElt = child;
|
|
95
|
-
if(child.type && !child.elements) {
|
|
146
|
+
if (child.type && !child.elements) {
|
|
96
147
|
resolvedElt = csnUtils.getFinalTypeInfo(child.type);
|
|
97
|
-
if(resolvedElt.elements)
|
|
148
|
+
if (resolvedElt.elements)
|
|
98
149
|
typeIdx = rootPrefix.length + 1;
|
|
99
150
|
}
|
|
100
151
|
flattenedSubTree = flattenedSubTree.concat(recurseIntoElement(scope, child, resolvedElt,
|
|
@@ -107,7 +158,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
107
158
|
return flattenedSubTree.map(([flatEltName, flatElt]) => {
|
|
108
159
|
return [ `${eltName}_${flatEltName}`, flatElt ];
|
|
109
160
|
}).filter(([name, flatElt]) =>{
|
|
110
|
-
if(duplicateDict[name]) {
|
|
161
|
+
if (duplicateDict[name]) {
|
|
111
162
|
error('name-duplicate-element', location,
|
|
112
163
|
{ '#': 'flatten-element-gen', name });
|
|
113
164
|
return false;
|
|
@@ -143,7 +194,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
143
194
|
};
|
|
144
195
|
// TODO: copy only those expression annotations that have no path into unreachable subtree, starting
|
|
145
196
|
// of typePathRoot (which could be some completely different path)
|
|
146
|
-
const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn =>
|
|
197
|
+
const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => findAnnotationExpression(flatElt, pn));
|
|
147
198
|
flattenAndPrefixExprPaths(flatElt, exprAnnoNames, elt.$path, rootPrefix, typeIdx);
|
|
148
199
|
// Copy selected type properties
|
|
149
200
|
['key', 'virtual', 'masked', 'viaAll', 'localized'].forEach(p => {
|
|
@@ -165,9 +216,9 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
165
216
|
retypeCloneWithFinalBaseType(flatElt, location);
|
|
166
217
|
|
|
167
218
|
const [ nonAnnoProps, exprAnnoProps ] = Object.keys(flatElt).reduce((acc, pn) => {
|
|
168
|
-
if(pn[0] !== '@' && pn !== 'value')
|
|
219
|
+
if (pn[0] !== '@' && pn !== 'value')
|
|
169
220
|
acc[0].push(pn);
|
|
170
|
-
if(
|
|
221
|
+
if (findAnnotationExpression(flatElt, pn) || pn === 'value')
|
|
171
222
|
acc[1].push(pn);
|
|
172
223
|
return acc;
|
|
173
224
|
}, [[],[]]);
|
|
@@ -181,18 +232,18 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
181
232
|
return flatElt;
|
|
182
233
|
|
|
183
234
|
function retypeCloneWithFinalBaseType(elt, location) {
|
|
184
|
-
if(elt.type &&
|
|
235
|
+
if (elt.type &&
|
|
185
236
|
!isBuiltinType(elt.type) &&
|
|
186
237
|
!isODataV4BuiltinFromService(elt.type, location)
|
|
187
238
|
&& !isItemsType(elt.type)) {
|
|
188
|
-
|
|
239
|
+
const resolvedType = csnUtils.getFinalTypeInfo(elt);
|
|
189
240
|
|
|
190
241
|
delete resolvedType.kind;
|
|
191
|
-
if(resolvedType.items)
|
|
242
|
+
if (resolvedType.items)
|
|
192
243
|
delete resolvedType.type;
|
|
193
244
|
|
|
194
|
-
if(elt.items) {
|
|
195
|
-
if(resolvedType.items) {
|
|
245
|
+
if (elt.items) {
|
|
246
|
+
if (resolvedType.items) {
|
|
196
247
|
elt.items = resolvedType;
|
|
197
248
|
delete elt.items.type;
|
|
198
249
|
}
|
|
@@ -200,7 +251,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
200
251
|
elt.items.type = resolvedType;
|
|
201
252
|
}
|
|
202
253
|
else {
|
|
203
|
-
if(resolvedType.items) {
|
|
254
|
+
if (resolvedType.items) {
|
|
204
255
|
elt.items = resolvedType.items;
|
|
205
256
|
delete elt.type;
|
|
206
257
|
}
|
|
@@ -210,7 +261,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
210
261
|
}
|
|
211
262
|
|
|
212
263
|
function isItemsType(typeName) {
|
|
213
|
-
|
|
264
|
+
const typeDef = csn.definitions[typeName];
|
|
214
265
|
return !!typeDef?.items;
|
|
215
266
|
}
|
|
216
267
|
|
|
@@ -220,7 +271,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
220
271
|
|
|
221
272
|
const typeServiceName = csnUtils.getServiceName(typeName);
|
|
222
273
|
let finalBaseType = csnUtils.getFinalTypeInfo(typeName).type;
|
|
223
|
-
if(!isBuiltinType(finalBaseType)) {
|
|
274
|
+
if (!isBuiltinType(finalBaseType)) {
|
|
224
275
|
const typeDef = csn.definitions[finalBaseType];
|
|
225
276
|
finalBaseType = typeDef?.items?.type || typeDef?.type;
|
|
226
277
|
}
|
|
@@ -241,39 +292,52 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
241
292
|
function flattenAndPrefixExprPaths(carrier, propNames, csnPath, rootPrefix, typeIdx, refParentIsItems = false) {
|
|
242
293
|
const refCheck = {
|
|
243
294
|
ref: (elemref, prop, xpr, path) => {
|
|
244
|
-
const { art } =
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
295
|
+
const { art, scope } = inspectRef(path);
|
|
296
|
+
if (scope !== '$magic' && art) {
|
|
297
|
+
const ft = csnUtils.getFinalTypeInfo(art.type);
|
|
298
|
+
if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
|
|
299
|
+
error('odata-anno-xpr-ref', path,
|
|
300
|
+
{
|
|
301
|
+
anno: refCheck.anno,
|
|
302
|
+
elemref,
|
|
303
|
+
name: refCheck.eltLocationStr,
|
|
304
|
+
'#': 'flatten_builtin'
|
|
305
|
+
});
|
|
306
|
+
}
|
|
248
307
|
}
|
|
249
308
|
}
|
|
250
309
|
}
|
|
251
310
|
|
|
252
311
|
refCheck.eltLocationStr = (function() {
|
|
253
312
|
const [head, ...tail ] = rootPrefix;
|
|
254
|
-
|
|
313
|
+
if(tail.length)
|
|
314
|
+
return `${head}:${tail.join('.')}`;
|
|
315
|
+
else
|
|
316
|
+
return `${head}`;
|
|
255
317
|
})();
|
|
256
318
|
|
|
257
319
|
const absolutifier = {
|
|
258
320
|
ref : (parent, prop, xpr) => {
|
|
259
321
|
const head = xpr[0].id || xpr[0];
|
|
260
|
-
if(typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
|
|
322
|
+
if (typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
|
|
261
323
|
const [xprHead, ...xprTail] = xpr.slice(1, xpr.length);
|
|
262
|
-
if(xprHead
|
|
263
|
-
|
|
264
|
-
|
|
324
|
+
if(xprHead) {
|
|
325
|
+
if (xprHead.id) {
|
|
326
|
+
xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
|
|
327
|
+
parent[prop] = [ xprHead, ...xprTail ];
|
|
328
|
+
}
|
|
329
|
+
else
|
|
330
|
+
parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
|
|
331
|
+
if (carrier.$scope === 'params')
|
|
332
|
+
parent.param = true;
|
|
333
|
+
else
|
|
334
|
+
parent[prop].unshift('$self');
|
|
265
335
|
}
|
|
266
|
-
else
|
|
267
|
-
parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
|
|
268
|
-
if(carrier.$scope === 'params')
|
|
269
|
-
parent.param = true;
|
|
270
|
-
else
|
|
271
|
-
parent[prop].unshift('$self');
|
|
272
336
|
}
|
|
273
|
-
else if(rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
|
|
337
|
+
else if (rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
|
|
274
338
|
const [xprHead, ...xprTail] = xpr;
|
|
275
|
-
if(!refParentIsItems) {
|
|
276
|
-
if(xprHead.id) {
|
|
339
|
+
if (!refParentIsItems) {
|
|
340
|
+
if (xprHead.id) {
|
|
277
341
|
xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
|
|
278
342
|
parent[prop] = [ xprHead, ...xprTail ];
|
|
279
343
|
}
|
|
@@ -282,7 +346,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
282
346
|
}
|
|
283
347
|
else
|
|
284
348
|
parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
|
|
285
|
-
if(carrier.$scope === 'params')
|
|
349
|
+
if (carrier.$scope === 'params')
|
|
286
350
|
parent.param = true;
|
|
287
351
|
else
|
|
288
352
|
parent[prop].unshift('$self');
|
|
@@ -320,57 +384,66 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
320
384
|
}
|
|
321
385
|
}
|
|
322
386
|
|
|
323
|
-
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
|
|
387
|
+
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
|
|
388
|
+
csnUtils, error, options, iterateOptions = {} ) {
|
|
324
389
|
|
|
325
390
|
// All anno path flattening is already done, don't do it on locations where we don't want it!
|
|
326
391
|
const typeNames = [];
|
|
327
392
|
forEachDefinition(csn, (def, defName) => {
|
|
328
|
-
if(def.kind === 'entity') {
|
|
393
|
+
if (def.kind === 'entity') {
|
|
329
394
|
['query', 'projection'].forEach(dictName => {
|
|
330
|
-
applyTransformationsOnNonDictionary(csn.definitions[defName],
|
|
395
|
+
applyTransformationsOnNonDictionary(csn.definitions[defName],
|
|
396
|
+
dictName, refFlattener, iterateOptions,
|
|
397
|
+
[ 'definitions', defName ]);
|
|
331
398
|
})
|
|
332
|
-
if(csn.definitions[defName].actions)
|
|
333
|
-
applyTransformationsOnDictionary(csn.definitions[defName].actions,
|
|
399
|
+
if (csn.definitions[defName].actions)
|
|
400
|
+
applyTransformationsOnDictionary(csn.definitions[defName].actions,
|
|
401
|
+
refFlattener, iterateOptions,
|
|
402
|
+
[ 'definitions', defName, 'actions' ]);
|
|
334
403
|
}
|
|
335
404
|
|
|
336
|
-
if(['type'].includes(def.kind)) {
|
|
405
|
+
if (['type'].includes(def.kind)) {
|
|
337
406
|
typeNames.push(defName);
|
|
338
|
-
applyTransformationsOnNonDictionary(csn.definitions,
|
|
407
|
+
applyTransformationsOnNonDictionary(csn.definitions,
|
|
408
|
+
defName, refFlattener, iterateOptions, [ 'definitions' ]);
|
|
339
409
|
}
|
|
340
410
|
});
|
|
341
411
|
adaptRefs.forEach(fn => fn());
|
|
342
412
|
adaptRefs.length = 0;
|
|
343
|
-
if(isBetaEnabled(options, 'odataPathsInAnnotationExpressions')) {
|
|
344
|
-
const refCheck = {
|
|
345
|
-
ref: (elemref, prop, xpr, path) => {
|
|
346
|
-
const { links, art } = (elemref._links && elemref._art ? { links: elemref._links, art: elemref._art } : inspectRef(path) );
|
|
347
|
-
|
|
348
|
-
let i = links.length-2;
|
|
349
|
-
const getProp = (propName) =>
|
|
350
|
-
(links[i].art?.[propName] ||
|
|
351
|
-
effectiveType(links[i].art)[propName]);
|
|
352
413
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
414
|
+
const refCheck = {
|
|
415
|
+
ref: (elemref, prop, xpr, path) => {
|
|
416
|
+
const { links, art } = (elemref._links && elemref._art
|
|
417
|
+
? { links: elemref._links, art: elemref._art }
|
|
418
|
+
: inspectRef(path) );
|
|
419
|
+
|
|
420
|
+
let i = links.length-2;
|
|
421
|
+
const getProp = (propName) =>
|
|
422
|
+
(links[i].art?.[propName] ||
|
|
423
|
+
effectiveType(links[i].art)[propName]);
|
|
424
|
+
|
|
425
|
+
const ft = csnUtils.getFinalTypeInfo(art?.type);
|
|
426
|
+
let target = undefined;
|
|
427
|
+
for(; i >= 0 && !getProp('items') && !target; i--) {
|
|
428
|
+
target = getProp('target');
|
|
429
|
+
}
|
|
430
|
+
if (target && csn.definitions[target].$flatelements
|
|
431
|
+
&& !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
|
|
432
|
+
error('odata-anno-xpr-ref', path,
|
|
433
|
+
{ anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
|
|
361
434
|
}
|
|
362
435
|
}
|
|
363
|
-
typeNames.forEach(tn => {
|
|
364
|
-
forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
|
|
365
|
-
Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
|
|
366
|
-
refCheck.anno = pn;
|
|
367
|
-
transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
|
|
368
|
-
});
|
|
369
|
-
}, [ 'definitions', tn ]);
|
|
370
|
-
})
|
|
371
|
-
adaptRefs.forEach(fn => fn(true, 1));
|
|
372
|
-
adaptRefs.length = 0;
|
|
373
436
|
}
|
|
437
|
+
typeNames.forEach(tn => {
|
|
438
|
+
forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
|
|
439
|
+
Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
|
|
440
|
+
refCheck.anno = pn;
|
|
441
|
+
transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
|
|
442
|
+
});
|
|
443
|
+
}, [ 'definitions', tn ]);
|
|
444
|
+
})
|
|
445
|
+
adaptRefs.forEach(fn => fn(true, 1));
|
|
446
|
+
adaptRefs.length = 0;
|
|
374
447
|
}
|
|
375
448
|
|
|
376
449
|
function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, pathDelimiter) {
|
|
@@ -399,26 +472,31 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
399
472
|
const resolvedLinkTypes = resolveLinkTypes(links);
|
|
400
473
|
setProp(parent, '$path', [ ...path ]);
|
|
401
474
|
const lastRef = ref[ref.length - 1];
|
|
402
|
-
const fn = (suspend=false, suspendPos=0
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
475
|
+
const fn = (suspend = false, suspendPos = 0,
|
|
476
|
+
refFilter = (_parent) => true) => {
|
|
477
|
+
if (refFilter(parent)) {
|
|
478
|
+
const scopedPath = [ ...parent.$path ];
|
|
479
|
+
// TODO: If foreign key annotations should be assigned via
|
|
480
|
+
// full path into target, uncomment this line and
|
|
481
|
+
// comment/remove setProp in expansion.js
|
|
482
|
+
// setProp(parent, '$structRef', parent.ref);
|
|
483
|
+
parent.ref = flattenStructStepsInRef(ref,
|
|
484
|
+
scopedPath, links, scope, resolvedLinkTypes,
|
|
485
|
+
suspend, suspendPos, parent.$bparam);
|
|
486
|
+
resolved.set(parent, { links, art, scope });
|
|
487
|
+
// Explicitly set implicit alias for things that are now flattened - but only in columns
|
|
488
|
+
// TODO: Can this be done elegantly during expand phase already?
|
|
489
|
+
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
|
|
490
|
+
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
|
|
491
|
+
delete parent.as;
|
|
492
|
+
delete parent.$implicitAlias;
|
|
493
|
+
}
|
|
494
|
+
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
|
|
495
|
+
else if (parent.ref[parent.ref.length - 1] !== lastRef &&
|
|
496
|
+
(insideColumns(scopedPath) || insideKeys(scopedPath)) &&
|
|
497
|
+
!parent.as) {
|
|
498
|
+
parent.as = lastRef;
|
|
499
|
+
}
|
|
422
500
|
}
|
|
423
501
|
|
|
424
502
|
/**
|
|
@@ -428,7 +506,10 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
428
506
|
* @returns {boolean}
|
|
429
507
|
*/
|
|
430
508
|
function insideColumns( path ) {
|
|
431
|
-
return path.length >= 3
|
|
509
|
+
return path.length >= 3
|
|
510
|
+
&& (path[path.length - 3] === 'SELECT'
|
|
511
|
+
|| path[path.length - 3] === 'projection')
|
|
512
|
+
&& path[path.length - 2] === 'columns';
|
|
432
513
|
}
|
|
433
514
|
/**
|
|
434
515
|
* Return true if the path points inside keys
|
|
@@ -437,7 +518,9 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
437
518
|
* @returns {boolean}
|
|
438
519
|
*/
|
|
439
520
|
function insideKeys( path ) {
|
|
440
|
-
return path.length >= 3
|
|
521
|
+
return path.length >= 3
|
|
522
|
+
&& path[path.length - 2] === 'keys'
|
|
523
|
+
&& typeof path[path.length - 1] === 'number';
|
|
441
524
|
}
|
|
442
525
|
};
|
|
443
526
|
// adapt queries later
|
|
@@ -88,7 +88,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
|
|
|
88
88
|
// type Foo: array of { qux: Integer };
|
|
89
89
|
function expandFirstLevelOfArrayed(def) {
|
|
90
90
|
if (def.items.type && !isBuiltinType(def.items.type)) {
|
|
91
|
-
|
|
91
|
+
const finalBaseType = csnUtils.getFinalTypeInfo(def.items.type);
|
|
92
92
|
if (finalBaseType?.elements) {
|
|
93
93
|
def.items.elements = cloneCsnDict(finalBaseType.elements, options);
|
|
94
94
|
delete def.items.type;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const { setProp, isBetaEnabled } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const { getNamespace, copyAnnotations, findAnnotationExpression,
|
|
11
|
+
const { getNamespace, copyAnnotations, findAnnotationExpression,
|
|
12
12
|
forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');
|
|
13
13
|
const { isBuiltinType } = require('../../base/builtins');
|
|
14
14
|
const { CompilerAssertion } = require('../../base/error');
|
|
@@ -77,18 +77,21 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
77
77
|
if (serviceName && requestedServiceNames.includes(serviceName)) {
|
|
78
78
|
if (def.kind === 'type' || def.kind === 'entity') {
|
|
79
79
|
forEachMember(def, (element, elementName, propertyName, path) => {
|
|
80
|
-
if (propertyName === 'elements'
|
|
80
|
+
if (propertyName === 'elements') {
|
|
81
81
|
const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
|
|
82
|
-
exposeTypeOf(element, element.key
|
|
82
|
+
exposeTypeOf(element, element.key, elementName, defName, serviceName, newTypeName, defName, path);
|
|
83
|
+
} else if (propertyName === 'params') {
|
|
84
|
+
const newTypeName = getNewTypeName(element, elementName, `ep_${defName.replace(/\./g, '_')}`, serviceName);
|
|
85
|
+
exposeTypeOf(element, true, elementName, defName, serviceName, newTypeName, defName, path);
|
|
83
86
|
}
|
|
84
87
|
}, path);
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
if (def.kind === 'action' || def.kind === 'function') {
|
|
88
|
-
exposeTypesOfAction(def, defName, defName, serviceName, path);
|
|
91
|
+
exposeTypesOfAction(def, defName, defName, serviceName, path, false);
|
|
89
92
|
}
|
|
90
93
|
def.actions && Object.entries(def.actions).forEach(([actionName, action]) => {
|
|
91
|
-
exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]));
|
|
94
|
+
exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]), true);
|
|
92
95
|
});
|
|
93
96
|
}
|
|
94
97
|
});
|
|
@@ -122,7 +125,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
122
125
|
* @param {String} actionName
|
|
123
126
|
* @param {String} serviceName
|
|
124
127
|
*/
|
|
125
|
-
function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
|
|
128
|
+
function exposeTypesOfAction(action, actionName, defName, serviceName, path, isBound) {
|
|
126
129
|
if (action.returns) {
|
|
127
130
|
const artificialName = `return_${actionName.replace(/\./g, '_')}`;
|
|
128
131
|
const newTypeName = getNewTypeName(action.returns, undefined, artificialName, serviceName);
|
|
@@ -130,7 +133,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
133
|
-
const artificialName =
|
|
136
|
+
const artificialName = `${isBound ? 'bap' : 'ap'}_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
|
|
134
137
|
const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);
|
|
135
138
|
exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['params', paramName]));
|
|
136
139
|
});
|
|
@@ -254,7 +257,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
254
257
|
}
|
|
255
258
|
else if(isTermDef) {
|
|
256
259
|
newType = Object.create(null);
|
|
257
|
-
for(
|
|
260
|
+
for(const n in typeDef) {
|
|
258
261
|
newType[n] = typeDef[n];
|
|
259
262
|
}
|
|
260
263
|
newType.kind = 'type';
|
|
@@ -293,7 +296,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
293
296
|
function isTypeExposable() {
|
|
294
297
|
let typeName = undefined;
|
|
295
298
|
let typeDef = node;
|
|
296
|
-
|
|
299
|
+
const elements = (node.items?.elements || node.elements)
|
|
297
300
|
// anonymous structured type
|
|
298
301
|
if(elements)
|
|
299
302
|
return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
|
|
@@ -315,7 +318,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
315
318
|
typeName = (type.ref && csnUtils.artifactRef(type)) || type;
|
|
316
319
|
}
|
|
317
320
|
else {
|
|
318
|
-
throw new CompilerAssertion(`
|
|
321
|
+
throw new CompilerAssertion(`Debug me: ${typeName} not found`);
|
|
319
322
|
}
|
|
320
323
|
}
|
|
321
324
|
}
|
|
@@ -372,7 +375,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
372
375
|
* @param {string} parentName name of the parent def holding the element
|
|
373
376
|
*/
|
|
374
377
|
function getAnonymousTypeNameInMultiSchema(typeName, parentName) {
|
|
375
|
-
|
|
378
|
+
const currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
|
|
376
379
|
const newSchemaName = currPrefix || fallBackSchemaName;
|
|
377
380
|
// new type name without any prefixes
|
|
378
381
|
const typePlainName = defNameWithoutServiceOrContextName(typeName, newSchemaName);
|
|
@@ -403,7 +406,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
403
406
|
|
|
404
407
|
// Duplicate elements
|
|
405
408
|
Object.entries(elements).forEach(([elemName, element]) => {
|
|
406
|
-
|
|
409
|
+
const cloned = cloneCsnNonDict(element, options);
|
|
407
410
|
// if this was an anonymous sub element of a key, mark it as not nullable
|
|
408
411
|
if(isAnonymous && isKey && !cloned.key) {
|
|
409
412
|
if(cloned.target) {
|
|
@@ -58,7 +58,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
58
58
|
return { 'cast': [ xpr.cast, { [castKeys[0]]: xpr[castKeys[0]] } ] };
|
|
59
59
|
}
|
|
60
60
|
else {
|
|
61
|
-
for(
|
|
61
|
+
for(const n in xpr) {
|
|
62
62
|
// xpr could be an array with polluted prototype
|
|
63
63
|
if (Object.hasOwnProperty.call(xpr, n))
|
|
64
64
|
xpr[n] = Cast(xpr[n], state)
|
|
@@ -214,7 +214,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
214
214
|
while(i < e && xpr[i] !== 'and') i++;
|
|
215
215
|
const a = i < e ? i : -1;
|
|
216
216
|
if(b >= 0) {
|
|
217
|
-
|
|
217
|
+
const token = [ 'between' ];
|
|
218
218
|
not = (xpr[b-1] === 'not');
|
|
219
219
|
if(not)
|
|
220
220
|
token.splice(0,0, 'not');
|
|
@@ -312,7 +312,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
312
312
|
if (typeof xpr === 'object') {
|
|
313
313
|
// if(xpr?.func && funkyfuncs.includes(xpr?.func))
|
|
314
314
|
// return xpr;
|
|
315
|
-
for(
|
|
315
|
+
for(const n in xpr) {
|
|
316
316
|
// xpr could be an array with polluted prototype
|
|
317
317
|
if (!Object.hasOwnProperty.call(xpr, n))
|
|
318
318
|
continue;
|
|
@@ -349,7 +349,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
349
349
|
s = p+tl;
|
|
350
350
|
[tl, p] = findToken(s, e);
|
|
351
351
|
while(p >= 0) {
|
|
352
|
-
|
|
352
|
+
const rhs = next(xpr, s, p, state);
|
|
353
353
|
naryExpr.push(...op, rhs);
|
|
354
354
|
if(state.array)
|
|
355
355
|
lhs = [ lhs, ...op, rhs ];
|