@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
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
const { setProp, isBetaEnabled } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const { getNamespace, copyAnnotations,
|
|
11
|
+
const { getNamespace, copyAnnotations, findAnnotationExpression,
|
|
12
12
|
forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');
|
|
13
|
+
const { isBuiltinType } = require('../../base/builtins');
|
|
13
14
|
const { CompilerAssertion } = require('../../base/error');
|
|
14
15
|
const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
15
16
|
|
|
@@ -76,18 +77,21 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
76
77
|
if (serviceName && requestedServiceNames.includes(serviceName)) {
|
|
77
78
|
if (def.kind === 'type' || def.kind === 'entity') {
|
|
78
79
|
forEachMember(def, (element, elementName, propertyName, path) => {
|
|
79
|
-
if (propertyName === 'elements'
|
|
80
|
+
if (propertyName === 'elements') {
|
|
80
81
|
const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
|
|
81
|
-
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);
|
|
82
86
|
}
|
|
83
87
|
}, path);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
if (def.kind === 'action' || def.kind === 'function') {
|
|
87
|
-
exposeTypesOfAction(def, defName, defName, serviceName, path);
|
|
91
|
+
exposeTypesOfAction(def, defName, defName, serviceName, path, false);
|
|
88
92
|
}
|
|
89
93
|
def.actions && Object.entries(def.actions).forEach(([actionName, action]) => {
|
|
90
|
-
exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]));
|
|
94
|
+
exposeTypesOfAction(action, `${defName}_${actionName}`, defName, serviceName, path.concat(['actions', actionName]), true);
|
|
91
95
|
});
|
|
92
96
|
}
|
|
93
97
|
});
|
|
@@ -105,7 +109,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
105
109
|
csn.definitions[defName] = def;
|
|
106
110
|
const artificialName = `term_${defName.replace(/\./g, '_')}`;//_${paramName}`;
|
|
107
111
|
const newTypeName = getNewTypeName(undefined, undefined, artificialName, serviceName);
|
|
108
|
-
exposeTypeOf(def, false, defName, defName, serviceName, newTypeName, path.concat(['vocabularies', defName]), undefined, true);
|
|
112
|
+
exposeTypeOf(def, false, defName, defName, serviceName, newTypeName, defName, path.concat(['vocabularies', defName]), undefined, true);
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
});
|
|
@@ -121,17 +125,17 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
121
125
|
* @param {String} actionName
|
|
122
126
|
* @param {String} serviceName
|
|
123
127
|
*/
|
|
124
|
-
function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
|
|
128
|
+
function exposeTypesOfAction(action, actionName, defName, serviceName, path, isBound) {
|
|
125
129
|
if (action.returns) {
|
|
126
130
|
const artificialName = `return_${actionName.replace(/\./g, '_')}`;
|
|
127
131
|
const newTypeName = getNewTypeName(action.returns, undefined, artificialName, serviceName);
|
|
128
|
-
exposeTypeOf(action.returns, false, actionName, defName, serviceName, newTypeName, path.concat(['returns']));
|
|
132
|
+
exposeTypeOf(action.returns, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['returns']));
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
132
|
-
const artificialName =
|
|
136
|
+
const artificialName = `${isBound ? 'bap' : 'ap'}_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
|
|
133
137
|
const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);
|
|
134
|
-
exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, path.concat(['params', paramName]));
|
|
138
|
+
exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, defName, path.concat(['params', paramName]));
|
|
135
139
|
});
|
|
136
140
|
}
|
|
137
141
|
|
|
@@ -144,7 +148,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
144
148
|
* @param {String} serviceName
|
|
145
149
|
* @param {String} newTypeName
|
|
146
150
|
*/
|
|
147
|
-
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName, isTermDef=false, ignoreInAPI=false) {
|
|
151
|
+
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, lastNonAnonymousFQDefName, path, parentName, isTermDef=false, ignoreInAPI=false) {
|
|
148
152
|
const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable();
|
|
149
153
|
if (isExposable) {
|
|
150
154
|
// this is the name used to register the new type in csn.definitions
|
|
@@ -164,6 +168,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
164
168
|
// expose the type as a new one not changing the original definition.
|
|
165
169
|
if(elements && !!node['@open'] !== !!typeDef['@open'])
|
|
166
170
|
fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
|
|
171
|
+
lastNonAnonymousFQDefName = fullQualifiedNewTypeName;
|
|
167
172
|
}
|
|
168
173
|
// check if that type is already defined
|
|
169
174
|
let newType = csn.definitions[fullQualifiedNewTypeName];
|
|
@@ -197,10 +202,12 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
197
202
|
newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
|
|
198
203
|
if (node.elements && node.elements[elemName].$location)
|
|
199
204
|
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
205
|
+
if (newElem.$path)
|
|
206
|
+
newElem.$path[1] = lastNonAnonymousFQDefName;
|
|
200
207
|
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
201
208
|
{
|
|
202
209
|
const { isExposable, typeDef, typeName } = exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
203
|
-
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName, isTermDef, ignoreInAPI);
|
|
210
|
+
getNewTypeName(newElem, elemName, newTypeName, serviceName), lastNonAnonymousFQDefName, path, fullQualifiedNewTypeName, isTermDef, ignoreInAPI);
|
|
204
211
|
// if the type for the newElem was not exposed it may be a scalar type def from an external service that hasn't
|
|
205
212
|
// been caught by expandToFinalBaseType() (forODataNew must not modify external imported services)
|
|
206
213
|
if(!isExposable && isBuiltinType(typeName) && !isBuiltinType((newElem.items?.type || newElem.type))) {
|
|
@@ -222,14 +229,23 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
222
229
|
}
|
|
223
230
|
});
|
|
224
231
|
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
if
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
// TODO: remove the this if and the else clause for the V5 release
|
|
233
|
+
if (isBetaEnabled(options, 'v5preview')) {
|
|
234
|
+
// We need this check, as we only need to copy the annotions if the type
|
|
235
|
+
// is user defined structured type outside of the service
|
|
236
|
+
if (!isAnonymous) {
|
|
237
|
+
copyAnnotations(typeDef, newType);
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
// expression annos and their sub annotations are not propagated to type
|
|
241
|
+
let [ xprANames, nxprANames ] = Object.keys(typeDef).reduce((acc, pn) => {
|
|
242
|
+
if (pn[0] === '@')
|
|
243
|
+
acc[findAnnotationExpression(typeDef, pn) ? 0 : 1].push(pn);
|
|
244
|
+
return acc;
|
|
245
|
+
}, [ [], [] ]);
|
|
246
|
+
nxprANames = nxprANames.filter(an => !xprANames.some(ean => an.startsWith(`${ean}.`)));
|
|
247
|
+
copyAnnotations(typeDef, newType, false, {}, nxprANames);
|
|
248
|
+
}
|
|
233
249
|
|
|
234
250
|
// if the origin type had items, add items to exposed type
|
|
235
251
|
if(typeDef.kind === 'type') {
|
|
@@ -241,7 +257,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
241
257
|
}
|
|
242
258
|
else if(isTermDef) {
|
|
243
259
|
newType = Object.create(null);
|
|
244
|
-
for(
|
|
260
|
+
for(const n in typeDef) {
|
|
245
261
|
newType[n] = typeDef[n];
|
|
246
262
|
}
|
|
247
263
|
newType.kind = 'type';
|
|
@@ -280,7 +296,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
280
296
|
function isTypeExposable() {
|
|
281
297
|
let typeName = undefined;
|
|
282
298
|
let typeDef = node;
|
|
283
|
-
|
|
299
|
+
const elements = (node.items?.elements || node.elements)
|
|
284
300
|
// anonymous structured type
|
|
285
301
|
if(elements)
|
|
286
302
|
return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
|
|
@@ -302,7 +318,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
302
318
|
typeName = (type.ref && csnUtils.artifactRef(type)) || type;
|
|
303
319
|
}
|
|
304
320
|
else {
|
|
305
|
-
throw new CompilerAssertion(`
|
|
321
|
+
throw new CompilerAssertion(`Debug me: ${typeName} not found`);
|
|
306
322
|
}
|
|
307
323
|
}
|
|
308
324
|
}
|
|
@@ -359,7 +375,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
359
375
|
* @param {string} parentName name of the parent def holding the element
|
|
360
376
|
*/
|
|
361
377
|
function getAnonymousTypeNameInMultiSchema(typeName, parentName) {
|
|
362
|
-
|
|
378
|
+
const currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
|
|
363
379
|
const newSchemaName = currPrefix || fallBackSchemaName;
|
|
364
380
|
// new type name without any prefixes
|
|
365
381
|
const typePlainName = defNameWithoutServiceOrContextName(typeName, newSchemaName);
|
|
@@ -390,7 +406,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
390
406
|
|
|
391
407
|
// Duplicate elements
|
|
392
408
|
Object.entries(elements).forEach(([elemName, element]) => {
|
|
393
|
-
|
|
409
|
+
const cloned = cloneCsnNonDict(element, options);
|
|
394
410
|
// if this was an anonymous sub element of a key, mark it as not nullable
|
|
395
411
|
if(isAnonymous && isKey && !cloned.key) {
|
|
396
412
|
if(cloned.target) {
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
-
// const funkyfuncs = Object.keys(require('../compiler/builtins.js').
|
|
6
|
-
// specialFunctions).filter(n => n.length).map(n=>n.toLowerCase());
|
|
7
|
-
|
|
8
5
|
/**
|
|
9
6
|
* parseExpr() accepts any JSON object and tries to convert a token stream expression
|
|
10
7
|
* array into an AST like expression with CDL operator precedence.
|
|
@@ -61,7 +58,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
61
58
|
return { 'cast': [ xpr.cast, { [castKeys[0]]: xpr[castKeys[0]] } ] };
|
|
62
59
|
}
|
|
63
60
|
else {
|
|
64
|
-
for(
|
|
61
|
+
for(const n in xpr) {
|
|
65
62
|
// xpr could be an array with polluted prototype
|
|
66
63
|
if (Object.hasOwnProperty.call(xpr, n))
|
|
67
64
|
xpr[n] = Cast(xpr[n], state)
|
|
@@ -217,7 +214,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
217
214
|
while(i < e && xpr[i] !== 'and') i++;
|
|
218
215
|
const a = i < e ? i : -1;
|
|
219
216
|
if(b >= 0) {
|
|
220
|
-
|
|
217
|
+
const token = [ 'between' ];
|
|
221
218
|
not = (xpr[b-1] === 'not');
|
|
222
219
|
if(not)
|
|
223
220
|
token.splice(0,0, 'not');
|
|
@@ -315,7 +312,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
315
312
|
if (typeof xpr === 'object') {
|
|
316
313
|
// if(xpr?.func && funkyfuncs.includes(xpr?.func))
|
|
317
314
|
// return xpr;
|
|
318
|
-
for(
|
|
315
|
+
for(const n in xpr) {
|
|
319
316
|
// xpr could be an array with polluted prototype
|
|
320
317
|
if (!Object.hasOwnProperty.call(xpr, n))
|
|
321
318
|
continue;
|
|
@@ -352,7 +349,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
|
|
|
352
349
|
s = p+tl;
|
|
353
350
|
[tl, p] = findToken(s, e);
|
|
354
351
|
while(p >= 0) {
|
|
355
|
-
|
|
352
|
+
const rhs = next(xpr, s, p, state);
|
|
356
353
|
naryExpr.push(...op, rhs);
|
|
357
354
|
if(state.array)
|
|
358
355
|
lhs = [ lhs, ...op, rhs ];
|
|
@@ -8,10 +8,12 @@ const { setProp } = require('../base/model');
|
|
|
8
8
|
|
|
9
9
|
const { copyAnnotations, applyTransformations, isDollarSelfOrProjectionOperand } = require('../model/csnUtils');
|
|
10
10
|
const { getUtils } = require('../model/csnUtils');
|
|
11
|
-
const { typeParameters
|
|
11
|
+
const { typeParameters } = require('../compiler/builtins');
|
|
12
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
12
13
|
const { ModelError, CompilerAssertion} = require('../base/error');
|
|
13
14
|
const { forEach } = require('../utils/objectUtils');
|
|
14
15
|
const { cloneCsnNonDict, cloneCsnDict } = require('../model/cloneCsn');
|
|
16
|
+
const { addTenantFieldToArt } = require('./addTenantFields');
|
|
15
17
|
|
|
16
18
|
const RestrictedOperators = ['<', '>', '>=', '<='];
|
|
17
19
|
const RelationalOperators = ['=', '!=', '<>', 'is' /*, 'like'*/,...RestrictedOperators];
|
|
@@ -160,14 +162,14 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
160
162
|
// Note that this must happen after struct flattening(flattenStructuredElement) - both fot elements and foreign keys.
|
|
161
163
|
// Return the newly generated foreign key element.
|
|
162
164
|
function createForeignKeyElement(assoc, assocName, foreignKey, artifact, artifactName, path) {
|
|
163
|
-
|
|
165
|
+
const result = {};
|
|
164
166
|
|
|
165
167
|
// FIXME: Duplicate code (postfix is added herein, can this be optimized?)
|
|
166
168
|
// Assemble foreign key element name from assoc name, '_' and foreign key name/alias
|
|
167
|
-
|
|
169
|
+
const foreignKeyElementName = `${assocName.replace(/\./g, pathDelimiter)}${pathDelimiter}${foreignKey.as || foreignKey.ref.join(pathDelimiter)}`;
|
|
168
170
|
|
|
169
171
|
|
|
170
|
-
|
|
172
|
+
const fkArtifact = inspectRef(path).art;
|
|
171
173
|
newForeignKey(fkArtifact, foreignKeyElementName);
|
|
172
174
|
|
|
173
175
|
function processAssociationOrComposition(fkArtifact,foreignKeyElementName) {
|
|
@@ -186,7 +188,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
186
188
|
return;
|
|
187
189
|
}
|
|
188
190
|
|
|
189
|
-
|
|
191
|
+
const foreignKeyElement = createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName);
|
|
190
192
|
|
|
191
193
|
// FIXME: must this code go into createRealFK?
|
|
192
194
|
// Not present in getForeignKeyArtifact
|
|
@@ -227,7 +229,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
227
229
|
// length: 42 },
|
|
228
230
|
// }
|
|
229
231
|
function flattenStructuredElement(elem, elemName, parentElementPath=[], pathInCsn=[]) {
|
|
230
|
-
|
|
232
|
+
const elementPath=parentElementPath.concat(elemName); // elementPath contains only element names without the csn structure node names
|
|
231
233
|
// in case the element is of user defined type => take the definition of the type
|
|
232
234
|
// for type of 'x' -> elem.type is an object, not a string -> use directly
|
|
233
235
|
let elemType;
|
|
@@ -239,7 +241,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
239
241
|
// Collect all child elements (recursively) into 'result'
|
|
240
242
|
// TODO: Do not report collisions in the generated elements here, but instead
|
|
241
243
|
// leave that work to the receiver of this result
|
|
242
|
-
|
|
244
|
+
const result = Object.create(null);
|
|
243
245
|
const addGeneratedFlattenedElement = (e, eName) => {
|
|
244
246
|
if (result[eName])
|
|
245
247
|
error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
|
|
@@ -249,10 +251,10 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
249
251
|
forEach(struct, (childName, childElem) => {
|
|
250
252
|
if (isStructured(childElem)) {
|
|
251
253
|
// Descend recursively into structured children
|
|
252
|
-
|
|
253
|
-
for (
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
const grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
|
|
255
|
+
for (const grandChildName in grandChildElems) {
|
|
256
|
+
const flatElemName = elemName + pathDelimiter + grandChildName;
|
|
257
|
+
const flatElem = grandChildElems[grandChildName];
|
|
256
258
|
addGeneratedFlattenedElement(flatElem, flatElemName);
|
|
257
259
|
// TODO: check with values. In CSN such elements have only "@Core.Computed": true
|
|
258
260
|
// If the original element had a value, construct one for the flattened element
|
|
@@ -263,8 +265,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
263
265
|
}
|
|
264
266
|
} else {
|
|
265
267
|
// Primitive child - clone it and restore its cross references
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
const flatElemName = elemName + pathDelimiter + childName;
|
|
269
|
+
const flatElem = cloneCsnNonDict(childElem, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
|
|
268
270
|
// Don't take over notNull from leaf elements
|
|
269
271
|
delete flatElem.notNull;
|
|
270
272
|
setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
|
|
@@ -300,7 +302,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
300
302
|
// 'localized' is needed for OData
|
|
301
303
|
if(options.toOdata)
|
|
302
304
|
props.push('localized');
|
|
303
|
-
for (
|
|
305
|
+
for (const p of props) {
|
|
304
306
|
if (elem[p]) {
|
|
305
307
|
flatElem[p] = elem[p];
|
|
306
308
|
}
|
|
@@ -321,10 +323,12 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
321
323
|
* @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
|
|
322
324
|
* @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
|
|
323
325
|
* @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
|
|
324
|
-
* @param {bool} [
|
|
326
|
+
* @param {bool} [suspend] suspend flattening by caller until association path step
|
|
327
|
+
* @param {int} [suspendPos] suspend if starting pos is lower or equal to suspendPos and suspend is true
|
|
328
|
+
* @param {bool} [revokeAtSuspendPos] revoke suspension after suspendPos (binding parameter path use case)
|
|
325
329
|
* @returns {string[]}
|
|
326
330
|
*/
|
|
327
|
-
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(),
|
|
331
|
+
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), suspend=false, suspendPos=0, revokeAtSuspendPos=false) {
|
|
328
332
|
// Refs of length 1 cannot contain steps - no need to check
|
|
329
333
|
if (ref.length < 2 || (scope === '$self' && ref.length === 2)) {
|
|
330
334
|
return ref;
|
|
@@ -350,10 +354,10 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
350
354
|
(resolvedLinkTypes.get(links[i])||{})[propName]);
|
|
351
355
|
|
|
352
356
|
let flattenStep = false;
|
|
353
|
-
|
|
357
|
+
suspend = !!art('items') || (suspend && i <= suspendPos);
|
|
354
358
|
for(; i < links.length; i++) {
|
|
355
359
|
|
|
356
|
-
if (flattenStep && !
|
|
360
|
+
if (flattenStep && !suspend) {
|
|
357
361
|
result[result.length - 1] += pathDelimiter + (ref[i].id ? ref[i].id : ref[i]);
|
|
358
362
|
// if we had a filter or args, we had an assoc so this step is done
|
|
359
363
|
// we then keep along the filter/args by updating the id of the current ref
|
|
@@ -362,14 +366,15 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
362
366
|
result[result.length-1] = ref[i];
|
|
363
367
|
}
|
|
364
368
|
// suspend flattening if the next path step has some 'items'
|
|
365
|
-
|
|
369
|
+
suspend = !!art('items');
|
|
366
370
|
}
|
|
367
371
|
else {
|
|
368
372
|
result.push(ref[i]);
|
|
373
|
+
suspend ||= !!art('items');
|
|
369
374
|
}
|
|
370
375
|
// revoke items suspension for next assoc step
|
|
371
|
-
if(
|
|
372
|
-
|
|
376
|
+
if(suspend && art('target') || (revokeAtSuspendPos && i === suspendPos))
|
|
377
|
+
suspend = false;
|
|
373
378
|
|
|
374
379
|
flattenStep = !links[i].art?.kind &&
|
|
375
380
|
!links[i].art?.SELECT &&
|
|
@@ -469,11 +474,11 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
469
474
|
// Add the created projection to the model and complain if artifact already exists.
|
|
470
475
|
// Used by Draft generation
|
|
471
476
|
function createExposingProjection(art, artName, projectionId, service) {
|
|
472
|
-
|
|
477
|
+
const projectionAbsoluteName = `${service}.${projectionId}`;
|
|
473
478
|
// Create elements matching the artifact's elements
|
|
474
|
-
|
|
479
|
+
const elements = Object.create(null);
|
|
475
480
|
art.elements && Object.entries(art.elements).forEach(([elemName, artElem]) => {
|
|
476
|
-
|
|
481
|
+
const elem = Object.assign({}, artElem);
|
|
477
482
|
// Transfer xrefs, that are redirected to the projection
|
|
478
483
|
// TODO: shall we remove the transferred elements from the original?
|
|
479
484
|
// if (artElem._xref) {
|
|
@@ -483,7 +488,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
483
488
|
elements[elemName] = elem;
|
|
484
489
|
});
|
|
485
490
|
|
|
486
|
-
|
|
491
|
+
const query = {
|
|
487
492
|
'SELECT': {
|
|
488
493
|
'from': {
|
|
489
494
|
'ref': [
|
|
@@ -493,13 +498,13 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
493
498
|
}
|
|
494
499
|
};
|
|
495
500
|
// Assemble the projection itself and add it into the model
|
|
496
|
-
|
|
501
|
+
const projection = {
|
|
497
502
|
'kind': 'entity',
|
|
498
503
|
projection: query.SELECT, // it is important that projection and query refer to the same object!
|
|
499
504
|
elements
|
|
500
505
|
};
|
|
501
506
|
// copy annotations from art to projection
|
|
502
|
-
for (
|
|
507
|
+
for (const a of Object.keys(art).filter(x => x.startsWith('@'))) {
|
|
503
508
|
projection[a] = art[a];
|
|
504
509
|
}
|
|
505
510
|
model.definitions[projectionAbsoluteName] = projection;
|
|
@@ -522,6 +527,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
522
527
|
if (!draftAdminDataEntity) {
|
|
523
528
|
draftAdminDataEntity = createAndAddDraftAdminDataEntity();
|
|
524
529
|
model.definitions['DRAFT.DraftAdministrativeData'] = draftAdminDataEntity;
|
|
530
|
+
if(options.tenantDiscriminator && options.transformation !== 'odata')
|
|
531
|
+
addTenantFieldToArt(model.definitions['DRAFT.DraftAdministrativeData'], options);
|
|
525
532
|
}
|
|
526
533
|
|
|
527
534
|
// Create a projection within this service
|
|
@@ -602,7 +609,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
602
609
|
if (!isBuiltinType(typeName) && !model.definitions[typeName]) {
|
|
603
610
|
throw new ModelError('Expecting valid type name: ' + typeName);
|
|
604
611
|
}
|
|
605
|
-
|
|
612
|
+
const result = {
|
|
606
613
|
[elemName]: {
|
|
607
614
|
type: typeName
|
|
608
615
|
}
|
|
@@ -630,16 +637,16 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
630
637
|
// keys: [{ ref: ['id'] }]
|
|
631
638
|
// } }
|
|
632
639
|
function createAssociationElement(elemName, target, isManaged = false) {
|
|
633
|
-
|
|
634
|
-
|
|
640
|
+
const elem = createScalarElement(elemName, 'cds.Association', false, undefined);
|
|
641
|
+
const assoc = elem[elemName];
|
|
635
642
|
assoc.target = target;
|
|
636
643
|
|
|
637
644
|
if (isManaged) {
|
|
638
645
|
assoc.keys = [];
|
|
639
|
-
|
|
646
|
+
const targetArt = getCsnDef(target);
|
|
640
647
|
targetArt.elements && Object.entries(targetArt.elements).forEach(([keyElemName, keyElem]) => {
|
|
641
648
|
if (keyElem.key) {
|
|
642
|
-
|
|
649
|
+
const foreignKey = createForeignKey(keyElemName, keyElem);
|
|
643
650
|
addForeignKey(foreignKey, assoc);
|
|
644
651
|
}
|
|
645
652
|
});
|
|
@@ -696,7 +703,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
696
703
|
if (!artifact.elements) {
|
|
697
704
|
throw new ModelError('Expecting artifact with elements: ' + JSON.stringify(artifact));
|
|
698
705
|
}
|
|
699
|
-
|
|
706
|
+
const elemName = Object.keys(elem)[0];
|
|
700
707
|
// Element must not exist
|
|
701
708
|
if (artifact.elements[elemName]) {
|
|
702
709
|
let path = null;
|
|
@@ -733,7 +740,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
733
740
|
error(null, path, { name: elementName }, 'Generated element $(NAME) conflicts with existing element');
|
|
734
741
|
}
|
|
735
742
|
|
|
736
|
-
|
|
743
|
+
const result = Object.create(null);
|
|
737
744
|
result[elementName] = {};
|
|
738
745
|
elem && Object.entries(elem).forEach(([prop, value]) => {
|
|
739
746
|
result[elementName][prop] = value;
|
|
@@ -746,13 +753,13 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
746
753
|
// of type name 'paramTypeName'
|
|
747
754
|
function createAction(actionName, returnTypeName = undefined, paramName = undefined, paramTypeName = undefined) {
|
|
748
755
|
// Assemble the action
|
|
749
|
-
|
|
756
|
+
const result = {
|
|
750
757
|
[actionName]: {
|
|
751
758
|
kind: 'action'
|
|
752
759
|
}
|
|
753
760
|
};
|
|
754
761
|
|
|
755
|
-
|
|
762
|
+
const action = result[actionName];
|
|
756
763
|
|
|
757
764
|
if (returnTypeName) {
|
|
758
765
|
if (!isBuiltinType(returnTypeName) && !model.definitions[returnTypeName])
|
|
@@ -787,7 +794,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
787
794
|
artifact.actions = Object.create(null);
|
|
788
795
|
}
|
|
789
796
|
|
|
790
|
-
|
|
797
|
+
const actionName = Object.keys(action)[0];
|
|
791
798
|
// Element must not exist
|
|
792
799
|
if (!artifact.actions[actionName]) {
|
|
793
800
|
// Add the action
|
|
@@ -804,7 +811,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
804
811
|
* second field if it has @cds.valid.to. Default value is [] for each field.
|
|
805
812
|
*/
|
|
806
813
|
function extractValidFromToKeyElement(element, path) {
|
|
807
|
-
|
|
814
|
+
const validFroms = [], validTos = [], validKeys = [];
|
|
808
815
|
if (hasAnnotationValue(element, '@cds.valid.from')) {
|
|
809
816
|
validFroms.push({ element, path: [...path] });
|
|
810
817
|
}
|
|
@@ -874,7 +881,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
874
881
|
*/
|
|
875
882
|
function recurseElements(artifact, path, callback) {
|
|
876
883
|
callback(artifact, path);
|
|
877
|
-
|
|
884
|
+
const elements = artifact.elements;
|
|
878
885
|
if (elements) {
|
|
879
886
|
path.push('elements', null);
|
|
880
887
|
forEach(elements, (name, obj) => {
|
|
@@ -888,7 +895,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
888
895
|
|
|
889
896
|
// Rename annotation 'fromName' in 'node' to 'toName' (both names including '@')
|
|
890
897
|
function renameAnnotation(node, fromName, toName) {
|
|
891
|
-
|
|
898
|
+
const annotation = node && node[fromName];
|
|
892
899
|
// Sanity checks
|
|
893
900
|
if (!fromName.startsWith('@')) {
|
|
894
901
|
throw new CompilerAssertion('Annotation name should start with "@": ' + fromName);
|
|
@@ -1035,7 +1042,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
1035
1042
|
if(art) {
|
|
1036
1043
|
if(art && !((art.items && art.items.elements) || art.elements)) {
|
|
1037
1044
|
if(followMgdAssoc && art.target && art.keys) {
|
|
1038
|
-
|
|
1045
|
+
const rc = [];
|
|
1039
1046
|
for(const k of art.keys) {
|
|
1040
1047
|
const nps = { ref: k.ref.map(p => fullRef ? { id: p } : p ) };
|
|
1041
1048
|
setProp(nps, '_art', k._art);
|
|
@@ -1053,7 +1060,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
1053
1060
|
}
|
|
1054
1061
|
const elements = art.items && art.items.elements || art.elements;
|
|
1055
1062
|
if(elements) {
|
|
1056
|
-
|
|
1063
|
+
const rc = []
|
|
1057
1064
|
Object.entries(elements).forEach(([en, elt]) => {
|
|
1058
1065
|
const nps = { ref: [ (fullRef ? { id: en, _art: elt } : en )] };
|
|
1059
1066
|
setProp(nps, '_art', elt);
|
|
@@ -1102,7 +1109,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
1102
1109
|
Flattening stops on all non-structured types.
|
|
1103
1110
|
*/
|
|
1104
1111
|
function expand(expr, location) {
|
|
1105
|
-
|
|
1112
|
+
const rc = [];
|
|
1106
1113
|
for(let i = 0; i < expr.length; i++)
|
|
1107
1114
|
{
|
|
1108
1115
|
if(Array.isArray(expr[i]))
|