@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
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { forEachDefinition,
|
|
4
4
|
copyAnnotations, forEachMemberRecursively,
|
|
5
5
|
transformExpression, findAnnotationExpression } = require('../../model/csnUtils');
|
|
6
|
+
const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
|
|
6
7
|
const transformUtils = require('../transformUtils');
|
|
7
|
-
const { setProp
|
|
8
|
+
const { setProp } = require('../../base/model');
|
|
8
9
|
const { applyTransformationsOnDictionary,
|
|
9
10
|
applyTransformationsOnNonDictionary } = require('../db/applyTransformations.js');
|
|
10
11
|
const { linkForeignKeyAnnotationExtensionsToAssociation,
|
|
@@ -13,46 +14,48 @@ const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
13
14
|
const { forEach } = require('../../utils/objectUtils');
|
|
14
15
|
|
|
15
16
|
function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options) {
|
|
16
|
-
|
|
17
|
-
const isExprAnno = (obj, pn) => {
|
|
18
|
-
return isBetaEnabled(options, 'odataPathsInAnnotationExpressions') && findAnnotationExpression(obj, pn);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
17
|
forEachDefinition(csn, (def, defName) => {
|
|
22
|
-
if(def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
|
|
18
|
+
if (def.kind === 'entity' && !isExternalServiceMember(def, defName)) {
|
|
23
19
|
['elements', 'params'].forEach(dictName => {
|
|
24
20
|
const dict = def[dictName];
|
|
25
|
-
if(dict) {
|
|
21
|
+
if (dict) {
|
|
26
22
|
const csnPath = ['definitions', defName, dictName];
|
|
27
23
|
const orderedElementList = [];
|
|
28
24
|
|
|
29
|
-
forEach(dict, (
|
|
30
|
-
const location = [ ...csnPath,
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
forEach(dict, (childName, child) => {
|
|
26
|
+
const location = [ ...csnPath, childName ];
|
|
27
|
+
const rootPrefix = [ defName ];
|
|
28
|
+
let resolvedElt = child;
|
|
29
|
+
let typeIdx = 0;
|
|
30
|
+
if (child.type && !child.elements) {
|
|
31
|
+
resolvedElt = csnUtils.getFinalTypeInfo(child.type);
|
|
32
|
+
if (resolvedElt.elements)
|
|
33
|
+
typeIdx = rootPrefix.length + 1;
|
|
34
|
+
}
|
|
35
|
+
if (resolvedElt.elements) {
|
|
36
|
+
const flattenedSubTree = recurseIntoElement(dictName, child, resolvedElt,
|
|
37
|
+
!!child.notNull, location, [ ...rootPrefix, childName ], typeIdx);
|
|
35
38
|
|
|
36
39
|
flattenedSubTree.forEach(([flatEltName, flatElt]) => {
|
|
37
40
|
if (dict[flatEltName] || orderedElementList.some(elt => elt[0] === flatEltName))
|
|
38
41
|
error('name-duplicate-element', location,
|
|
39
42
|
{ '#': 'flatten-element-exist', name: flatEltName });
|
|
40
|
-
propagateToFlatElem(
|
|
43
|
+
propagateToFlatElem(child, flatElt);
|
|
41
44
|
rewriteOnCondition(flatElt, flattenedSubTree);
|
|
42
45
|
orderedElementList.push([flatEltName, flatElt]);
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
else {
|
|
46
|
-
const flatElt = cloneElt(dictName,
|
|
47
|
-
orderedElementList.push([
|
|
49
|
+
const flatElt = cloneElt(dictName, child, location, [ defName, childName ]);
|
|
50
|
+
orderedElementList.push([childName, flatElt]);
|
|
48
51
|
}
|
|
49
52
|
});
|
|
50
53
|
|
|
51
54
|
const flatDict = orderedElementList.reduce((elements, [ flatEltName, flatElt ]) => {
|
|
52
|
-
if(flatElt.items) {
|
|
55
|
+
if (flatElt.items) {
|
|
53
56
|
// rewrite annotation paths inside items.elements
|
|
54
57
|
forEachMemberRecursively(flatElt.items, (elt, _eltName, _prop, path) => {
|
|
55
|
-
const exprAnnos = Object.keys(elt).filter(pn =>
|
|
58
|
+
const exprAnnos = Object.keys(elt).filter(pn => findAnnotationExpression(elt, pn));
|
|
56
59
|
flattenAndPrefixExprPaths(elt, exprAnnos, elt.$path, path, 0, true);
|
|
57
60
|
}, [ flatEltName ], true, { pathWithoutProp: true } );
|
|
58
61
|
}
|
|
@@ -60,21 +63,77 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
60
63
|
elements[flatEltName] = flatElt;
|
|
61
64
|
return elements;
|
|
62
65
|
}, Object.create(null));
|
|
63
|
-
setProp(def,
|
|
66
|
+
setProp(def, `$flat${dictName}`, flatDict);
|
|
64
67
|
}
|
|
65
68
|
});
|
|
69
|
+
// entity annotations
|
|
66
70
|
const flatAnnos = Object.create(null);
|
|
67
|
-
const annoNames = copyAnnotations(def, flatAnnos).filter(an =>
|
|
71
|
+
const annoNames = copyAnnotations(def, flatAnnos).filter(an => findAnnotationExpression(def, an));
|
|
68
72
|
flattenAndPrefixExprPaths(flatAnnos, annoNames, [ 'definitions', defName ], [ defName ], 0);
|
|
69
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
|
+
}
|
|
70
129
|
}
|
|
71
130
|
});
|
|
72
131
|
|
|
73
132
|
function recurseIntoElement(scope, elt, resolvedElt, rootPathIsNotNull, location, rootPrefix = [], typeIdx = 0) {
|
|
74
133
|
const eltName = rootPrefix[rootPrefix.length-1];
|
|
75
|
-
if(!resolvedElt.elements) {
|
|
134
|
+
if (!resolvedElt.elements) {
|
|
76
135
|
const flatElt = cloneElt(scope, elt, location, rootPrefix, typeIdx);
|
|
77
|
-
if(rootPathIsNotNull)
|
|
136
|
+
if (rootPathIsNotNull)
|
|
78
137
|
flatElt.notNull = true;
|
|
79
138
|
else
|
|
80
139
|
delete flatElt.notNull;
|
|
@@ -84,14 +143,14 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
84
143
|
let flattenedSubTree = [];
|
|
85
144
|
forEach(resolvedElt.elements, (childName, child) => {
|
|
86
145
|
resolvedElt = child;
|
|
87
|
-
if(child.type && !child.elements) {
|
|
146
|
+
if (child.type && !child.elements) {
|
|
88
147
|
resolvedElt = csnUtils.getFinalTypeInfo(child.type);
|
|
89
|
-
if(resolvedElt.elements)
|
|
148
|
+
if (resolvedElt.elements)
|
|
90
149
|
typeIdx = rootPrefix.length + 1;
|
|
91
150
|
}
|
|
92
151
|
flattenedSubTree = flattenedSubTree.concat(recurseIntoElement(scope, child, resolvedElt,
|
|
93
152
|
!!(child.notNull && rootPathIsNotNull),
|
|
94
|
-
[... location, 'elements', childName],
|
|
153
|
+
[... location, 'elements', childName],
|
|
95
154
|
[ ...rootPrefix, childName ], typeIdx));
|
|
96
155
|
});
|
|
97
156
|
// 1) rename, 2) filter duplicates and finalize new elements
|
|
@@ -99,7 +158,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
99
158
|
return flattenedSubTree.map(([flatEltName, flatElt]) => {
|
|
100
159
|
return [ `${eltName}_${flatEltName}`, flatElt ];
|
|
101
160
|
}).filter(([name, flatElt]) =>{
|
|
102
|
-
if(duplicateDict[name]) {
|
|
161
|
+
if (duplicateDict[name]) {
|
|
103
162
|
error('name-duplicate-element', location,
|
|
104
163
|
{ '#': 'flatten-element-gen', name });
|
|
105
164
|
return false;
|
|
@@ -135,7 +194,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
135
194
|
};
|
|
136
195
|
// TODO: copy only those expression annotations that have no path into unreachable subtree, starting
|
|
137
196
|
// of typePathRoot (which could be some completely different path)
|
|
138
|
-
const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn =>
|
|
197
|
+
const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => findAnnotationExpression(flatElt, pn));
|
|
139
198
|
flattenAndPrefixExprPaths(flatElt, exprAnnoNames, elt.$path, rootPrefix, typeIdx);
|
|
140
199
|
// Copy selected type properties
|
|
141
200
|
['key', 'virtual', 'masked', 'viaAll', 'localized'].forEach(p => {
|
|
@@ -157,9 +216,9 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
157
216
|
retypeCloneWithFinalBaseType(flatElt, location);
|
|
158
217
|
|
|
159
218
|
const [ nonAnnoProps, exprAnnoProps ] = Object.keys(flatElt).reduce((acc, pn) => {
|
|
160
|
-
if(pn[0] !== '@' && pn !== 'value')
|
|
219
|
+
if (pn[0] !== '@' && pn !== 'value')
|
|
161
220
|
acc[0].push(pn);
|
|
162
|
-
if(
|
|
221
|
+
if (findAnnotationExpression(flatElt, pn) || pn === 'value')
|
|
163
222
|
acc[1].push(pn);
|
|
164
223
|
return acc;
|
|
165
224
|
}, [[],[]]);
|
|
@@ -173,18 +232,18 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
173
232
|
return flatElt;
|
|
174
233
|
|
|
175
234
|
function retypeCloneWithFinalBaseType(elt, location) {
|
|
176
|
-
if(elt.type &&
|
|
235
|
+
if (elt.type &&
|
|
177
236
|
!isBuiltinType(elt.type) &&
|
|
178
237
|
!isODataV4BuiltinFromService(elt.type, location)
|
|
179
238
|
&& !isItemsType(elt.type)) {
|
|
180
|
-
|
|
181
|
-
|
|
239
|
+
const resolvedType = csnUtils.getFinalTypeInfo(elt);
|
|
240
|
+
|
|
182
241
|
delete resolvedType.kind;
|
|
183
|
-
if(resolvedType.items)
|
|
242
|
+
if (resolvedType.items)
|
|
184
243
|
delete resolvedType.type;
|
|
185
|
-
|
|
186
|
-
if(elt.items) {
|
|
187
|
-
if(resolvedType.items) {
|
|
244
|
+
|
|
245
|
+
if (elt.items) {
|
|
246
|
+
if (resolvedType.items) {
|
|
188
247
|
elt.items = resolvedType;
|
|
189
248
|
delete elt.items.type;
|
|
190
249
|
}
|
|
@@ -192,7 +251,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
192
251
|
elt.items.type = resolvedType;
|
|
193
252
|
}
|
|
194
253
|
else {
|
|
195
|
-
if(resolvedType.items) {
|
|
254
|
+
if (resolvedType.items) {
|
|
196
255
|
elt.items = resolvedType.items;
|
|
197
256
|
delete elt.type;
|
|
198
257
|
}
|
|
@@ -200,19 +259,19 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
200
259
|
elt.type = resolvedType;
|
|
201
260
|
}
|
|
202
261
|
}
|
|
203
|
-
|
|
262
|
+
|
|
204
263
|
function isItemsType(typeName) {
|
|
205
|
-
|
|
264
|
+
const typeDef = csn.definitions[typeName];
|
|
206
265
|
return !!typeDef?.items;
|
|
207
266
|
}
|
|
208
|
-
|
|
267
|
+
|
|
209
268
|
function isODataV4BuiltinFromService( typeName, path ) {
|
|
210
269
|
if (options.odataVersion === 'v2' || typeof typeName !== 'string')
|
|
211
270
|
return false;
|
|
212
|
-
|
|
271
|
+
|
|
213
272
|
const typeServiceName = csnUtils.getServiceName(typeName);
|
|
214
273
|
let finalBaseType = csnUtils.getFinalTypeInfo(typeName).type;
|
|
215
|
-
if(!isBuiltinType(finalBaseType)) {
|
|
274
|
+
if (!isBuiltinType(finalBaseType)) {
|
|
216
275
|
const typeDef = csn.definitions[finalBaseType];
|
|
217
276
|
finalBaseType = typeDef?.items?.type || typeDef?.type;
|
|
218
277
|
}
|
|
@@ -224,61 +283,83 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
224
283
|
}
|
|
225
284
|
|
|
226
285
|
// The path rewriting must be done with the current CSN path of that exact annotation location
|
|
227
|
-
// in the element tree and done exactly after the initial copy, (otherwise, csnRefs is not able
|
|
286
|
+
// in the element tree and done exactly after the initial copy, (otherwise, csnRefs is not able
|
|
228
287
|
// to locate the relative location of the path expression in this element).
|
|
229
288
|
// At this time both annotations and values must be rewritten.
|
|
230
289
|
//
|
|
231
290
|
// Later, the query can/must be rewritten as long as a flat OData CSN is published
|
|
232
291
|
// but this then operates on the entity/view which has all struct infos available
|
|
233
292
|
function flattenAndPrefixExprPaths(carrier, propNames, csnPath, rootPrefix, typeIdx, refParentIsItems = false) {
|
|
234
|
-
refFlattener.$refParentIsItems = refParentIsItems;
|
|
235
293
|
const refCheck = {
|
|
236
|
-
ref: (
|
|
237
|
-
const { art } = inspectRef(path);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
294
|
+
ref: (elemref, prop, xpr, path) => {
|
|
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
|
+
}
|
|
241
307
|
}
|
|
242
308
|
}
|
|
243
309
|
}
|
|
244
310
|
|
|
245
311
|
refCheck.eltLocationStr = (function() {
|
|
246
312
|
const [head, ...tail ] = rootPrefix;
|
|
247
|
-
|
|
313
|
+
if(tail.length)
|
|
314
|
+
return `${head}:${tail.join('.')}`;
|
|
315
|
+
else
|
|
316
|
+
return `${head}`;
|
|
248
317
|
})();
|
|
249
318
|
|
|
250
319
|
const absolutifier = {
|
|
251
320
|
ref : (parent, prop, xpr) => {
|
|
252
321
|
const head = xpr[0].id || xpr[0];
|
|
253
|
-
if(typeIdx
|
|
322
|
+
if (typeIdx < rootPrefix.length && head === '$self' && !isMagicVariable(head)) {
|
|
254
323
|
const [xprHead, ...xprTail] = xpr.slice(1, xpr.length);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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');
|
|
335
|
+
}
|
|
260
336
|
}
|
|
261
|
-
else if(rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
|
|
337
|
+
else if (rootPrefix.length > 2 && head !== '$self' && !parent.param && !isMagicVariable(head)) {
|
|
262
338
|
const [xprHead, ...xprTail] = xpr;
|
|
263
|
-
if(!refParentIsItems)
|
|
264
|
-
|
|
339
|
+
if (!refParentIsItems) {
|
|
340
|
+
if (xprHead.id) {
|
|
341
|
+
xprHead.id = rootPrefix.slice(1, -1).concat(xprHead.id).join('_');
|
|
342
|
+
parent[prop] = [ xprHead, ...xprTail ];
|
|
343
|
+
}
|
|
344
|
+
else
|
|
345
|
+
parent[prop] = [ rootPrefix.slice(1, -1).concat(xprHead).join('_'), ...xprTail];
|
|
346
|
+
}
|
|
265
347
|
else
|
|
266
348
|
parent[prop] = [ ...rootPrefix.slice(0, rootPrefix.length-1), ...xpr];
|
|
267
|
-
if(carrier.$scope === 'params')
|
|
349
|
+
if (carrier.$scope === 'params')
|
|
268
350
|
parent.param = true;
|
|
269
351
|
else
|
|
270
352
|
parent[prop].unshift('$self');
|
|
271
353
|
}
|
|
272
354
|
}
|
|
273
355
|
}
|
|
274
|
-
|
|
356
|
+
|
|
275
357
|
propNames.forEach(pn => {
|
|
276
358
|
refCheck.anno = pn;
|
|
277
359
|
transformExpression(carrier, pn, [ refCheck, refFlattener ], csnPath);
|
|
278
360
|
});
|
|
279
|
-
adaptRefs.forEach(fn => fn());
|
|
361
|
+
adaptRefs.forEach(fn => fn(refParentIsItems));
|
|
280
362
|
adaptRefs.length = 0;
|
|
281
|
-
refFlattener.$refParentIsItems = false;
|
|
282
363
|
propNames.forEach(pn => {
|
|
283
364
|
transformExpression(carrier, pn, absolutifier, csnPath)
|
|
284
365
|
})
|
|
@@ -303,35 +384,66 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
303
384
|
}
|
|
304
385
|
}
|
|
305
386
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
* @param {CSN.Model} csn
|
|
309
|
-
* @param {CSN.Options} options
|
|
310
|
-
* @param {WeakMap} resolved Cache for resolved refs
|
|
311
|
-
* @param {string} pathDelimiter
|
|
312
|
-
* @param {object} iterateOptions
|
|
313
|
-
*/
|
|
314
|
-
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, iterateOptions = {} ) {
|
|
387
|
+
function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef, effectiveType,
|
|
388
|
+
csnUtils, error, options, iterateOptions = {} ) {
|
|
315
389
|
|
|
316
390
|
// All anno path flattening is already done, don't do it on locations where we don't want it!
|
|
391
|
+
const typeNames = [];
|
|
317
392
|
forEachDefinition(csn, (def, defName) => {
|
|
318
|
-
if(def.kind === 'entity') {
|
|
393
|
+
if (def.kind === 'entity') {
|
|
319
394
|
['query', 'projection'].forEach(dictName => {
|
|
320
|
-
applyTransformationsOnNonDictionary(csn.definitions[defName],
|
|
395
|
+
applyTransformationsOnNonDictionary(csn.definitions[defName],
|
|
396
|
+
dictName, refFlattener, iterateOptions,
|
|
397
|
+
[ 'definitions', defName ]);
|
|
321
398
|
})
|
|
322
|
-
if(csn.definitions[defName].actions)
|
|
323
|
-
applyTransformationsOnDictionary(csn.definitions[defName].actions,
|
|
399
|
+
if (csn.definitions[defName].actions)
|
|
400
|
+
applyTransformationsOnDictionary(csn.definitions[defName].actions,
|
|
401
|
+
refFlattener, iterateOptions,
|
|
402
|
+
[ 'definitions', defName, 'actions' ]);
|
|
324
403
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, iterateOptions, [ 'definitions' ]);
|
|
404
|
+
|
|
405
|
+
if (['type'].includes(def.kind)) {
|
|
406
|
+
typeNames.push(defName);
|
|
407
|
+
applyTransformationsOnNonDictionary(csn.definitions,
|
|
408
|
+
defName, refFlattener, iterateOptions, [ 'definitions' ]);
|
|
331
409
|
}
|
|
332
410
|
});
|
|
333
411
|
adaptRefs.forEach(fn => fn());
|
|
334
412
|
adaptRefs.length = 0;
|
|
413
|
+
|
|
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' });
|
|
434
|
+
}
|
|
435
|
+
}
|
|
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;
|
|
335
447
|
}
|
|
336
448
|
|
|
337
449
|
function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, pathDelimiter) {
|
|
@@ -355,32 +467,36 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
355
467
|
}
|
|
356
468
|
const adaptRefs = [];
|
|
357
469
|
const transformer = {
|
|
358
|
-
$refParentIsItems: false,
|
|
359
470
|
ref: (parent, prop, ref, path) => {
|
|
360
471
|
const { links, art, scope } = inspectRef(path);
|
|
361
472
|
const resolvedLinkTypes = resolveLinkTypes(links);
|
|
362
473
|
setProp(parent, '$path', [ ...path ]);
|
|
363
474
|
const lastRef = ref[ref.length - 1];
|
|
364
|
-
const fn = (
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
+
}
|
|
384
500
|
}
|
|
385
501
|
|
|
386
502
|
/**
|
|
@@ -390,7 +506,10 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
390
506
|
* @returns {boolean}
|
|
391
507
|
*/
|
|
392
508
|
function insideColumns( path ) {
|
|
393
|
-
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';
|
|
394
513
|
}
|
|
395
514
|
/**
|
|
396
515
|
* Return true if the path points inside keys
|
|
@@ -399,7 +518,9 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
399
518
|
* @returns {boolean}
|
|
400
519
|
*/
|
|
401
520
|
function insideKeys( path ) {
|
|
402
|
-
return path.length >= 3
|
|
521
|
+
return path.length >= 3
|
|
522
|
+
&& path[path.length - 2] === 'keys'
|
|
523
|
+
&& typeof path[path.length - 1] === 'number';
|
|
403
524
|
}
|
|
404
525
|
};
|
|
405
526
|
// adapt queries later
|
|
@@ -6,9 +6,9 @@ const {
|
|
|
6
6
|
forEachDefinition,
|
|
7
7
|
forEachGeneric,
|
|
8
8
|
forEachMemberRecursively,
|
|
9
|
-
|
|
10
|
-
findAnnotationExpression,
|
|
9
|
+
findAnnotationExpression,
|
|
11
10
|
} = require('../../model/csnUtils');
|
|
11
|
+
const { isBuiltinType } = require('../../base/builtins');
|
|
12
12
|
const { isArtifactInSomeService, isArtifactInService } = require('./utils');
|
|
13
13
|
const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
14
14
|
|
|
@@ -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;
|
|
@@ -98,12 +98,6 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
|
|
|
98
98
|
|
|
99
99
|
function expandToFinalBaseType(node, defName) {
|
|
100
100
|
if (!node) return;
|
|
101
|
-
// TODO: Clarify how should events be handled?
|
|
102
|
-
// They are not treated by the transformUtils::toFinalBaseType function
|
|
103
|
-
// in the same manner as named types, because the elements of structured events are not
|
|
104
|
-
// propagated as it is with types.
|
|
105
|
-
// It is ok to skip the expansion to the final base type for now as events are not rendered in
|
|
106
|
-
// EDMX at the moment and the reference in the OData CSN is fulfilled.
|
|
107
101
|
if (node.kind === 'event') return;
|
|
108
102
|
|
|
109
103
|
if(node.type && !isBuiltinType(node.type)) {
|
|
@@ -199,7 +193,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
|
|
|
199
193
|
// do the clone only if really needed
|
|
200
194
|
if((finalBaseType.items && !node.items) ||
|
|
201
195
|
(finalBaseType.elements && !node.elements)) {
|
|
202
|
-
//
|
|
196
|
+
// clone the definition not another clone
|
|
203
197
|
let _type = node._type;
|
|
204
198
|
while(_type._type && !_type.items && !_type.elements)
|
|
205
199
|
_type = _type._type;
|
|
@@ -227,15 +221,10 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
|
|
|
227
221
|
const typeRefStr = f(node.type.ref);
|
|
228
222
|
const typeRefRootPath = $path2path(typeRefCsnPath);
|
|
229
223
|
forEachMemberRecursively(clone, (elt, eltName, prop, location) => {
|
|
230
|
-
const [ xprANames /*nxprANames */ ] = Object.keys(elt).reduce((acc, pn) => {
|
|
231
|
-
if (pn[0] === '@')
|
|
232
|
-
acc[findAnnotationExpression(elt, pn) ? 0 : 1].push(pn);
|
|
233
|
-
return acc;
|
|
234
|
-
}, [ [], [] ]);
|
|
235
224
|
const usingPositionStr = f($path2path(location));
|
|
236
225
|
const eltRootPath = $path2path(elt.$path);
|
|
237
|
-
|
|
238
|
-
|
|
226
|
+
|
|
227
|
+
Object.keys(elt).filter(pn => pn[0] === '@' && findAnnotationExpression(elt, pn)).forEach(xprAName => {
|
|
239
228
|
transformExpression(elt, xprAName, {
|
|
240
229
|
ref: (parent, prop, xpr, csnPath) => {
|
|
241
230
|
let prefixMatch = true;
|
|
@@ -245,23 +234,18 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
|
|
|
245
234
|
prefixMatch = (xpr[i].id || xpr[i]) === typeRefRootPath[i];
|
|
246
235
|
}
|
|
247
236
|
if(prefixMatch && xpr.length > typeRefRootPath.length) {
|
|
248
|
-
|
|
237
|
+
if(xpr.length >= eltRootPath.length)
|
|
238
|
+
parent[prop] = [ ...xpr.slice(eltRootPath.length-1)];
|
|
239
|
+
else
|
|
240
|
+
parent[prop] = [ '$self', ...xpr.slice(typeRefRootPath.length)];
|
|
249
241
|
}
|
|
250
242
|
else {
|
|
251
243
|
error('odata-anno-xpr-ref', csnPath,
|
|
252
244
|
{ anno: xprAName, elemref: xpr.join('.'), name: usingPositionStr, code: typeRefStr });
|
|
253
|
-
//isSubTreeSpan = false;
|
|
254
245
|
}
|
|
255
246
|
}
|
|
256
247
|
},
|
|
257
248
|
}, elt.$path);
|
|
258
|
-
// if(!isSubTreeSpan) {
|
|
259
|
-
// // remove annotation and sub annotations from cloned element
|
|
260
|
-
// delete elt[xprAName];
|
|
261
|
-
// nxprANames.filter(an => an.startsWith(`${xprAName}.`)).forEach((nxprAName) => {
|
|
262
|
-
// delete elt[nxprAName];
|
|
263
|
-
// });
|
|
264
|
-
// }
|
|
265
249
|
});
|
|
266
250
|
setProp(elt, '$path', location);
|
|
267
251
|
}, node.$path);
|