@sap/cds-compiler 3.1.2 → 3.4.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 +101 -3
- package/bin/cdsc.js +4 -2
- package/doc/CHANGELOG_BETA.md +35 -0
- package/lib/api/main.js +153 -29
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +106 -24
- package/lib/base/message-registry.js +177 -79
- package/lib/base/messages.js +78 -57
- package/lib/base/model.js +2 -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 +53 -0
- package/lib/checks/defaultValues.js +4 -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 +23 -0
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/selectItems.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/types.js +2 -2
- package/lib/checks/utils.js +17 -7
- package/lib/checks/validator.js +36 -14
- package/lib/compiler/assert-consistency.js +21 -13
- package/lib/compiler/builtins.js +8 -0
- package/lib/compiler/checks.js +57 -40
- package/lib/compiler/define.js +139 -69
- package/lib/compiler/extend.js +319 -50
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/kick-start.js +2 -35
- package/lib/compiler/populate.js +111 -68
- package/lib/compiler/propagator.js +5 -3
- package/lib/compiler/resolve.js +71 -108
- package/lib/compiler/shared.js +82 -54
- package/lib/compiler/tweak-assocs.js +26 -14
- package/lib/compiler/utils.js +13 -2
- package/lib/edm/annotations/genericTranslation.js +10 -7
- package/lib/edm/csn2edm.js +11 -11
- 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 +4312 -4186
- package/lib/inspect/inspectModelStatistics.js +1 -1
- package/lib/inspect/inspectPropagation.js +23 -9
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +161 -172
- package/lib/json/to-csn.js +70 -10
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +8 -11
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +140 -93
- package/lib/language/language.g4 +57 -33
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.d.ts +3 -6
- package/lib/main.js +1 -0
- package/lib/model/.eslintrc.json +13 -0
- package/lib/model/api.js +4 -2
- package/lib/model/csnRefs.js +78 -50
- package/lib/model/csnUtils.js +272 -222
- package/lib/model/enrichCsn.js +41 -31
- package/lib/model/revealInternalProperties.js +61 -57
- package/lib/model/sortViews.js +35 -31
- package/lib/modelCompare/compare.js +52 -18
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +10 -1
- package/lib/render/manageConstraints.js +11 -7
- package/lib/render/toCdl.js +151 -106
- package/lib/render/toHdbcds.js +8 -6
- package/lib/render/toRename.js +4 -4
- package/lib/render/toSql.js +17 -7
- package/lib/render/utils/common.js +27 -9
- package/lib/render/utils/sql.js +5 -5
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/applyTransformations.js +32 -3
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +92 -41
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +8 -2
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +10 -7
- package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
- package/lib/transform/localized.js +29 -20
- package/lib/transform/odata/toFinalBaseType.js +8 -11
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +122 -51
- package/lib/transform/translateAssocsToJoins.js +17 -16
- package/lib/utils/moduleResolve.js +5 -5
- package/lib/utils/objectUtils.js +3 -3
- package/lib/utils/term.js +5 -5
- 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 → syntax-expecting-integer.md} +4 -4
- 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');
|
|
@@ -1086,38 +1106,40 @@ const _dependents = Symbol('_dependents');
|
|
|
1086
1106
|
* _dependents: All artifacts that depend on this artifact (because they have a ref that points to it)
|
|
1087
1107
|
* _dependencies: All artifacts this artifact depends on (because it has a ref to it)
|
|
1088
1108
|
*
|
|
1089
|
-
* @param {
|
|
1109
|
+
* @param {CSN.Model} csn A CSN to enrich in-place
|
|
1110
|
+
* @param {object} refs csnRefs, only used for artifactRef
|
|
1090
1111
|
* @returns {object} CSN with _dependents/_dependencies set, "cleanup" function, _dependents/_dependencies Symbol used
|
|
1091
1112
|
*/
|
|
1092
|
-
function setDependencies( csn ) {
|
|
1113
|
+
function setDependencies( csn, refs = csnRefs(csn) ) {
|
|
1093
1114
|
const cleanup = [];
|
|
1094
|
-
const { artifactRef } =
|
|
1115
|
+
const { artifactRef } = refs;
|
|
1095
1116
|
|
|
1096
1117
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
1097
|
-
|
|
1118
|
+
const queries = getNormalizedQuery(artifact).query;
|
|
1119
|
+
if (queries) {
|
|
1098
1120
|
initDependencies(artifact);
|
|
1099
|
-
forAllQueries(
|
|
1100
|
-
if(query.SELECT
|
|
1101
|
-
if(query.SELECT.from.args)
|
|
1121
|
+
forAllQueries(queries, (query) => {
|
|
1122
|
+
if (query.SELECT?.from) {
|
|
1123
|
+
if (query.SELECT.from.args)
|
|
1102
1124
|
handleArgs(artifact, artifactName, query.SELECT.from.args);
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
}
|
|
1125
|
+
|
|
1126
|
+
else if (typeof query.SELECT.from === 'string' || query.SELECT.from.ref)
|
|
1127
|
+
handleDependency(artifactRef(query.SELECT.from), artifact, artifactName);
|
|
1107
1128
|
}
|
|
1108
|
-
},
|
|
1129
|
+
}, [ 'definitions', artifactName, (artifact.projection ? 'projection' : 'query') ]);
|
|
1109
1130
|
}
|
|
1110
|
-
})
|
|
1131
|
+
});
|
|
1111
1132
|
|
|
1112
|
-
return {
|
|
1133
|
+
return {
|
|
1134
|
+
cleanup, csn, _dependents, _dependencies,
|
|
1135
|
+
};
|
|
1113
1136
|
|
|
1114
|
-
function handleArgs(artifact, artifactName, args){
|
|
1115
|
-
for(
|
|
1116
|
-
if (arg.args)
|
|
1137
|
+
function handleArgs(artifact, artifactName, args) {
|
|
1138
|
+
for (const arg of args) {
|
|
1139
|
+
if (arg.args)
|
|
1117
1140
|
handleArgs(artifact, artifactName, arg.args);
|
|
1118
|
-
|
|
1119
|
-
handleDependency(artifactRef(arg), artifact, artifactName)
|
|
1120
|
-
}
|
|
1141
|
+
else if (arg.ref)
|
|
1142
|
+
handleDependency(artifactRef(arg), artifact, artifactName);
|
|
1121
1143
|
}
|
|
1122
1144
|
}
|
|
1123
1145
|
|
|
@@ -1127,15 +1149,15 @@ function setDependencies( csn ) {
|
|
|
1127
1149
|
dependency[_dependents][dependantName] = dependant;
|
|
1128
1150
|
}
|
|
1129
1151
|
|
|
1130
|
-
function initDependents(obj){
|
|
1131
|
-
if(!obj[_dependents]) {
|
|
1152
|
+
function initDependents(obj) {
|
|
1153
|
+
if (!obj[_dependents]) {
|
|
1132
1154
|
obj[_dependents] = Object.create(null);
|
|
1133
1155
|
cleanup.push(() => delete obj[_dependents]);
|
|
1134
1156
|
}
|
|
1135
1157
|
}
|
|
1136
1158
|
|
|
1137
|
-
function initDependencies(obj){
|
|
1138
|
-
if(!obj[_dependencies]) {
|
|
1159
|
+
function initDependencies(obj) {
|
|
1160
|
+
if (!obj[_dependencies]) {
|
|
1139
1161
|
obj[_dependencies] = new Set();
|
|
1140
1162
|
cleanup.push(() => delete obj[_dependencies]);
|
|
1141
1163
|
}
|
|
@@ -1150,7 +1172,35 @@ function setDependencies( csn ) {
|
|
|
1150
1172
|
* @returns {boolean}
|
|
1151
1173
|
*/
|
|
1152
1174
|
function isPersistedOnDatabase(art) {
|
|
1153
|
-
return !('entity'
|
|
1175
|
+
return !(art.kind === 'entity' && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Check if the given artifact will be persisted on the database via `CREATE VIEW`
|
|
1179
|
+
*
|
|
1180
|
+
* @param {CSN.Artifact} artifact
|
|
1181
|
+
* @returns {boolean}
|
|
1182
|
+
*/
|
|
1183
|
+
function isPersistedAsView(artifact) {
|
|
1184
|
+
return artifact && artifact.kind === 'entity' &&
|
|
1185
|
+
!artifact._ignore &&
|
|
1186
|
+
!artifact.abstract &&
|
|
1187
|
+
((artifact.query || artifact.projection) && !hasAnnotationValue(artifact, '@cds.persistence.table')) &&
|
|
1188
|
+
!hasAnnotationValue(artifact, '@cds.persistence.skip') &&
|
|
1189
|
+
!hasAnnotationValue(artifact, '@cds.persistence.exists');
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Check if the given artifact will be persisted on the database via `CREATE TABLE`
|
|
1193
|
+
*
|
|
1194
|
+
* @param {CSN.Artifact} artifact
|
|
1195
|
+
* @returns {boolean}
|
|
1196
|
+
*/
|
|
1197
|
+
function isPersistedAsTable(artifact) {
|
|
1198
|
+
return artifact.kind === 'entity' &&
|
|
1199
|
+
!artifact._ignore &&
|
|
1200
|
+
!artifact.abstract &&
|
|
1201
|
+
(!artifact.query && !artifact.projection || hasAnnotationValue(artifact, '@cds.persistence.table')) &&
|
|
1202
|
+
!hasAnnotationValue(artifact, '@cds.persistence.skip') &&
|
|
1203
|
+
!hasAnnotationValue(artifact, '@cds.persistence.exists');
|
|
1154
1204
|
}
|
|
1155
1205
|
|
|
1156
1206
|
/**
|
|
@@ -1160,7 +1210,7 @@ function isPersistedOnDatabase(art) {
|
|
|
1160
1210
|
* @returns {string} String containing compiler version that was used to generate content
|
|
1161
1211
|
*/
|
|
1162
1212
|
function generatedByCompilerVersion() {
|
|
1163
|
-
return `generated by cds-compiler version ${version}`;
|
|
1213
|
+
return `generated by cds-compiler version ${ version }`;
|
|
1164
1214
|
}
|
|
1165
1215
|
|
|
1166
1216
|
/**
|
|
@@ -1170,9 +1220,9 @@ function generatedByCompilerVersion() {
|
|
|
1170
1220
|
* @returns {object} Object with a query property.
|
|
1171
1221
|
*/
|
|
1172
1222
|
function getNormalizedQuery(art) {
|
|
1173
|
-
if (art.projection)
|
|
1223
|
+
if (art.projection)
|
|
1174
1224
|
return { query: { SELECT: art.projection } };
|
|
1175
|
-
|
|
1225
|
+
|
|
1176
1226
|
return art;
|
|
1177
1227
|
}
|
|
1178
1228
|
|
|
@@ -1196,14 +1246,14 @@ function getRootArtifactName(artifactName, csn) {
|
|
|
1196
1246
|
if (seen === '')
|
|
1197
1247
|
seen = parts[i];
|
|
1198
1248
|
else
|
|
1199
|
-
|
|
1249
|
+
seen = `${ seen }.${ parts[i] }`;
|
|
1200
1250
|
|
|
1201
1251
|
const art = csn.definitions[seen];
|
|
1202
|
-
|
|
1252
|
+
// Our artifact seems to be contained in this context
|
|
1203
1253
|
if (art && (art.kind === 'context' || art.kind === 'service'))
|
|
1204
1254
|
return seen;
|
|
1205
1255
|
}
|
|
1206
|
-
|
|
1256
|
+
// Our artifact is a root artifact itself
|
|
1207
1257
|
return seen;
|
|
1208
1258
|
}
|
|
1209
1259
|
|
|
@@ -1223,7 +1273,7 @@ function getLastPartOf(name) {
|
|
|
1223
1273
|
// ['foo'] => 'foo';
|
|
1224
1274
|
// ['foo::bar'] => 'bar'
|
|
1225
1275
|
function getLastPartOfRef(ref) {
|
|
1226
|
-
|
|
1276
|
+
const lastPathStep = ref[ref.length - 1];
|
|
1227
1277
|
return getLastPartOf(lastPathStep.id || lastPathStep);
|
|
1228
1278
|
}
|
|
1229
1279
|
|
|
@@ -1244,9 +1294,8 @@ function copyAnnotations(fromNode, toNode, overwrite = false) {
|
|
|
1244
1294
|
const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
|
|
1245
1295
|
|
|
1246
1296
|
for (const anno of annotations) {
|
|
1247
|
-
if (toNode[anno] === undefined || overwrite)
|
|
1297
|
+
if (toNode[anno] === undefined || overwrite)
|
|
1248
1298
|
toNode[anno] = fromNode[anno];
|
|
1249
|
-
}
|
|
1250
1299
|
}
|
|
1251
1300
|
}
|
|
1252
1301
|
|
|
@@ -1270,9 +1319,8 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
|
1270
1319
|
.filter(key => key.startsWith('@') || key === 'doc');
|
|
1271
1320
|
|
|
1272
1321
|
for (const anno of annotations) {
|
|
1273
|
-
if (toNode[anno] === undefined || overwrite)
|
|
1322
|
+
if (toNode[anno] === undefined || overwrite)
|
|
1274
1323
|
toNode[anno] = fromNode[anno];
|
|
1275
|
-
}
|
|
1276
1324
|
}
|
|
1277
1325
|
}
|
|
1278
1326
|
|
|
@@ -1296,7 +1344,7 @@ function applyAnnotationsFromExtensions(csn, config) {
|
|
|
1296
1344
|
if (!csn.extensions)
|
|
1297
1345
|
return;
|
|
1298
1346
|
|
|
1299
|
-
const filter = config.filter || (
|
|
1347
|
+
const filter = config.filter || (_name => true);
|
|
1300
1348
|
for (let i = 0; i < csn.extensions.length; ++i) {
|
|
1301
1349
|
const ext = csn.extensions[i];
|
|
1302
1350
|
const name = ext.annotate || ext.extend;
|
|
@@ -1305,7 +1353,8 @@ function applyAnnotationsFromExtensions(csn, config) {
|
|
|
1305
1353
|
if (def) {
|
|
1306
1354
|
copyAnnotationsAndDoc(ext, def, config.override);
|
|
1307
1355
|
applyAnnotationsToElements(ext, def);
|
|
1308
|
-
}
|
|
1356
|
+
}
|
|
1357
|
+
else if (config.notFound) {
|
|
1309
1358
|
config.notFound(name, i);
|
|
1310
1359
|
}
|
|
1311
1360
|
}
|
|
@@ -1343,8 +1392,7 @@ function isAspect(node) {
|
|
|
1343
1392
|
*/
|
|
1344
1393
|
function hasValidSkipOrExists(artifact) {
|
|
1345
1394
|
return artifact.kind === 'entity' &&
|
|
1346
|
-
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
|
|
1347
|
-
|
|
1395
|
+
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true));
|
|
1348
1396
|
}
|
|
1349
1397
|
|
|
1350
1398
|
/**
|
|
@@ -1368,7 +1416,7 @@ function getNamespace(csn, artifactName) {
|
|
|
1368
1416
|
for (let i = 1; i < parts.length; i++) {
|
|
1369
1417
|
// This was definitely a namespace so far
|
|
1370
1418
|
const previousArtifactName = seen;
|
|
1371
|
-
seen = `${seen}.${parts[i]}`;
|
|
1419
|
+
seen = `${ seen }.${ parts[i] }`;
|
|
1372
1420
|
// This might not be - if it isn't, return the result.
|
|
1373
1421
|
const currentArtifact = csn.definitions[seen];
|
|
1374
1422
|
if (currentArtifact && currentArtifact.kind !== 'namespace')
|
|
@@ -1385,7 +1433,7 @@ function getNamespace(csn, artifactName) {
|
|
|
1385
1433
|
* @param {CSN.Options} options
|
|
1386
1434
|
*/
|
|
1387
1435
|
function sortCsnDefinitionsForTests(csn, options) {
|
|
1388
|
-
if (!options.testMode)
|
|
1436
|
+
if (!options.testMode && !options.testSortCsn)
|
|
1389
1437
|
return;
|
|
1390
1438
|
const sorted = Object.create(null);
|
|
1391
1439
|
Object.keys(csn.definitions || {}).sort().forEach((name) => {
|
|
@@ -1401,11 +1449,10 @@ function sortCsnDefinitionsForTests(csn, options) {
|
|
|
1401
1449
|
* @returns {string[]}
|
|
1402
1450
|
*/
|
|
1403
1451
|
function getServiceNames(csn) {
|
|
1404
|
-
|
|
1452
|
+
const result = [];
|
|
1405
1453
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
1406
|
-
if (artifact.kind === 'service' && !artifact.abstract)
|
|
1454
|
+
if (artifact.kind === 'service' && !artifact.abstract)
|
|
1407
1455
|
result.push(artifactName);
|
|
1408
|
-
}
|
|
1409
1456
|
});
|
|
1410
1457
|
return result;
|
|
1411
1458
|
}
|
|
@@ -1420,9 +1467,9 @@ function getServiceNames(csn) {
|
|
|
1420
1467
|
function walkCsnPath(csn, path) {
|
|
1421
1468
|
/** @type {object} */
|
|
1422
1469
|
let obj = csn;
|
|
1423
|
-
for(const segment of path)
|
|
1470
|
+
for (const segment of path)
|
|
1424
1471
|
obj = obj[segment];
|
|
1425
|
-
|
|
1472
|
+
|
|
1426
1473
|
|
|
1427
1474
|
return obj;
|
|
1428
1475
|
}
|
|
@@ -1436,23 +1483,21 @@ function walkCsnPath(csn, path) {
|
|
|
1436
1483
|
* @returns {string|null}
|
|
1437
1484
|
*/
|
|
1438
1485
|
function getVariableReplacement(ref, options) {
|
|
1439
|
-
if(options && options.variableReplacements) {
|
|
1486
|
+
if (options && options.variableReplacements) {
|
|
1440
1487
|
let replacement = options.variableReplacements;
|
|
1441
|
-
for(const segment of ref) {
|
|
1488
|
+
for (const segment of ref) {
|
|
1442
1489
|
replacement = replacement[segment];
|
|
1443
|
-
if(replacement === undefined)
|
|
1490
|
+
if (replacement === undefined)
|
|
1444
1491
|
return null;
|
|
1445
1492
|
}
|
|
1446
1493
|
|
|
1447
|
-
if(replacement === undefined)
|
|
1494
|
+
if (replacement === undefined)
|
|
1448
1495
|
return null; // no valid replacement found
|
|
1449
|
-
else if(typeof replacement === 'string')
|
|
1496
|
+
else if (typeof replacement === 'string')
|
|
1450
1497
|
return replacement; // valid replacement
|
|
1451
|
-
|
|
1452
|
-
return null; // $user.foo, but we only have configured $user.foo.bar -> error
|
|
1453
|
-
} else {
|
|
1454
|
-
return null;
|
|
1498
|
+
return null; // $user.foo, but we only have configured $user.foo.bar -> error
|
|
1455
1499
|
}
|
|
1500
|
+
return null;
|
|
1456
1501
|
}
|
|
1457
1502
|
|
|
1458
1503
|
/**
|
|
@@ -1470,21 +1515,22 @@ function isDeepEqual(obj, other, noExtendedProps) {
|
|
|
1470
1515
|
let objectKeys = Object.keys(obj);
|
|
1471
1516
|
let otherKeys = Object.keys(other);
|
|
1472
1517
|
|
|
1473
|
-
if(noExtendedProps) {
|
|
1474
|
-
objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1475
|
-
otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1518
|
+
if (noExtendedProps) {
|
|
1519
|
+
objectKeys = objectKeys.filter(k => ![ '@', '$', '_' ].includes(k[0]));
|
|
1520
|
+
otherKeys = otherKeys.filter(k => ![ '@', '$', '_' ].includes(k[0]));
|
|
1476
1521
|
}
|
|
1477
1522
|
if (objectKeys.length !== otherKeys.length)
|
|
1478
1523
|
return false;
|
|
1479
1524
|
|
|
1480
|
-
for (
|
|
1481
|
-
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
|
|
1482
|
-
|
|
1525
|
+
for (const key of objectKeys) {
|
|
1526
|
+
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object') &&
|
|
1527
|
+
(other[key] !== null && typeof other[key] === 'object');
|
|
1483
1528
|
|
|
1484
1529
|
if (areValuesObjects) {
|
|
1485
1530
|
if (!isDeepEqual(obj[key], other[key], noExtendedProps))
|
|
1486
1531
|
return false;
|
|
1487
|
-
}
|
|
1532
|
+
}
|
|
1533
|
+
else if (obj[key] !== other[key]) {
|
|
1488
1534
|
return false;
|
|
1489
1535
|
}
|
|
1490
1536
|
}
|
|
@@ -1496,6 +1542,7 @@ module.exports = {
|
|
|
1496
1542
|
cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
|
|
1497
1543
|
cloneCsnNonDict,
|
|
1498
1544
|
cloneCsnDictionary,
|
|
1545
|
+
cloneAnnotationValue,
|
|
1499
1546
|
isBuiltinType,
|
|
1500
1547
|
applyAnnotationsFromExtensions,
|
|
1501
1548
|
forEachGeneric,
|
|
@@ -1513,8 +1560,11 @@ module.exports = {
|
|
|
1513
1560
|
getElementDatabaseNameOf,
|
|
1514
1561
|
applyTransformations,
|
|
1515
1562
|
applyTransformationsOnNonDictionary,
|
|
1563
|
+
applyTransformationsOnDictionary,
|
|
1516
1564
|
setDependencies,
|
|
1517
1565
|
isPersistedOnDatabase,
|
|
1566
|
+
isPersistedAsView,
|
|
1567
|
+
isPersistedAsTable,
|
|
1518
1568
|
generatedByCompilerVersion,
|
|
1519
1569
|
getNormalizedQuery,
|
|
1520
1570
|
getRootArtifactName,
|