@sap/cds-compiler 4.8.0 → 4.9.2
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 +38 -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 +30 -17
- 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 +38 -21
- package/lib/base/messages.js +51 -20
- 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 +10 -3
- package/lib/compiler/checks.js +44 -18
- package/lib/compiler/define.js +38 -30
- 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/populate.js +0 -2
- package/lib/compiler/propagator.js +23 -19
- package/lib/compiler/resolve.js +48 -29
- package/lib/compiler/shared.js +60 -20
- package/lib/compiler/tweak-assocs.js +72 -116
- package/lib/compiler/xpr-rewrite.js +762 -0
- package/lib/edm/annotations/edmJson.js +24 -7
- package/lib/edm/annotations/genericTranslation.js +81 -61
- 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 +12 -12
- 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 +23 -11
- 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 +75 -28
- package/lib/transform/db/expansion.js +8 -3
- package/lib/transform/db/flattening.js +2 -2
- 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 +3 -3
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +221 -124
- 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 +47 -42
- package/lib/transform/translateAssocsToJoins.js +47 -47
- package/lib/transform/universalCsn/universalCsnEnricher.js +16 -19
- 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
|
}
|
|
@@ -239,66 +290,87 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
239
290
|
// Later, the query can/must be rewritten as long as a flat OData CSN is published
|
|
240
291
|
// but this then operates on the entity/view which has all struct infos available
|
|
241
292
|
function flattenAndPrefixExprPaths(carrier, propNames, csnPath, rootPrefix, typeIdx, refParentIsItems = false) {
|
|
293
|
+
|
|
242
294
|
const refCheck = {
|
|
243
295
|
ref: (elemref, prop, xpr, path) => {
|
|
244
|
-
const { art } =
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
296
|
+
const { art, scope } = inspectRef(path);
|
|
297
|
+
if (scope !== '$magic' && art) {
|
|
298
|
+
const ft = csnUtils.getFinalTypeInfo(art.type);
|
|
299
|
+
if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
|
|
300
|
+
error('odata-anno-xpr-ref', path,
|
|
301
|
+
{
|
|
302
|
+
anno: refCheck.anno,
|
|
303
|
+
elemref,
|
|
304
|
+
name: refCheck.eltLocationStr,
|
|
305
|
+
'#': 'flatten_builtin'
|
|
306
|
+
});
|
|
307
|
+
}
|
|
248
308
|
}
|
|
249
309
|
}
|
|
250
310
|
}
|
|
251
311
|
|
|
252
312
|
refCheck.eltLocationStr = (function() {
|
|
253
313
|
const [head, ...tail ] = rootPrefix;
|
|
254
|
-
|
|
314
|
+
if(tail.length)
|
|
315
|
+
return `${head}:${tail.join('.')}`;
|
|
316
|
+
else
|
|
317
|
+
return `${head}`;
|
|
255
318
|
})();
|
|
256
319
|
|
|
320
|
+
let refChanged = false;
|
|
257
321
|
const absolutifier = {
|
|
258
322
|
ref : (parent, prop, xpr) => {
|
|
259
323
|
const head = xpr[0].id || xpr[0];
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if(
|
|
263
|
-
|
|
264
|
-
|
|
324
|
+
let isPrefixed = false;
|
|
325
|
+
if(!isMagicVariable(head)) {
|
|
326
|
+
if (head === '$self' && typeIdx < rootPrefix.length) {
|
|
327
|
+
isPrefixed = true;
|
|
328
|
+
const [xprHead, ...xprTail] = xpr.slice(1, xpr.length);
|
|
329
|
+
if(xprHead) {
|
|
330
|
+
if (xprHead.id) {
|
|
331
|
+
xprHead.id = rootPrefix.slice(1, typeIdx).concat(xprHead.id).join('_');
|
|
332
|
+
parent[prop] = [ xprHead, ...xprTail ];
|
|
333
|
+
}
|
|
334
|
+
else
|
|
335
|
+
parent[prop] = [ rootPrefix.slice(1, typeIdx).concat(xprHead).join('_'), ...xprTail];
|
|
336
|
+
}
|
|
265
337
|
}
|
|
266
|
-
else
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if(xprHead.id) {
|
|
277
|
-
xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
|
|
278
|
-
parent[prop] = [ xprHead, ...xprTail ];
|
|
338
|
+
else if (head !== '$self' && !parent.param && rootPrefix.length > 2) {
|
|
339
|
+
isPrefixed = true;
|
|
340
|
+
const [xprHead, ...xprTail] = xpr;
|
|
341
|
+
if (!refParentIsItems) {
|
|
342
|
+
if (xprHead.id) {
|
|
343
|
+
xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
|
|
344
|
+
parent[prop] = [ xprHead, ...xprTail ];
|
|
345
|
+
}
|
|
346
|
+
else
|
|
347
|
+
parent[prop] = [ rootPrefix.slice(1, -1).concat(xprHead).join('_'), ...xprTail];
|
|
279
348
|
}
|
|
280
349
|
else
|
|
281
|
-
parent[prop] = [ rootPrefix.slice(
|
|
350
|
+
parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
|
|
351
|
+
}
|
|
352
|
+
if(isPrefixed) {
|
|
353
|
+
if (carrier.$scope === 'params')
|
|
354
|
+
parent.param = true;
|
|
355
|
+
else
|
|
356
|
+
parent[prop].unshift('$self');
|
|
282
357
|
}
|
|
283
|
-
else
|
|
284
|
-
parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
|
|
285
|
-
if(carrier.$scope === 'params')
|
|
286
|
-
parent.param = true;
|
|
287
|
-
else
|
|
288
|
-
parent[prop].unshift('$self');
|
|
289
358
|
}
|
|
359
|
+
if(isPrefixed)
|
|
360
|
+
refChanged = isPrefixed;
|
|
290
361
|
}
|
|
291
362
|
}
|
|
292
|
-
|
|
293
363
|
propNames.forEach(pn => {
|
|
364
|
+
refChanged = false;
|
|
294
365
|
refCheck.anno = pn;
|
|
295
366
|
transformExpression(carrier, pn, [ refCheck, refFlattener ], csnPath);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
propNames.forEach(pn => {
|
|
367
|
+
adaptRefs.forEach(fn =>
|
|
368
|
+
{ if( fn(refParentIsItems)) refChanged = true });
|
|
369
|
+
adaptRefs.length = 0;
|
|
300
370
|
transformExpression(carrier, pn, absolutifier, csnPath)
|
|
301
|
-
|
|
371
|
+
if(refChanged && carrier[pn]['='])
|
|
372
|
+
carrier[pn]['='] = true;
|
|
373
|
+
});
|
|
302
374
|
}
|
|
303
375
|
|
|
304
376
|
// TODO: This should be part of the generic path rewriting algorithm
|
|
@@ -320,57 +392,70 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
320
392
|
}
|
|
321
393
|
}
|
|
322
394
|
|
|
323
|
-
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
|
|
395
|
+
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
|
|
396
|
+
csnUtils, error, options, iterateOptions = {} ) {
|
|
324
397
|
|
|
325
398
|
// All anno path flattening is already done, don't do it on locations where we don't want it!
|
|
326
399
|
const typeNames = [];
|
|
327
400
|
forEachDefinition(csn, (def, defName) => {
|
|
328
|
-
if(def.kind === 'entity') {
|
|
401
|
+
if (def.kind === 'entity') {
|
|
329
402
|
['query', 'projection'].forEach(dictName => {
|
|
330
|
-
applyTransformationsOnNonDictionary(csn.definitions[defName],
|
|
403
|
+
applyTransformationsOnNonDictionary(csn.definitions[defName],
|
|
404
|
+
dictName, refFlattener, iterateOptions,
|
|
405
|
+
[ 'definitions', defName ]);
|
|
331
406
|
})
|
|
332
|
-
if(csn.definitions[defName].actions)
|
|
333
|
-
applyTransformationsOnDictionary(csn.definitions[defName].actions,
|
|
407
|
+
if (csn.definitions[defName].actions)
|
|
408
|
+
applyTransformationsOnDictionary(csn.definitions[defName].actions,
|
|
409
|
+
refFlattener, iterateOptions,
|
|
410
|
+
[ 'definitions', defName, 'actions' ]);
|
|
334
411
|
}
|
|
335
412
|
|
|
336
|
-
if(['type'].includes(def.kind)) {
|
|
413
|
+
if (['type'].includes(def.kind)) {
|
|
337
414
|
typeNames.push(defName);
|
|
338
|
-
applyTransformationsOnNonDictionary(csn.definitions,
|
|
415
|
+
applyTransformationsOnNonDictionary(csn.definitions,
|
|
416
|
+
defName, refFlattener, iterateOptions, [ 'definitions' ]);
|
|
339
417
|
}
|
|
340
418
|
});
|
|
341
419
|
adaptRefs.forEach(fn => fn());
|
|
342
420
|
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
421
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
422
|
+
const refCheck = {
|
|
423
|
+
ref: (elemref, prop, xpr, path) => {
|
|
424
|
+
const { links, art } = (elemref._links && elemref._art
|
|
425
|
+
? { links: elemref._links, art: elemref._art }
|
|
426
|
+
: inspectRef(path) );
|
|
427
|
+
|
|
428
|
+
let i = links.length-2;
|
|
429
|
+
const getProp = (propName) =>
|
|
430
|
+
(links[i].art?.[propName] ||
|
|
431
|
+
effectiveType(links[i].art)[propName]);
|
|
432
|
+
|
|
433
|
+
const ft = csnUtils.getFinalTypeInfo(art?.type);
|
|
434
|
+
let target = undefined;
|
|
435
|
+
for(; i >= 0 && !getProp('items') && !target; i--) {
|
|
436
|
+
target = getProp('target');
|
|
437
|
+
}
|
|
438
|
+
if (target && csn.definitions[target].$flatelements
|
|
439
|
+
&& !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
|
|
440
|
+
error('odata-anno-xpr-ref', path,
|
|
441
|
+
{ anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
|
|
361
442
|
}
|
|
362
443
|
}
|
|
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
444
|
}
|
|
445
|
+
typeNames.forEach(tn => {
|
|
446
|
+
forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
|
|
447
|
+
Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
|
|
448
|
+
let refChanged = false;
|
|
449
|
+
refCheck.anno = pn;
|
|
450
|
+
transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
|
|
451
|
+
adaptRefs.forEach(fn => {
|
|
452
|
+
if (fn(true, 1)) refChanged = true });
|
|
453
|
+
adaptRefs.length = 0;
|
|
454
|
+
if(refChanged && member[pn]['='])
|
|
455
|
+
member[pn]['='] = true;
|
|
456
|
+
});
|
|
457
|
+
}, [ 'definitions', tn ]);
|
|
458
|
+
})
|
|
374
459
|
}
|
|
375
460
|
|
|
376
461
|
function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, pathDelimiter) {
|
|
@@ -395,32 +480,39 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
395
480
|
const adaptRefs = [];
|
|
396
481
|
const transformer = {
|
|
397
482
|
ref: (parent, prop, ref, path) => {
|
|
483
|
+
let refChanged = false;
|
|
398
484
|
const { links, art, scope } = inspectRef(path);
|
|
399
485
|
const resolvedLinkTypes = resolveLinkTypes(links);
|
|
400
486
|
setProp(parent, '$path', [ ...path ]);
|
|
401
487
|
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
|
-
|
|
488
|
+
const fn = (suspend = false, suspendPos = 0,
|
|
489
|
+
refFilter = (_parent) => true) => {
|
|
490
|
+
if (refFilter(parent)) {
|
|
491
|
+
const scopedPath = [ ...parent.$path ];
|
|
492
|
+
// TODO: If foreign key annotations should be assigned via
|
|
493
|
+
// full path into target, uncomment this line and
|
|
494
|
+
// comment/remove setProp in expansion.js
|
|
495
|
+
// setProp(parent, '$structRef', parent.ref);
|
|
496
|
+
[ parent.ref, refChanged ] = flattenStructStepsInRef(ref,
|
|
497
|
+
scopedPath, links, scope, resolvedLinkTypes,
|
|
498
|
+
suspend, suspendPos, parent.$bparam);
|
|
499
|
+
resolved.set(parent, { links, art, scope });
|
|
500
|
+
// Explicitly set implicit alias for things that are now flattened - but only in columns
|
|
501
|
+
// TODO: Can this be done elegantly during expand phase already?
|
|
502
|
+
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
|
|
503
|
+
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
|
|
504
|
+
delete parent.as;
|
|
505
|
+
delete parent.$implicitAlias;
|
|
506
|
+
}
|
|
507
|
+
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
|
|
508
|
+
else if (parent.ref[parent.ref.length - 1] !== lastRef &&
|
|
509
|
+
(insideColumns(scopedPath) || insideKeys(scopedPath)) &&
|
|
510
|
+
!parent.as) {
|
|
511
|
+
parent.as = lastRef;
|
|
512
|
+
}
|
|
513
|
+
return refChanged;
|
|
422
514
|
}
|
|
423
|
-
|
|
515
|
+
return false;
|
|
424
516
|
/**
|
|
425
517
|
* Return true if the path points inside columns
|
|
426
518
|
*
|
|
@@ -428,7 +520,10 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
428
520
|
* @returns {boolean}
|
|
429
521
|
*/
|
|
430
522
|
function insideColumns( path ) {
|
|
431
|
-
return path.length >= 3
|
|
523
|
+
return path.length >= 3
|
|
524
|
+
&& (path[path.length - 3] === 'SELECT'
|
|
525
|
+
|| path[path.length - 3] === 'projection')
|
|
526
|
+
&& path[path.length - 2] === 'columns';
|
|
432
527
|
}
|
|
433
528
|
/**
|
|
434
529
|
* Return true if the path points inside keys
|
|
@@ -437,7 +532,9 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
437
532
|
* @returns {boolean}
|
|
438
533
|
*/
|
|
439
534
|
function insideKeys( path ) {
|
|
440
|
-
return path.length >= 3
|
|
535
|
+
return path.length >= 3
|
|
536
|
+
&& path[path.length - 2] === 'keys'
|
|
537
|
+
&& typeof path[path.length - 1] === 'number';
|
|
441
538
|
}
|
|
442
539
|
};
|
|
443
540
|
// 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) {
|