@sap/cds-compiler 3.1.2 → 3.3.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 +80 -3
- package/bin/cdsc.js +1 -1
- package/doc/CHANGELOG_BETA.md +18 -0
- package/lib/api/main.js +8 -13
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +2 -24
- package/lib/base/message-registry.js +43 -14
- package/lib/base/messages.js +20 -10
- package/lib/base/model.js +1 -1
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/arrayOfs.js +15 -7
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +48 -0
- package/lib/checks/defaultValues.js +2 -2
- package/lib/checks/elements.js +81 -6
- package/lib/checks/foreignKeys.js +12 -13
- package/lib/checks/invalidTarget.js +10 -11
- package/lib/checks/managedInType.js +21 -15
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +9 -9
- package/lib/checks/parameters.js +21 -0
- package/lib/checks/selectItems.js +1 -1
- package/lib/checks/types.js +2 -2
- package/lib/checks/utils.js +17 -7
- package/lib/checks/validator.js +26 -14
- package/lib/compiler/assert-consistency.js +13 -6
- package/lib/compiler/builtins.js +8 -0
- package/lib/compiler/checks.js +40 -33
- package/lib/compiler/define.js +50 -44
- package/lib/compiler/extend.js +303 -37
- package/lib/compiler/kick-start.js +2 -35
- package/lib/compiler/populate.js +83 -62
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +61 -104
- package/lib/compiler/shared.js +16 -6
- package/lib/compiler/tweak-assocs.js +25 -12
- package/lib/compiler/utils.js +2 -2
- package/lib/edm/annotations/genericTranslation.js +3 -3
- package/lib/edm/csn2edm.js +10 -10
- package/lib/edm/edm.js +17 -9
- package/lib/edm/edmPreprocessor.js +53 -30
- package/lib/edm/edmUtils.js +7 -2
- package/lib/gen/Dictionary.json +14 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -2
- package/lib/gen/languageParser.js +4205 -4100
- package/lib/inspect/inspectModelStatistics.js +1 -1
- package/lib/inspect/inspectPropagation.js +23 -9
- package/lib/json/csnVersion.js +1 -1
- package/lib/json/from-csn.js +26 -19
- package/lib/json/to-csn.js +47 -5
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +29 -13
- package/lib/language/language.g4 +28 -8
- package/lib/main.d.ts +3 -6
- package/lib/model/.eslintrc.json +13 -0
- package/lib/model/api.js +4 -2
- package/lib/model/csnRefs.js +74 -47
- package/lib/model/csnUtils.js +236 -218
- package/lib/model/enrichCsn.js +41 -31
- package/lib/model/revealInternalProperties.js +61 -57
- package/lib/model/sortViews.js +31 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +5 -0
- package/lib/render/manageConstraints.js +2 -2
- package/lib/render/toCdl.js +31 -44
- package/lib/render/toHdbcds.js +7 -5
- package/lib/render/toRename.js +4 -4
- package/lib/render/toSql.js +11 -5
- package/lib/render/utils/common.js +20 -9
- package/lib/render/utils/sql.js +5 -5
- package/lib/transform/db/applyTransformations.js +32 -3
- package/lib/transform/db/expansion.js +81 -37
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +1 -1
- package/lib/transform/forOdataNew.js +10 -7
- package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
- package/lib/transform/localized.js +28 -19
- package/lib/transform/odata/toFinalBaseType.js +8 -11
- package/lib/transform/odata/typesExposure.js +1 -1
- package/lib/transform/transformUtilsNew.js +101 -39
- package/lib/transform/translateAssocsToJoins.js +5 -4
- package/lib/utils/moduleResolve.js +5 -5
- package/lib/utils/objectUtils.js +3 -3
- package/package.json +2 -2
- package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
- package/share/messages/check-proper-type-of.md +4 -4
- package/share/messages/check-proper-type.md +2 -2
- package/share/messages/duplicate-autoexposed.md +4 -4
- package/share/messages/extend-repeated-intralayer.md +4 -5
- package/share/messages/extend-unrelated-layer.md +4 -4
- package/share/messages/message-explanations.json +3 -1
- package/share/messages/redirected-to-ambiguous.md +7 -6
- package/share/messages/redirected-to-complex.md +63 -0
- package/share/messages/redirected-to-unrelated.md +6 -5
- package/share/messages/rewrite-not-supported.md +4 -4
- package/share/messages/syntax-expected-integer.md +3 -3
- package/share/messages/wildcard-excluding-one.md +37 -0
package/lib/model/csnUtils.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { csnRefs, implicitAs } = require('../model/csnRefs');
|
|
4
|
-
const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
|
|
5
|
-
const { isBuiltinType } = require('../compiler/builtins.js')
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
const { applyTransformations, applyTransformationsOnNonDictionary, applyTransformationsOnDictionary } = require('../transform/db/applyTransformations');
|
|
5
|
+
const { isBuiltinType } = require('../compiler/builtins.js');
|
|
6
|
+
const {
|
|
7
|
+
sortCsn,
|
|
8
|
+
cloneCsnDictionary: _cloneCsnDictionary,
|
|
9
|
+
cloneAnnotationValue: _cloneAnnotationValue,
|
|
10
|
+
} = require('../json/to-csn');
|
|
11
|
+
const { ModelError } = require('../base/error');
|
|
12
|
+
const { typeParameters } = require('../compiler/builtins');
|
|
9
13
|
const { forEach } = require('../utils/objectUtils');
|
|
10
|
-
const version = require('../../package.json')
|
|
14
|
+
const { version } = require('../../package.json');
|
|
11
15
|
|
|
12
16
|
// Low-level utility functions to work with compact CSN.
|
|
13
17
|
|
|
@@ -87,7 +91,7 @@ function getUtils(model, universalReady) {
|
|
|
87
91
|
* @param {boolean} [isSubquery]
|
|
88
92
|
* @returns {object} Map of sources
|
|
89
93
|
*/
|
|
90
|
-
function getSources(query, isSubquery=false) {
|
|
94
|
+
function getSources(query, isSubquery = false) {
|
|
91
95
|
// Remark CW: better just a while along query.SET.args[0]
|
|
92
96
|
if (query.SET) {
|
|
93
97
|
if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
|
|
@@ -102,15 +106,15 @@ function getUtils(model, universalReady) {
|
|
|
102
106
|
else if (query.SELECT.from.ref) {
|
|
103
107
|
let art = artifactRef(query.SELECT.from);
|
|
104
108
|
|
|
105
|
-
if(art.target)
|
|
109
|
+
if (art.target)
|
|
106
110
|
art = artifactRef(art.target);
|
|
107
111
|
|
|
108
|
-
if(isSubquery && !query.SELECT.elements)
|
|
112
|
+
if (isSubquery && !query.SELECT.elements)
|
|
109
113
|
throw new ModelError('Expected subquery to have .elements');
|
|
110
114
|
|
|
111
115
|
return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
|
|
117
|
+
query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
|
|
114
118
|
}
|
|
115
119
|
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
116
120
|
return getSources(query.SELECT.from, true);
|
|
@@ -163,18 +167,17 @@ function getUtils(model, universalReady) {
|
|
|
163
167
|
* @param {object} elements elements to merge into the map
|
|
164
168
|
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
165
169
|
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
166
|
-
* @param {any} [
|
|
170
|
+
* @param {any} [errorParent] Parent name to use for error messages, ref before alias
|
|
167
171
|
* @returns {object} existingMap
|
|
168
172
|
*/
|
|
169
|
-
function mergeElementsIntoMap(existingMap, elements, $location, parent,
|
|
173
|
+
function mergeElementsIntoMap(existingMap, elements, $location, parent, errorParent) {
|
|
170
174
|
for (const elementName in elements) {
|
|
171
175
|
const element = elements[elementName];
|
|
172
176
|
if (!existingMap[elementName])
|
|
173
177
|
existingMap[elementName] = [];
|
|
174
178
|
|
|
175
|
-
|
|
176
179
|
existingMap[elementName].push({
|
|
177
|
-
element, name: elementName, source: $location, parent: getBaseName(parent),
|
|
180
|
+
element, name: elementName, source: $location, parent: getBaseName(parent), errorParent,
|
|
178
181
|
});
|
|
179
182
|
}
|
|
180
183
|
|
|
@@ -190,9 +193,9 @@ function getUtils(model, universalReady) {
|
|
|
190
193
|
return name;
|
|
191
194
|
|
|
192
195
|
if (name.id)
|
|
193
|
-
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
196
|
+
return name.id.substring( name.id.lastIndexOf('.') + 1 );
|
|
194
197
|
|
|
195
|
-
return name.substring( name.lastIndexOf('.')+1 )
|
|
198
|
+
return name.substring( name.lastIndexOf('.') + 1 );
|
|
196
199
|
}
|
|
197
200
|
|
|
198
201
|
/**
|
|
@@ -202,17 +205,17 @@ function getUtils(model, universalReady) {
|
|
|
202
205
|
function getQueryPrimarySource(query) {
|
|
203
206
|
if (!query)
|
|
204
207
|
return undefined;
|
|
205
|
-
else if (query.SELECT)
|
|
208
|
+
else if (query.SELECT)
|
|
206
209
|
return getQueryPrimarySource(query.SELECT);
|
|
207
|
-
|
|
210
|
+
else if (query.SET)
|
|
208
211
|
return getQueryPrimarySource(query.SET);
|
|
209
|
-
|
|
212
|
+
else if (query.from)
|
|
210
213
|
return getQueryPrimarySource(query.from);
|
|
211
|
-
|
|
214
|
+
else if (query.ref)
|
|
212
215
|
return query;
|
|
213
|
-
|
|
216
|
+
else if (query.args)
|
|
214
217
|
return getQueryPrimarySource(query.args[0]);
|
|
215
|
-
|
|
218
|
+
|
|
216
219
|
return undefined;
|
|
217
220
|
}
|
|
218
221
|
|
|
@@ -222,7 +225,7 @@ function getUtils(model, universalReady) {
|
|
|
222
225
|
* @param {string} [initialId] Initial entry (optional)
|
|
223
226
|
*/
|
|
224
227
|
function createVisited(initialId) {
|
|
225
|
-
|
|
228
|
+
const visited = Object.create(null);
|
|
226
229
|
check(initialId);
|
|
227
230
|
return { check };
|
|
228
231
|
|
|
@@ -232,10 +235,11 @@ function getUtils(model, universalReady) {
|
|
|
232
235
|
* @param {string} id unique identifier
|
|
233
236
|
*/
|
|
234
237
|
function check(id) {
|
|
235
|
-
if (!id)
|
|
236
|
-
|
|
238
|
+
if (!id)
|
|
239
|
+
return;
|
|
240
|
+
if (visited[id])
|
|
237
241
|
throw new ModelError('Circular dependency');
|
|
238
|
-
|
|
242
|
+
|
|
239
243
|
visited[id] = true;
|
|
240
244
|
}
|
|
241
245
|
}
|
|
@@ -246,9 +250,8 @@ function getUtils(model, universalReady) {
|
|
|
246
250
|
*/
|
|
247
251
|
function getCsnDef(defName) {
|
|
248
252
|
if (model.definitions[defName])
|
|
249
|
-
return model.definitions[defName]
|
|
250
|
-
|
|
251
|
-
throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
|
|
253
|
+
return model.definitions[defName];
|
|
254
|
+
throw new ModelError(`Nonexistent definition in the model: '${ defName }'`);
|
|
252
255
|
}
|
|
253
256
|
|
|
254
257
|
/**
|
|
@@ -256,10 +259,10 @@ function getUtils(model, universalReady) {
|
|
|
256
259
|
* or a typedef of a structured type.
|
|
257
260
|
*
|
|
258
261
|
* @param {object} obj
|
|
262
|
+
* @returns {boolean}
|
|
259
263
|
*/
|
|
260
264
|
function isStructured(obj) {
|
|
261
|
-
return obj.elements ||
|
|
262
|
-
(obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseTypeWithProps(obj.type)?.elements)));
|
|
265
|
+
return !!(obj.elements || (obj.type && getFinalBaseTypeWithProps(obj.type)?.elements));
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
/**
|
|
@@ -270,11 +273,11 @@ function getUtils(model, universalReady) {
|
|
|
270
273
|
* @returns {object}
|
|
271
274
|
*/
|
|
272
275
|
function getFinalTypeDef(typeName) {
|
|
273
|
-
|
|
276
|
+
const visited = createVisited(typeName);
|
|
274
277
|
let type = model.definitions[typeName];
|
|
275
|
-
if (!type)
|
|
278
|
+
if (!type)
|
|
276
279
|
return typeName;
|
|
277
|
-
|
|
280
|
+
|
|
278
281
|
for (let nextType = type; nextType;) {
|
|
279
282
|
type = nextType;
|
|
280
283
|
visited.check(type.type);
|
|
@@ -289,7 +292,7 @@ function getUtils(model, universalReady) {
|
|
|
289
292
|
* @returns {string}
|
|
290
293
|
*/
|
|
291
294
|
function getFinalType(typeName) {
|
|
292
|
-
|
|
295
|
+
const visited = createVisited(typeName);
|
|
293
296
|
let type = model.definitions[typeName];
|
|
294
297
|
while (type && type.type) {
|
|
295
298
|
typeName = type.type;
|
|
@@ -313,7 +316,7 @@ function getUtils(model, universalReady) {
|
|
|
313
316
|
function isAssocOrComposition(typeName) {
|
|
314
317
|
if (typeName === 'cds.Association' || typeName === 'cds.Composition')
|
|
315
318
|
return true;
|
|
316
|
-
|
|
319
|
+
const visited = createVisited(typeName);
|
|
317
320
|
let type = model.definitions[typeName];
|
|
318
321
|
while (type) {
|
|
319
322
|
if (type.type === 'cds.Association' || type.type === 'cds.Composition')
|
|
@@ -331,7 +334,7 @@ function getUtils(model, universalReady) {
|
|
|
331
334
|
function isAssociation(typeName) {
|
|
332
335
|
if (typeName === 'cds.Association')
|
|
333
336
|
return true;
|
|
334
|
-
|
|
337
|
+
const visited = createVisited(typeName);
|
|
335
338
|
let type = model.definitions[typeName];
|
|
336
339
|
while (type) {
|
|
337
340
|
if (type.type === 'cds.Association')
|
|
@@ -349,7 +352,7 @@ function getUtils(model, universalReady) {
|
|
|
349
352
|
function isComposition(typeName) {
|
|
350
353
|
if (typeName === 'cds.Composition')
|
|
351
354
|
return true;
|
|
352
|
-
|
|
355
|
+
const visited = createVisited(typeName);
|
|
353
356
|
let type = model.definitions[typeName];
|
|
354
357
|
while (type) {
|
|
355
358
|
if (type.type === 'cds.Composition')
|
|
@@ -366,12 +369,14 @@ function getUtils(model, universalReady) {
|
|
|
366
369
|
*/
|
|
367
370
|
function getNamespaceOfArtifact(name) {
|
|
368
371
|
let lastDotIdx = name.lastIndexOf('.');
|
|
369
|
-
if (lastDotIdx === -1)
|
|
372
|
+
if (lastDotIdx === -1)
|
|
373
|
+
return undefined;
|
|
370
374
|
while (model.definitions[name]) {
|
|
371
375
|
if (model.definitions[name].kind === 'namespace')
|
|
372
376
|
return name;
|
|
373
377
|
lastDotIdx = name.lastIndexOf('.');
|
|
374
|
-
if (lastDotIdx === -1)
|
|
378
|
+
if (lastDotIdx === -1)
|
|
379
|
+
return undefined;
|
|
375
380
|
name = name.substring(0, lastDotIdx);
|
|
376
381
|
}
|
|
377
382
|
return name;
|
|
@@ -387,7 +392,8 @@ function getUtils(model, universalReady) {
|
|
|
387
392
|
if (model.definitions[name].kind === 'context' || model.definitions[name].kind === 'service')
|
|
388
393
|
return name;
|
|
389
394
|
lastDotIdx = name.lastIndexOf('.');
|
|
390
|
-
if (lastDotIdx === -1)
|
|
395
|
+
if (lastDotIdx === -1)
|
|
396
|
+
return undefined;
|
|
391
397
|
name = name.substring(0, lastDotIdx);
|
|
392
398
|
}
|
|
393
399
|
return undefined;
|
|
@@ -402,11 +408,11 @@ function getUtils(model, universalReady) {
|
|
|
402
408
|
*/
|
|
403
409
|
function addStringAnnotationTo(absoluteName, theValue, node) {
|
|
404
410
|
// Sanity check
|
|
405
|
-
if (!absoluteName.startsWith('@'))
|
|
406
|
-
throw Error(
|
|
407
|
-
|
|
411
|
+
if (!absoluteName.startsWith('@'))
|
|
412
|
+
throw Error(`Annotation name should start with "@": ${ absoluteName }`);
|
|
413
|
+
|
|
408
414
|
// Only overwrite if undefined or null
|
|
409
|
-
if(node[absoluteName] === undefined || node[absoluteName] === null) {
|
|
415
|
+
if (node[absoluteName] === undefined || node[absoluteName] === null) {
|
|
410
416
|
// Assemble the annotation
|
|
411
417
|
node[absoluteName] = theValue;
|
|
412
418
|
}
|
|
@@ -420,14 +426,14 @@ function getUtils(model, universalReady) {
|
|
|
420
426
|
* @returns {string|null}
|
|
421
427
|
*/
|
|
422
428
|
function getServiceName(artifactName) {
|
|
423
|
-
for(;;) {
|
|
424
|
-
|
|
425
|
-
if (idx === -1)
|
|
429
|
+
for (;;) {
|
|
430
|
+
const idx = artifactName.lastIndexOf('.');
|
|
431
|
+
if (idx === -1)
|
|
432
|
+
return null;
|
|
426
433
|
artifactName = artifactName.substring(0, idx);
|
|
427
|
-
|
|
428
|
-
if (artifact && artifact.kind === 'service')
|
|
434
|
+
const artifact = model.definitions[artifactName];
|
|
435
|
+
if (artifact && artifact.kind === 'service')
|
|
429
436
|
return artifactName;
|
|
430
|
-
}
|
|
431
437
|
}
|
|
432
438
|
}
|
|
433
439
|
|
|
@@ -450,35 +456,33 @@ function getUtils(model, universalReady) {
|
|
|
450
456
|
* @returns {object}
|
|
451
457
|
*/
|
|
452
458
|
function cloneWithTransformations(rootNode, transformers) {
|
|
453
|
-
|
|
454
459
|
return transformNode(rootNode);
|
|
455
460
|
|
|
456
|
-
|
|
461
|
+
// This general transformation function will be applied to each node recursively
|
|
457
462
|
function transformNode(node) {
|
|
458
463
|
// Return primitive values and null unchanged, but let objects and dictionaries through
|
|
459
464
|
// (Note that 'node instanceof Object' would be false for dictionaries).
|
|
460
|
-
if (node === null || typeof node !== 'object')
|
|
461
|
-
return node
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (node
|
|
465
|
+
if (node === null || typeof node !== 'object')
|
|
466
|
+
return node;
|
|
467
|
+
|
|
468
|
+
// Simply return if node is to be ignored
|
|
469
|
+
if (node === undefined || node._ignore)
|
|
465
470
|
return undefined;
|
|
466
|
-
|
|
467
|
-
if (Array.isArray(node))
|
|
471
|
+
// Transform arrays element-wise
|
|
472
|
+
if (Array.isArray(node))
|
|
468
473
|
return node.map(transformNode);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
for (
|
|
474
|
+
|
|
475
|
+
// Things not having 'proto' are dictionaries
|
|
476
|
+
const proto = Object.getPrototypeOf(node);
|
|
477
|
+
// Iterate own properties of 'node' and transform them into 'resultNode'
|
|
478
|
+
const resultNode = Object.create(proto);
|
|
479
|
+
for (const key of Object.keys(node)) {
|
|
475
480
|
// Dictionary always use transformNode(), other objects their transformer according to key
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
if (resultValue !== undefined)
|
|
481
|
+
const transformer = (proto === null || proto === undefined) ? transformNode : transformers[key] || transformers[key.charAt(0)];
|
|
482
|
+
// Apply transformer, or use transformNode() if there is none
|
|
483
|
+
const resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
|
|
484
|
+
if (resultValue !== undefined)
|
|
480
485
|
resultNode[key] = resultValue;
|
|
481
|
-
}
|
|
482
486
|
}
|
|
483
487
|
return resultNode;
|
|
484
488
|
}
|
|
@@ -514,21 +518,21 @@ function getUtils(model, universalReady) {
|
|
|
514
518
|
return null;
|
|
515
519
|
|
|
516
520
|
// Nothing to copy from builtin type name.
|
|
517
|
-
if (typeof type === 'string' && isBuiltinType( type ))
|
|
521
|
+
if (typeof type === 'string' && isBuiltinType( type ))
|
|
518
522
|
return { type };
|
|
519
|
-
|
|
523
|
+
|
|
520
524
|
|
|
521
525
|
// We differentiate between ref and type to avoid collisions due to dict key.
|
|
522
526
|
// Delimiter chosen arbitrarily; just one that is rarely used.
|
|
523
|
-
const resolvedKey = (typeof type === 'object') ? `ref[${type.ref.length}]:${type.ref.join('\\')}` : `type:${type}`;
|
|
527
|
+
const resolvedKey = (typeof type === 'object') ? `ref[${ type.ref.length }]:${ type.ref.join('\\') }` : `type:${ type }`;
|
|
524
528
|
|
|
525
529
|
if (finalBaseTypeCache[resolvedKey]) {
|
|
526
530
|
if (finalBaseTypeCache[resolvedKey] === true)
|
|
527
|
-
throw new ModelError(`Detected circular type reference; can't resolve: ${resolvedKey}`);
|
|
531
|
+
throw new ModelError(`Detected circular type reference; can't resolve: ${ resolvedKey }`);
|
|
528
532
|
return finalBaseTypeCache[resolvedKey];
|
|
529
533
|
}
|
|
530
534
|
|
|
531
|
-
|
|
535
|
+
const typeRef = artifactRef(type); // throws if not found
|
|
532
536
|
const isNonScalar = _cacheNonScalar(typeRef);
|
|
533
537
|
if (isNonScalar)
|
|
534
538
|
return finalBaseTypeCache[resolvedKey];
|
|
@@ -597,9 +601,8 @@ function getUtils(model, universalReady) {
|
|
|
597
601
|
target.type = source.type;
|
|
598
602
|
const typeProps = [ ...typeParameters.list, 'enum', 'default', 'localized' ];
|
|
599
603
|
for (const param of typeProps) {
|
|
600
|
-
if (target[param] === undefined && source[param] !== undefined)
|
|
604
|
+
if (target[param] === undefined && source[param] !== undefined)
|
|
601
605
|
target[param] = source[param];
|
|
602
|
-
}
|
|
603
606
|
}
|
|
604
607
|
return target;
|
|
605
608
|
}
|
|
@@ -609,7 +612,12 @@ function getUtils(model, universalReady) {
|
|
|
609
612
|
/**
|
|
610
613
|
* Deeply clone the given CSN model and return it.
|
|
611
614
|
* In testMode (or with testSortCsn), definitions are sorted.
|
|
612
|
-
*
|
|
615
|
+
*
|
|
616
|
+
* This function is CSN aware! Don't put annotation values into it, or
|
|
617
|
+
* keys such as "elements" will be interpreted according to CSN rules!
|
|
618
|
+
*
|
|
619
|
+
* @see cloneAnnotationValue
|
|
620
|
+
* @see cloneCsnDictionary
|
|
613
621
|
*
|
|
614
622
|
* @param {object} csn Top-level CSN. You can pass non-dictionary values.
|
|
615
623
|
* @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
|
|
@@ -620,18 +628,34 @@ function cloneCsnNonDict(csn, options) {
|
|
|
620
628
|
|
|
621
629
|
/**
|
|
622
630
|
* Deeply clone the given CSN dictionary and return it.
|
|
623
|
-
* Note that annotations are only copied shallowly.
|
|
624
631
|
* This function does _not_ sort the given dictionary.
|
|
625
632
|
* See cloneCsnNonDict() if you want sorted definitions.
|
|
626
633
|
*
|
|
634
|
+
* This function is CSN aware! Don't put annotation values into it, or
|
|
635
|
+
* keys such as "elements" will be interpreted according to CSN rules!
|
|
636
|
+
*
|
|
637
|
+
* @see cloneAnnotationValue
|
|
638
|
+
* @see cloneCsnNonDict
|
|
639
|
+
*
|
|
627
640
|
* @param {object} csn
|
|
628
641
|
* @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
|
|
629
|
-
* used and cloneOptions are passed to
|
|
642
|
+
* used and cloneOptions are passed to sortCsn().
|
|
630
643
|
*/
|
|
631
644
|
function cloneCsnDictionary(csn, options) {
|
|
632
645
|
return _cloneCsnDictionary(csn, options);
|
|
633
646
|
}
|
|
634
647
|
|
|
648
|
+
/**
|
|
649
|
+
* Clones the given annotation _value_. `value` must not be an object with annotations,
|
|
650
|
+
* but the annotation value itself, e.g. `[ { a: 1 } ]`, not `@anno: [...]`.
|
|
651
|
+
*
|
|
652
|
+
* @param {any} value
|
|
653
|
+
* @returns {any}
|
|
654
|
+
*/
|
|
655
|
+
function cloneAnnotationValue(value) {
|
|
656
|
+
return _cloneAnnotationValue( value );
|
|
657
|
+
}
|
|
658
|
+
|
|
635
659
|
/**
|
|
636
660
|
* Apply function `callback` to all artifacts in dictionary
|
|
637
661
|
* `model.definitions`. See function `forEachGeneric` for details.
|
|
@@ -660,28 +684,28 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
|
|
|
660
684
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
661
685
|
* @param constructCallback
|
|
662
686
|
*/
|
|
663
|
-
function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
687
|
+
function forEachMember( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
|
|
664
688
|
constructCallback = (_construct, _prop, _path) => {}) {
|
|
665
689
|
// Allow processing _ignored elements if requested
|
|
666
|
-
if (ignoreIgnore && construct._ignore)
|
|
690
|
+
if (ignoreIgnore && construct._ignore)
|
|
667
691
|
return;
|
|
668
|
-
|
|
692
|
+
|
|
669
693
|
|
|
670
694
|
// `items` itself is a structure that can contain "elements", and more.
|
|
671
|
-
if (construct.items)
|
|
672
|
-
forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions, constructCallback );
|
|
673
|
-
|
|
695
|
+
if (construct.items)
|
|
696
|
+
forEachMember( construct.items, callback, [ ...path, 'items' ], ignoreIgnore, iterateOptions, constructCallback );
|
|
697
|
+
|
|
674
698
|
|
|
675
699
|
// Unlike XSN, we don't make "returns" a "params" in the callback.
|
|
676
700
|
// Backends rely on the fact that `forEachElement` also goes through all
|
|
677
701
|
// `elements` of the return type (if structured).
|
|
678
702
|
// TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
|
|
679
|
-
if (construct.returns && !iterateOptions.elementsOnly)
|
|
680
|
-
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions, constructCallback );
|
|
681
|
-
}
|
|
703
|
+
if (construct.returns && !iterateOptions.elementsOnly)
|
|
704
|
+
forEachMember( construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback );
|
|
682
705
|
|
|
683
|
-
|
|
684
|
-
|
|
706
|
+
|
|
707
|
+
path = [ ...path ]; // Copy
|
|
708
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? [ 'elements' ] : [ 'elements', 'enum', 'actions', 'params' ]);
|
|
685
709
|
propsWithMembers.forEach((prop) => {
|
|
686
710
|
forEachGeneric( construct, prop, callback, path, iterateOptions );
|
|
687
711
|
if (construct[prop]) {
|
|
@@ -703,15 +727,14 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
703
727
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
704
728
|
* @param {constructCallback|constructCallback[]} callback
|
|
705
729
|
*/
|
|
706
|
-
function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
707
|
-
|
|
730
|
+
function forEachMemberWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
|
|
731
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
708
732
|
forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
709
733
|
if (construct.query) {
|
|
710
734
|
forAllQueries(construct.query, (q, p) => {
|
|
711
735
|
const s = q.SELECT;
|
|
712
|
-
if(s)
|
|
736
|
+
if (s)
|
|
713
737
|
forEachMember(s, callback, p, ignoreIgnore, iterateOptions);
|
|
714
|
-
}
|
|
715
738
|
}, [ ...path, 'query' ]);
|
|
716
739
|
}
|
|
717
740
|
}
|
|
@@ -727,10 +750,10 @@ function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true
|
|
|
727
750
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
728
751
|
* @param {constructCallback|constructCallback[]} callback
|
|
729
752
|
*/
|
|
730
|
-
function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
753
|
+
function forEachMemberRecursively( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
|
|
731
754
|
constructCallback = (_construct, _prop, _path) => {}) {
|
|
732
755
|
forEachMember( construct, ( member, memberName, prop, subpath, parent ) => {
|
|
733
|
-
if(Array.isArray(callback))
|
|
756
|
+
if (Array.isArray(callback))
|
|
734
757
|
callback.forEach(cb => cb( member, memberName, prop, subpath, parent ));
|
|
735
758
|
else
|
|
736
759
|
callback( member, memberName, prop, subpath, parent );
|
|
@@ -751,15 +774,14 @@ function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=tr
|
|
|
751
774
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
752
775
|
* @param {constructCallback|constructCallback[]} callback
|
|
753
776
|
*/
|
|
754
|
-
function forEachMemberRecursivelyWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
755
|
-
|
|
777
|
+
function forEachMemberRecursivelyWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
|
|
778
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
756
779
|
forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
757
|
-
if(construct.query) {
|
|
780
|
+
if (construct.query) {
|
|
758
781
|
forAllQueries(construct.query, (q, p) => {
|
|
759
782
|
const s = q.SELECT;
|
|
760
|
-
if(s)
|
|
783
|
+
if (s)
|
|
761
784
|
forEachMemberRecursively(s, callback, p, ignoreIgnore, iterateOptions);
|
|
762
|
-
}
|
|
763
785
|
}, [ ...path, 'query' ]);
|
|
764
786
|
}
|
|
765
787
|
}
|
|
@@ -782,9 +804,9 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
|
|
|
782
804
|
if (!Object.prototype.hasOwnProperty.call(dict, name))
|
|
783
805
|
continue;
|
|
784
806
|
const dictObj = dict[name];
|
|
785
|
-
if((iterateOptions.skip && iterateOptions.skip.includes(dictObj.kind))
|
|
786
|
-
|
|
787
|
-
|
|
807
|
+
if ((iterateOptions.skip && iterateOptions.skip.includes(dictObj.kind)) ||
|
|
808
|
+
(iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function' &&
|
|
809
|
+
iterateOptions.skipArtifact(dictObj, name)))
|
|
788
810
|
continue;
|
|
789
811
|
executeCallbacks( dictObj, name );
|
|
790
812
|
}
|
|
@@ -794,7 +816,7 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
|
|
|
794
816
|
if (Array.isArray(callback))
|
|
795
817
|
callback.forEach(cb => cb( o, name, prop, path.concat(p), construct ));
|
|
796
818
|
else
|
|
797
|
-
callback( o, name, prop, path.concat(p), construct )
|
|
819
|
+
callback( o, name, prop, path.concat(p), construct );
|
|
798
820
|
}
|
|
799
821
|
}
|
|
800
822
|
|
|
@@ -803,13 +825,13 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
|
|
|
803
825
|
* @param {queryCallback|queryCallback[]} queryCallback
|
|
804
826
|
* @param {CSN.Path} path
|
|
805
827
|
*/
|
|
806
|
-
function forAllQueries(mainQuery, queryCallback, path = []){
|
|
828
|
+
function forAllQueries(mainQuery, queryCallback, path = []) {
|
|
807
829
|
return traverseQuery(mainQuery, queryCallback, path);
|
|
808
830
|
function traverseQuery( query, callback, queryPath ) {
|
|
809
831
|
if (query.SELECT) {
|
|
810
832
|
// The projection is turned into a normalized query - there
|
|
811
833
|
// is no real SELECT, it is fake
|
|
812
|
-
if(!(path.length === 3 && path[2] === 'projection'))
|
|
834
|
+
if (!(path.length === 3 && path[2] === 'projection'))
|
|
813
835
|
queryPath.push('SELECT');
|
|
814
836
|
executeCallbacks();
|
|
815
837
|
query = query.SELECT;
|
|
@@ -821,25 +843,24 @@ function forAllQueries(mainQuery, queryCallback, path = []){
|
|
|
821
843
|
}
|
|
822
844
|
|
|
823
845
|
if (query.from)
|
|
824
|
-
traverseFrom( query.from, callback, queryPath.concat(['from']) );
|
|
846
|
+
traverseFrom( query.from, callback, queryPath.concat([ 'from' ]) );
|
|
825
847
|
|
|
826
|
-
for (const prop of ['args', 'xpr', 'columns', 'where', 'having']) {
|
|
848
|
+
for (const prop of [ 'args', 'xpr', 'columns', 'where', 'having' ]) {
|
|
827
849
|
// all properties which could have sub queries (directly or indirectly)
|
|
828
850
|
const expr = query[prop];
|
|
829
851
|
if (expr && typeof expr === 'object') {
|
|
830
|
-
if(Array.isArray(expr)){
|
|
831
|
-
for(let i = 0; i < expr.length; i++)
|
|
832
|
-
traverseQuery(expr[i], callback, queryPath.concat([prop, i]));
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
for(const argName of Object.keys( expr ))
|
|
836
|
-
traverseQuery(expr[argName], callback, queryPath.concat([prop, argName]))
|
|
837
|
-
}
|
|
852
|
+
if (Array.isArray(expr)) {
|
|
853
|
+
for (let i = 0; i < expr.length; i++)
|
|
854
|
+
traverseQuery(expr[i], callback, queryPath.concat([ prop, i ]));
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
for (const argName of Object.keys( expr ))
|
|
858
|
+
traverseQuery(expr[argName], callback, queryPath.concat([ prop, argName ]));
|
|
838
859
|
}
|
|
839
860
|
}
|
|
840
861
|
}
|
|
841
862
|
function executeCallbacks() {
|
|
842
|
-
if(Array.isArray(callback))
|
|
863
|
+
if (Array.isArray(callback))
|
|
843
864
|
callback.forEach(cb => cb( query, queryPath ));
|
|
844
865
|
else
|
|
845
866
|
callback( query, queryPath );
|
|
@@ -852,15 +873,16 @@ function forAllQueries(mainQuery, queryCallback, path = []){
|
|
|
852
873
|
* @param {CSN.Path} csnPath
|
|
853
874
|
*/
|
|
854
875
|
function traverseFrom( from, callback, csnPath = [] ) {
|
|
855
|
-
if (from.ref) // ignore
|
|
876
|
+
if (from.ref) { // ignore
|
|
856
877
|
return;
|
|
857
|
-
else if (from.args){ // join
|
|
858
|
-
for(let i = 0; i < from.args.length; i++){
|
|
859
|
-
traverseFrom(from.args[i], callback, csnPath.concat(['args', i]));
|
|
860
|
-
}
|
|
861
878
|
}
|
|
862
|
-
else
|
|
863
|
-
|
|
879
|
+
else if (from.args) { // join
|
|
880
|
+
for (let i = 0; i < from.args.length; i++)
|
|
881
|
+
traverseFrom(from.args[i], callback, csnPath.concat([ 'args', i ]));
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
traverseQuery( from, callback, csnPath );
|
|
885
|
+
} // sub query in FROM
|
|
864
886
|
}
|
|
865
887
|
}
|
|
866
888
|
|
|
@@ -886,12 +908,11 @@ function forAllQueries(mainQuery, queryCallback, path = []){
|
|
|
886
908
|
* @returns {boolean}
|
|
887
909
|
*/
|
|
888
910
|
function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
|
|
889
|
-
if(expected === false)
|
|
911
|
+
if (expected === false)
|
|
890
912
|
return artifact[annotationName] === expected || artifact[annotationName] === null;
|
|
891
913
|
else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
|
|
892
914
|
return artifact[annotationName].toLowerCase() === expected.toLowerCase();
|
|
893
|
-
|
|
894
|
-
return artifact[annotationName] === expected;
|
|
915
|
+
return artifact[annotationName] === expected;
|
|
895
916
|
}
|
|
896
917
|
|
|
897
918
|
/**
|
|
@@ -910,11 +931,11 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
910
931
|
// V4 struct: on/off
|
|
911
932
|
const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
|
|
912
933
|
const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
|
|
913
|
-
const isNavigable = elementCsn.target
|
|
914
|
-
(elementCsn['@odata.navigable'] === undefined ||
|
|
934
|
+
const isNavigable = elementCsn.target
|
|
935
|
+
? (elementCsn['@odata.navigable'] === undefined ||
|
|
915
936
|
elementCsn['@odata.navigable'] !== undefined && (elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
|
|
916
937
|
// Foreign Keys can be ignored
|
|
917
|
-
if(elementCsn['@odata.foreignKey4'])
|
|
938
|
+
if (elementCsn['@odata.foreignKey4'])
|
|
918
939
|
return isNotIgnored && renderForeignKey;
|
|
919
940
|
// ordinary elements can be ignored and isNavigable is always true for them
|
|
920
941
|
// assocs cannot be ignored but not navigable
|
|
@@ -941,18 +962,18 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
941
962
|
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
|
|
942
963
|
*/
|
|
943
964
|
// eslint-disable-next-line no-unused-vars
|
|
944
|
-
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect='plain') {
|
|
945
|
-
if(csn && typeof csn === 'object' && csn.definitions) {
|
|
946
|
-
isValidMappingDialectCombi(sqlDialect, sqlMapping)
|
|
947
|
-
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds')
|
|
965
|
+
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect = 'plain') {
|
|
966
|
+
if (csn && typeof csn === 'object' && csn.definitions) {
|
|
967
|
+
isValidMappingDialectCombi(sqlDialect, sqlMapping);
|
|
968
|
+
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds')
|
|
948
969
|
return getResultingName(csn, sqlMapping, artifactName);
|
|
949
|
-
|
|
950
|
-
else if (sqlMapping === 'plain')
|
|
970
|
+
|
|
971
|
+
else if (sqlMapping === 'plain')
|
|
951
972
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
973
|
+
|
|
974
|
+
throw new Error(`Unknown naming mode: ${ sqlMapping }`);
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
956
977
|
throw new Error('A valid CSN model is required to correctly calculate the database name of an artifact.');
|
|
957
978
|
}
|
|
958
979
|
}
|
|
@@ -988,7 +1009,7 @@ function getResultingName(csn, namingMode, artifactName) {
|
|
|
988
1009
|
const name = realParts ? realParts.join('.') : artifactName;
|
|
989
1010
|
|
|
990
1011
|
|
|
991
|
-
return (namespace && namingMode === 'hdbcds') ? `${namespace}::${name.slice(namespace.length + 1)}` : name;
|
|
1012
|
+
return (namespace && namingMode === 'hdbcds') ? `${ namespace }::${ name.slice(namespace.length + 1) }` : name;
|
|
992
1013
|
}
|
|
993
1014
|
|
|
994
1015
|
|
|
@@ -1019,7 +1040,8 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1019
1040
|
result.push(suffix);
|
|
1020
1041
|
|
|
1021
1042
|
return result;
|
|
1022
|
-
}
|
|
1043
|
+
}
|
|
1044
|
+
else if (art && art.kind === 'service') {
|
|
1023
1045
|
// inside services, we immediately turn . into _
|
|
1024
1046
|
const prefix = parts.slice(0, i).join('.');
|
|
1025
1047
|
const suffix = parts.slice(i).join('_');
|
|
@@ -1037,8 +1059,8 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1037
1059
|
}
|
|
1038
1060
|
|
|
1039
1061
|
function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
|
|
1040
|
-
if(sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
|
|
1041
|
-
throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${sqlDialect}`);
|
|
1062
|
+
if (sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
|
|
1063
|
+
throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${ sqlDialect }`);
|
|
1042
1064
|
return true;
|
|
1043
1065
|
}
|
|
1044
1066
|
|
|
@@ -1060,20 +1082,18 @@ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
|
|
|
1060
1082
|
* @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
|
|
1061
1083
|
*/
|
|
1062
1084
|
// eslint-disable-next-line no-unused-vars
|
|
1063
|
-
function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
|
|
1064
|
-
isValidMappingDialectCombi(sqlDialect, sqlMapping)
|
|
1065
|
-
if (sqlMapping === 'hdbcds')
|
|
1085
|
+
function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect = 'plain') {
|
|
1086
|
+
isValidMappingDialectCombi(sqlDialect, sqlMapping);
|
|
1087
|
+
if (sqlMapping === 'hdbcds')
|
|
1066
1088
|
return elemName;
|
|
1067
|
-
|
|
1068
|
-
else if (sqlMapping === 'plain')
|
|
1089
|
+
|
|
1090
|
+
else if (sqlMapping === 'plain')
|
|
1069
1091
|
return elemName.replace(/\./g, '_').toUpperCase();
|
|
1070
|
-
|
|
1071
|
-
else if (sqlMapping === 'quoted')
|
|
1092
|
+
|
|
1093
|
+
else if (sqlMapping === 'quoted')
|
|
1072
1094
|
return elemName.replace(/\./g, '_');
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
1076
|
-
}
|
|
1095
|
+
|
|
1096
|
+
throw new Error(`Unknown naming mode: ${ sqlMapping }`);
|
|
1077
1097
|
}
|
|
1078
1098
|
|
|
1079
1099
|
const _dependencies = Symbol('_dependencies');
|
|
@@ -1094,30 +1114,30 @@ function setDependencies( csn ) {
|
|
|
1094
1114
|
const { artifactRef } = csnRefs(csn);
|
|
1095
1115
|
|
|
1096
1116
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
1097
|
-
if(getNormalizedQuery(artifact).query) {
|
|
1117
|
+
if (getNormalizedQuery(artifact).query) {
|
|
1098
1118
|
initDependencies(artifact);
|
|
1099
1119
|
forAllQueries(getNormalizedQuery(artifact).query, (query) => {
|
|
1100
|
-
if(query.SELECT && query.SELECT.from) {
|
|
1101
|
-
if(query.SELECT.from.args)
|
|
1120
|
+
if (query.SELECT && query.SELECT.from) {
|
|
1121
|
+
if (query.SELECT.from.args)
|
|
1102
1122
|
handleArgs(artifact, artifactName, query.SELECT.from.args);
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
}
|
|
1123
|
+
|
|
1124
|
+
else if (typeof query.SELECT.from === 'string' || query.SELECT.from.ref )
|
|
1125
|
+
handleDependency(artifactRef(query.SELECT.from), artifact, artifactName);
|
|
1107
1126
|
}
|
|
1108
|
-
},
|
|
1127
|
+
}, [ 'definitions', artifactName, (artifact.projection ? 'projection' : 'query') ]);
|
|
1109
1128
|
}
|
|
1110
|
-
})
|
|
1129
|
+
});
|
|
1111
1130
|
|
|
1112
|
-
return {
|
|
1131
|
+
return {
|
|
1132
|
+
cleanup, csn, _dependents, _dependencies,
|
|
1133
|
+
};
|
|
1113
1134
|
|
|
1114
|
-
function handleArgs(artifact, artifactName, args){
|
|
1115
|
-
for(
|
|
1116
|
-
if (arg.args)
|
|
1135
|
+
function handleArgs(artifact, artifactName, args) {
|
|
1136
|
+
for (const arg of args) {
|
|
1137
|
+
if (arg.args)
|
|
1117
1138
|
handleArgs(artifact, artifactName, arg.args);
|
|
1118
|
-
|
|
1119
|
-
handleDependency(artifactRef(arg), artifact, artifactName)
|
|
1120
|
-
}
|
|
1139
|
+
else if (arg.ref)
|
|
1140
|
+
handleDependency(artifactRef(arg), artifact, artifactName);
|
|
1121
1141
|
}
|
|
1122
1142
|
}
|
|
1123
1143
|
|
|
@@ -1127,15 +1147,15 @@ function setDependencies( csn ) {
|
|
|
1127
1147
|
dependency[_dependents][dependantName] = dependant;
|
|
1128
1148
|
}
|
|
1129
1149
|
|
|
1130
|
-
function initDependents(obj){
|
|
1131
|
-
if(!obj[_dependents]) {
|
|
1150
|
+
function initDependents(obj) {
|
|
1151
|
+
if (!obj[_dependents]) {
|
|
1132
1152
|
obj[_dependents] = Object.create(null);
|
|
1133
1153
|
cleanup.push(() => delete obj[_dependents]);
|
|
1134
1154
|
}
|
|
1135
1155
|
}
|
|
1136
1156
|
|
|
1137
|
-
function initDependencies(obj){
|
|
1138
|
-
if(!obj[_dependencies]) {
|
|
1157
|
+
function initDependencies(obj) {
|
|
1158
|
+
if (!obj[_dependencies]) {
|
|
1139
1159
|
obj[_dependencies] = new Set();
|
|
1140
1160
|
cleanup.push(() => delete obj[_dependencies]);
|
|
1141
1161
|
}
|
|
@@ -1150,7 +1170,7 @@ function setDependencies( csn ) {
|
|
|
1150
1170
|
* @returns {boolean}
|
|
1151
1171
|
*/
|
|
1152
1172
|
function isPersistedOnDatabase(art) {
|
|
1153
|
-
return !('entity'
|
|
1173
|
+
return !(art.kind === 'entity' && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
|
|
1154
1174
|
}
|
|
1155
1175
|
|
|
1156
1176
|
/**
|
|
@@ -1160,7 +1180,7 @@ function isPersistedOnDatabase(art) {
|
|
|
1160
1180
|
* @returns {string} String containing compiler version that was used to generate content
|
|
1161
1181
|
*/
|
|
1162
1182
|
function generatedByCompilerVersion() {
|
|
1163
|
-
return `generated by cds-compiler version ${version}`;
|
|
1183
|
+
return `generated by cds-compiler version ${ version }`;
|
|
1164
1184
|
}
|
|
1165
1185
|
|
|
1166
1186
|
/**
|
|
@@ -1170,9 +1190,9 @@ function generatedByCompilerVersion() {
|
|
|
1170
1190
|
* @returns {object} Object with a query property.
|
|
1171
1191
|
*/
|
|
1172
1192
|
function getNormalizedQuery(art) {
|
|
1173
|
-
if (art.projection)
|
|
1193
|
+
if (art.projection)
|
|
1174
1194
|
return { query: { SELECT: art.projection } };
|
|
1175
|
-
|
|
1195
|
+
|
|
1176
1196
|
return art;
|
|
1177
1197
|
}
|
|
1178
1198
|
|
|
@@ -1196,14 +1216,14 @@ function getRootArtifactName(artifactName, csn) {
|
|
|
1196
1216
|
if (seen === '')
|
|
1197
1217
|
seen = parts[i];
|
|
1198
1218
|
else
|
|
1199
|
-
|
|
1219
|
+
seen = `${ seen }.${ parts[i] }`;
|
|
1200
1220
|
|
|
1201
1221
|
const art = csn.definitions[seen];
|
|
1202
|
-
|
|
1222
|
+
// Our artifact seems to be contained in this context
|
|
1203
1223
|
if (art && (art.kind === 'context' || art.kind === 'service'))
|
|
1204
1224
|
return seen;
|
|
1205
1225
|
}
|
|
1206
|
-
|
|
1226
|
+
// Our artifact is a root artifact itself
|
|
1207
1227
|
return seen;
|
|
1208
1228
|
}
|
|
1209
1229
|
|
|
@@ -1223,7 +1243,7 @@ function getLastPartOf(name) {
|
|
|
1223
1243
|
// ['foo'] => 'foo';
|
|
1224
1244
|
// ['foo::bar'] => 'bar'
|
|
1225
1245
|
function getLastPartOfRef(ref) {
|
|
1226
|
-
|
|
1246
|
+
const lastPathStep = ref[ref.length - 1];
|
|
1227
1247
|
return getLastPartOf(lastPathStep.id || lastPathStep);
|
|
1228
1248
|
}
|
|
1229
1249
|
|
|
@@ -1244,9 +1264,8 @@ function copyAnnotations(fromNode, toNode, overwrite = false) {
|
|
|
1244
1264
|
const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
|
|
1245
1265
|
|
|
1246
1266
|
for (const anno of annotations) {
|
|
1247
|
-
if (toNode[anno] === undefined || overwrite)
|
|
1267
|
+
if (toNode[anno] === undefined || overwrite)
|
|
1248
1268
|
toNode[anno] = fromNode[anno];
|
|
1249
|
-
}
|
|
1250
1269
|
}
|
|
1251
1270
|
}
|
|
1252
1271
|
|
|
@@ -1270,9 +1289,8 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
|
1270
1289
|
.filter(key => key.startsWith('@') || key === 'doc');
|
|
1271
1290
|
|
|
1272
1291
|
for (const anno of annotations) {
|
|
1273
|
-
if (toNode[anno] === undefined || overwrite)
|
|
1292
|
+
if (toNode[anno] === undefined || overwrite)
|
|
1274
1293
|
toNode[anno] = fromNode[anno];
|
|
1275
|
-
}
|
|
1276
1294
|
}
|
|
1277
1295
|
}
|
|
1278
1296
|
|
|
@@ -1296,7 +1314,7 @@ function applyAnnotationsFromExtensions(csn, config) {
|
|
|
1296
1314
|
if (!csn.extensions)
|
|
1297
1315
|
return;
|
|
1298
1316
|
|
|
1299
|
-
const filter = config.filter || (
|
|
1317
|
+
const filter = config.filter || (_name => true);
|
|
1300
1318
|
for (let i = 0; i < csn.extensions.length; ++i) {
|
|
1301
1319
|
const ext = csn.extensions[i];
|
|
1302
1320
|
const name = ext.annotate || ext.extend;
|
|
@@ -1305,7 +1323,8 @@ function applyAnnotationsFromExtensions(csn, config) {
|
|
|
1305
1323
|
if (def) {
|
|
1306
1324
|
copyAnnotationsAndDoc(ext, def, config.override);
|
|
1307
1325
|
applyAnnotationsToElements(ext, def);
|
|
1308
|
-
}
|
|
1326
|
+
}
|
|
1327
|
+
else if (config.notFound) {
|
|
1309
1328
|
config.notFound(name, i);
|
|
1310
1329
|
}
|
|
1311
1330
|
}
|
|
@@ -1343,8 +1362,7 @@ function isAspect(node) {
|
|
|
1343
1362
|
*/
|
|
1344
1363
|
function hasValidSkipOrExists(artifact) {
|
|
1345
1364
|
return artifact.kind === 'entity' &&
|
|
1346
|
-
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
|
|
1347
|
-
|
|
1365
|
+
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true));
|
|
1348
1366
|
}
|
|
1349
1367
|
|
|
1350
1368
|
/**
|
|
@@ -1368,7 +1386,7 @@ function getNamespace(csn, artifactName) {
|
|
|
1368
1386
|
for (let i = 1; i < parts.length; i++) {
|
|
1369
1387
|
// This was definitely a namespace so far
|
|
1370
1388
|
const previousArtifactName = seen;
|
|
1371
|
-
seen = `${seen}.${parts[i]}`;
|
|
1389
|
+
seen = `${ seen }.${ parts[i] }`;
|
|
1372
1390
|
// This might not be - if it isn't, return the result.
|
|
1373
1391
|
const currentArtifact = csn.definitions[seen];
|
|
1374
1392
|
if (currentArtifact && currentArtifact.kind !== 'namespace')
|
|
@@ -1385,7 +1403,7 @@ function getNamespace(csn, artifactName) {
|
|
|
1385
1403
|
* @param {CSN.Options} options
|
|
1386
1404
|
*/
|
|
1387
1405
|
function sortCsnDefinitionsForTests(csn, options) {
|
|
1388
|
-
if (!options.testMode)
|
|
1406
|
+
if (!options.testMode && !options.testSortCsn)
|
|
1389
1407
|
return;
|
|
1390
1408
|
const sorted = Object.create(null);
|
|
1391
1409
|
Object.keys(csn.definitions || {}).sort().forEach((name) => {
|
|
@@ -1401,11 +1419,10 @@ function sortCsnDefinitionsForTests(csn, options) {
|
|
|
1401
1419
|
* @returns {string[]}
|
|
1402
1420
|
*/
|
|
1403
1421
|
function getServiceNames(csn) {
|
|
1404
|
-
|
|
1422
|
+
const result = [];
|
|
1405
1423
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
1406
|
-
if (artifact.kind === 'service' && !artifact.abstract)
|
|
1424
|
+
if (artifact.kind === 'service' && !artifact.abstract)
|
|
1407
1425
|
result.push(artifactName);
|
|
1408
|
-
}
|
|
1409
1426
|
});
|
|
1410
1427
|
return result;
|
|
1411
1428
|
}
|
|
@@ -1420,9 +1437,9 @@ function getServiceNames(csn) {
|
|
|
1420
1437
|
function walkCsnPath(csn, path) {
|
|
1421
1438
|
/** @type {object} */
|
|
1422
1439
|
let obj = csn;
|
|
1423
|
-
for(const segment of path)
|
|
1440
|
+
for (const segment of path)
|
|
1424
1441
|
obj = obj[segment];
|
|
1425
|
-
|
|
1442
|
+
|
|
1426
1443
|
|
|
1427
1444
|
return obj;
|
|
1428
1445
|
}
|
|
@@ -1436,23 +1453,21 @@ function walkCsnPath(csn, path) {
|
|
|
1436
1453
|
* @returns {string|null}
|
|
1437
1454
|
*/
|
|
1438
1455
|
function getVariableReplacement(ref, options) {
|
|
1439
|
-
if(options && options.variableReplacements) {
|
|
1456
|
+
if (options && options.variableReplacements) {
|
|
1440
1457
|
let replacement = options.variableReplacements;
|
|
1441
|
-
for(const segment of ref) {
|
|
1458
|
+
for (const segment of ref) {
|
|
1442
1459
|
replacement = replacement[segment];
|
|
1443
|
-
if(replacement === undefined)
|
|
1460
|
+
if (replacement === undefined)
|
|
1444
1461
|
return null;
|
|
1445
1462
|
}
|
|
1446
1463
|
|
|
1447
|
-
if(replacement === undefined)
|
|
1464
|
+
if (replacement === undefined)
|
|
1448
1465
|
return null; // no valid replacement found
|
|
1449
|
-
else if(typeof replacement === 'string')
|
|
1466
|
+
else if (typeof replacement === 'string')
|
|
1450
1467
|
return replacement; // valid replacement
|
|
1451
|
-
|
|
1452
|
-
return null; // $user.foo, but we only have configured $user.foo.bar -> error
|
|
1453
|
-
} else {
|
|
1454
|
-
return null;
|
|
1468
|
+
return null; // $user.foo, but we only have configured $user.foo.bar -> error
|
|
1455
1469
|
}
|
|
1470
|
+
return null;
|
|
1456
1471
|
}
|
|
1457
1472
|
|
|
1458
1473
|
/**
|
|
@@ -1470,21 +1485,22 @@ function isDeepEqual(obj, other, noExtendedProps) {
|
|
|
1470
1485
|
let objectKeys = Object.keys(obj);
|
|
1471
1486
|
let otherKeys = Object.keys(other);
|
|
1472
1487
|
|
|
1473
|
-
if(noExtendedProps) {
|
|
1474
|
-
objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1475
|
-
otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1488
|
+
if (noExtendedProps) {
|
|
1489
|
+
objectKeys = objectKeys.filter(k => ![ '@', '$', '_' ].includes(k[0]));
|
|
1490
|
+
otherKeys = otherKeys.filter(k => ![ '@', '$', '_' ].includes(k[0]));
|
|
1476
1491
|
}
|
|
1477
1492
|
if (objectKeys.length !== otherKeys.length)
|
|
1478
1493
|
return false;
|
|
1479
1494
|
|
|
1480
|
-
for (
|
|
1481
|
-
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
|
|
1482
|
-
|
|
1495
|
+
for (const key of objectKeys) {
|
|
1496
|
+
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object') &&
|
|
1497
|
+
(other[key] !== null && typeof other[key] === 'object');
|
|
1483
1498
|
|
|
1484
1499
|
if (areValuesObjects) {
|
|
1485
1500
|
if (!isDeepEqual(obj[key], other[key], noExtendedProps))
|
|
1486
1501
|
return false;
|
|
1487
|
-
}
|
|
1502
|
+
}
|
|
1503
|
+
else if (obj[key] !== other[key]) {
|
|
1488
1504
|
return false;
|
|
1489
1505
|
}
|
|
1490
1506
|
}
|
|
@@ -1496,6 +1512,7 @@ module.exports = {
|
|
|
1496
1512
|
cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
|
|
1497
1513
|
cloneCsnNonDict,
|
|
1498
1514
|
cloneCsnDictionary,
|
|
1515
|
+
cloneAnnotationValue,
|
|
1499
1516
|
isBuiltinType,
|
|
1500
1517
|
applyAnnotationsFromExtensions,
|
|
1501
1518
|
forEachGeneric,
|
|
@@ -1513,6 +1530,7 @@ module.exports = {
|
|
|
1513
1530
|
getElementDatabaseNameOf,
|
|
1514
1531
|
applyTransformations,
|
|
1515
1532
|
applyTransformationsOnNonDictionary,
|
|
1533
|
+
applyTransformationsOnDictionary,
|
|
1516
1534
|
setDependencies,
|
|
1517
1535
|
isPersistedOnDatabase,
|
|
1518
1536
|
generatedByCompilerVersion,
|