@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/bin/cdsc.js +4 -2
  3. package/doc/CHANGELOG_BETA.md +35 -0
  4. package/lib/api/main.js +153 -29
  5. package/lib/api/validate.js +8 -3
  6. package/lib/base/dictionaries.js +6 -6
  7. package/lib/base/error.js +2 -2
  8. package/lib/base/keywords.js +106 -24
  9. package/lib/base/message-registry.js +177 -79
  10. package/lib/base/messages.js +78 -57
  11. package/lib/base/model.js +2 -1
  12. package/lib/checks/actionsFunctions.js +1 -1
  13. package/lib/checks/annotationsOData.js +2 -2
  14. package/lib/checks/arrayOfs.js +15 -7
  15. package/lib/checks/cdsPersistence.js +1 -1
  16. package/lib/checks/checkForTypes.js +53 -0
  17. package/lib/checks/defaultValues.js +4 -2
  18. package/lib/checks/elements.js +81 -6
  19. package/lib/checks/foreignKeys.js +12 -13
  20. package/lib/checks/invalidTarget.js +10 -11
  21. package/lib/checks/managedInType.js +21 -15
  22. package/lib/checks/nullableKeys.js +1 -1
  23. package/lib/checks/onConditions.js +9 -9
  24. package/lib/checks/parameters.js +23 -0
  25. package/lib/checks/queryNoDbArtifacts.js +1 -1
  26. package/lib/checks/selectItems.js +1 -1
  27. package/lib/checks/sql-snippets.js +12 -10
  28. package/lib/checks/types.js +2 -2
  29. package/lib/checks/utils.js +17 -7
  30. package/lib/checks/validator.js +36 -14
  31. package/lib/compiler/assert-consistency.js +21 -13
  32. package/lib/compiler/builtins.js +8 -0
  33. package/lib/compiler/checks.js +57 -40
  34. package/lib/compiler/define.js +139 -69
  35. package/lib/compiler/extend.js +319 -50
  36. package/lib/compiler/finalize-parse-cdl.js +14 -9
  37. package/lib/compiler/kick-start.js +2 -35
  38. package/lib/compiler/populate.js +111 -68
  39. package/lib/compiler/propagator.js +5 -3
  40. package/lib/compiler/resolve.js +71 -108
  41. package/lib/compiler/shared.js +82 -54
  42. package/lib/compiler/tweak-assocs.js +26 -14
  43. package/lib/compiler/utils.js +13 -2
  44. package/lib/edm/annotations/genericTranslation.js +10 -7
  45. package/lib/edm/csn2edm.js +11 -11
  46. package/lib/edm/edm.js +17 -9
  47. package/lib/edm/edmPreprocessor.js +53 -30
  48. package/lib/edm/edmUtils.js +7 -2
  49. package/lib/gen/Dictionary.json +14 -0
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -2
  52. package/lib/gen/languageParser.js +4312 -4186
  53. package/lib/inspect/inspectModelStatistics.js +1 -1
  54. package/lib/inspect/inspectPropagation.js +23 -9
  55. package/lib/json/csnVersion.js +13 -13
  56. package/lib/json/from-csn.js +161 -172
  57. package/lib/json/to-csn.js +70 -10
  58. package/lib/language/.eslintrc.json +4 -0
  59. package/lib/language/antlrParser.js +8 -11
  60. package/lib/language/docCommentParser.js +1 -2
  61. package/lib/language/errorStrategy.js +54 -27
  62. package/lib/language/genericAntlrParser.js +140 -93
  63. package/lib/language/language.g4 +57 -33
  64. package/lib/language/multiLineStringParser.js +75 -63
  65. package/lib/main.d.ts +3 -6
  66. package/lib/main.js +1 -0
  67. package/lib/model/.eslintrc.json +13 -0
  68. package/lib/model/api.js +4 -2
  69. package/lib/model/csnRefs.js +78 -50
  70. package/lib/model/csnUtils.js +272 -222
  71. package/lib/model/enrichCsn.js +41 -31
  72. package/lib/model/revealInternalProperties.js +61 -57
  73. package/lib/model/sortViews.js +35 -31
  74. package/lib/modelCompare/compare.js +52 -18
  75. package/lib/modelCompare/filter.js +83 -0
  76. package/lib/optionProcessor.js +10 -1
  77. package/lib/render/manageConstraints.js +11 -7
  78. package/lib/render/toCdl.js +151 -106
  79. package/lib/render/toHdbcds.js +8 -6
  80. package/lib/render/toRename.js +4 -4
  81. package/lib/render/toSql.js +17 -7
  82. package/lib/render/utils/common.js +27 -9
  83. package/lib/render/utils/sql.js +5 -5
  84. package/lib/sql-identifier.js +7 -0
  85. package/lib/transform/db/applyTransformations.js +32 -3
  86. package/lib/transform/db/assertUnique.js +27 -38
  87. package/lib/transform/db/expansion.js +92 -41
  88. package/lib/transform/db/flattening.js +1 -1
  89. package/lib/transform/db/temporal.js +3 -1
  90. package/lib/transform/db/transformExists.js +8 -2
  91. package/lib/transform/db/views.js +42 -13
  92. package/lib/transform/draft/db.js +2 -2
  93. package/lib/transform/forOdataNew.js +10 -7
  94. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
  95. package/lib/transform/localized.js +29 -20
  96. package/lib/transform/odata/toFinalBaseType.js +8 -11
  97. package/lib/transform/odata/typesExposure.js +2 -1
  98. package/lib/transform/parseExpr.js +245 -0
  99. package/lib/transform/transformUtilsNew.js +122 -51
  100. package/lib/transform/translateAssocsToJoins.js +17 -16
  101. package/lib/utils/moduleResolve.js +5 -5
  102. package/lib/utils/objectUtils.js +3 -3
  103. package/lib/utils/term.js +5 -5
  104. package/package.json +2 -2
  105. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  106. package/share/messages/check-proper-type-of.md +4 -4
  107. package/share/messages/check-proper-type.md +2 -2
  108. package/share/messages/duplicate-autoexposed.md +4 -4
  109. package/share/messages/extend-repeated-intralayer.md +4 -5
  110. package/share/messages/extend-unrelated-layer.md +4 -4
  111. package/share/messages/message-explanations.json +3 -1
  112. package/share/messages/redirected-to-ambiguous.md +7 -6
  113. package/share/messages/redirected-to-complex.md +63 -0
  114. package/share/messages/redirected-to-unrelated.md +6 -5
  115. package/share/messages/rewrite-not-supported.md +4 -4
  116. package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
  117. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -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 { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
7
- const { ModelError } = require("../base/error");
8
- const { typeParameters } = require("../compiler/builtins");
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').version;
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
- query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
113
- query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
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} [error_parent] Parent name to use for error messages, ref before alias
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, error_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), error_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
- } else if (query.SET) {
210
+ else if (query.SET)
208
211
  return getQueryPrimarySource(query.SET);
209
- } else if (query.from) {
212
+ else if (query.from)
210
213
  return getQueryPrimarySource(query.from);
211
- } else if (query.ref) {
214
+ else if (query.ref)
212
215
  return query;
213
- } else if (query.args) {
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
- let visited = Object.create(null);
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) return;
236
- if (visited[id]) {
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
- else
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
- let visited = createVisited(typeName);
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
- let visited = createVisited(typeName);
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
- let visited = createVisited(typeName);
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
- let visited = createVisited(typeName);
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
- let visited = createVisited(typeName);
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) return undefined;
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) return undefined;
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) return undefined;
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('Annotation name should start with "@": ' + absoluteName);
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
- let idx = artifactName.lastIndexOf('.');
425
- if (idx === -1) return null;
429
+ for (;;) {
430
+ const idx = artifactName.lastIndexOf('.');
431
+ if (idx === -1)
432
+ return null;
426
433
  artifactName = artifactName.substring(0, idx);
427
- let artifact = model.definitions[artifactName];
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
- // This general transformation function will be applied to each node recursively
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
- // Simply return if node is to be ignored
464
- if (node == undefined || node._ignore)
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
- // Transform arrays element-wise
467
- if (Array.isArray(node)) {
471
+ // Transform arrays element-wise
472
+ if (Array.isArray(node))
468
473
  return node.map(transformNode);
469
- }
470
- // Things not having 'proto' are dictionaries
471
- let proto = Object.getPrototypeOf(node);
472
- // Iterate own properties of 'node' and transform them into 'resultNode'
473
- let resultNode = Object.create(proto);
474
- for (let key of Object.keys(node)) {
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
- let transformer = (proto == undefined) ? transformNode : transformers[key] || transformers[key.charAt(0)];
477
- // Apply transformer, or use transformNode() if there is none
478
- let resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
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
- let typeRef = artifactRef(type); // throws if not found
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
- * Note that annotations are only copied shallowly.
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 sort().
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
- path = [...path]; // Copy
684
- const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
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
- constructCallback = (_construct, _prop, _path) => {}) {
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
- constructCallback = (_construct, _prop, _path) => {}) {
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
- || (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function'
787
- && iterateOptions.skipArtifact(dictObj, name)))
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
- } else {
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
- traverseQuery( from, callback, csnPath ); // sub query in FROM
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
- else
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
- } else {
953
- throw new Error('Unknown naming mode: ' + sqlMapping);
954
- }
955
- } else {
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
- } else if(art && art.kind === 'service') {
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
- else {
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 {object} csn A CSN to enrich in-place
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 } = csnRefs(csn);
1115
+ const { artifactRef } = refs;
1095
1116
 
1096
1117
  forEachDefinition(csn, (artifact, artifactName) => {
1097
- if(getNormalizedQuery(artifact).query) {
1118
+ const queries = getNormalizedQuery(artifact).query;
1119
+ if (queries) {
1098
1120
  initDependencies(artifact);
1099
- forAllQueries(getNormalizedQuery(artifact).query, (query) => {
1100
- if(query.SELECT && query.SELECT.from) {
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
- } else {
1104
- if(typeof query.SELECT.from === 'string' || query.SELECT.from.ref )
1105
- handleDependency(artifactRef(query.SELECT.from), artifact, artifactName);
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
- }, ['definitions', artifactName, (artifact.projection ? 'projection' : 'query')])
1129
+ }, [ 'definitions', artifactName, (artifact.projection ? 'projection' : 'query') ]);
1109
1130
  }
1110
- })
1131
+ });
1111
1132
 
1112
- return {cleanup, csn, _dependents, _dependencies};
1133
+ return {
1134
+ cleanup, csn, _dependents, _dependencies,
1135
+ };
1113
1136
 
1114
- function handleArgs(artifact, artifactName, args){
1115
- for(let arg of args){
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
- } else if (arg.ref) {
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' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
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
- seen = `${seen}.${parts[i]}`;
1249
+ seen = `${ seen }.${ parts[i] }`;
1200
1250
 
1201
1251
  const art = csn.definitions[seen];
1202
- // Our artifact seems to be contained in this context
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
- // Our artifact is a root artifact itself
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
- let lastPathStep = ref[ref.length - 1];
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 || ((_name) => true);
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
- } else if (config.notFound) {
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
- let result = [];
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
- else
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 (let key of objectKeys) {
1481
- const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
1482
- && (other[key] !== null && typeof other[key] === 'object');
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
- } else if (obj[key] !== other[key]) {
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,