@sap/cds-compiler 2.13.8 → 3.0.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 (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -1,59 +0,0 @@
1
- 'use strict';
2
-
3
- const { forEachManagedAssociation } = require('./utils');
4
- const { attachPath, attachPathOnPartialCSN } = require('./attachPath');
5
-
6
- /**
7
- * This module runs through the model and for each managed association in it,
8
- * in case the foreign keys are structured, it is expanding them. Example:
9
- * entity A {
10
- * toB: association to B { stru };
11
- * } // -> CSN: keys:[ { ref:['stru'] } ]
12
- *
13
- * entity B {
14
- * stru: {
15
- * subid: Integer;
16
- * }
17
- * }
18
- * after expand -> keys:[ { ref: ['stru_subid'] } ]
19
- */
20
- module.exports = function (csn, referenceFlattener, csnUtils, isExternalServiceMember) {
21
-
22
- forEachManagedAssociation(csn, (element) => {
23
- if (element.keys) {
24
- expandStructuredKeysForAssociation(element, referenceFlattener);
25
- }
26
- }, isExternalServiceMember);
27
-
28
- // update paths and resolve references
29
- attachPath(csn);
30
- referenceFlattener.resolveAllReferences(csn, csnUtils.inspectRef, csnUtils.isStructured);
31
-
32
- function expandStructuredKeysForAssociation(assoc, referenceFlattener) {
33
- let newKeys = [];
34
- for (let key of assoc.keys) {
35
- // when are assigned $paths and when not???
36
- let paths = key.$paths;
37
- if (paths) {
38
- let lastPath = paths[paths.length - 1];
39
- let generatedElements = referenceFlattener.getGeneratedElementsForPath(lastPath);
40
- if (generatedElements) {
41
- generatedElements.forEach(elementName => {
42
- let newRef = { ref: [elementName] };
43
- if (key.as) {
44
- newRef.as = elementName.replace(key.ref[0], key.as);
45
- }
46
- newKeys.push(newRef);
47
- })
48
- continue;
49
- }
50
- }
51
- newKeys.push(key);
52
- }
53
-
54
- if (newKeys.length) {
55
- attachPathOnPartialCSN(newKeys, assoc.$path.concat('keys'));
56
- assoc.keys = newKeys;
57
- }
58
- }
59
- }
@@ -1,261 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * The module handles the processing of foreign key for managed associations.
5
- */
6
-
7
- const { copyAnnotations } = require('../../model/csnUtils');
8
- const sortByAssociationDependency = require('./sortByAssociationDependency');
9
- const { flattenStructure } = require('./structureFlattener');
10
- const { setProp } = require('../../base/model');
11
- const { implicitAs } = require('../../model/csnRefs');
12
-
13
- /**
14
- *
15
- * @param {CSN.Model} csn
16
- * @param {*} options
17
- * @param {*} referenceFlattener
18
- * @param {*} csnUtils
19
- * @param {object} error;
20
- */
21
- module.exports = function (csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember) {
22
-
23
- const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
24
- const flatKeys = !structuredOData || (structuredOData && options.toOdata.odataForeignKeys);
25
-
26
- // sort all associations by their dependencies
27
- const sortedAssociations = sortByAssociationDependency(csn, referenceFlattener, isExternalServiceMember);
28
-
29
- // generate foreign keys
30
- processSortedAssociations(sortedAssociations, flatKeys);
31
-
32
-
33
- function processSortedAssociations(sortedAssociations, flatKeys,) {
34
- // The map will collect all generated foreign key names for the specific path
35
- let generatedForeignKeyNamesForPath = Object.create(null); // map<path,[key-name]>
36
-
37
- sortedAssociations.forEach(item => {
38
- const { definitionName, structuralNodeName, elementName, element, parent, path } = item;
39
-
40
- if (csnUtils.isManagedAssociation(element) && element.keys) {
41
- if (flatKeys) // tackling the ref value in assoc.keys
42
- takeoverForeignKeysOfTargetAssociations(element, path, generatedForeignKeyNamesForPath);
43
- // TODO: move in separate function
44
- fixCardinality(element);
45
- }
46
-
47
- let arrayOfGeneratedForeignKeyNames = generateForeignKeys(definitionName, structuralNodeName, elementName, element, parent, path);
48
- generatedForeignKeyNamesForPath[item.path.join('/')] = arrayOfGeneratedForeignKeyNames;
49
- })
50
-
51
- }
52
-
53
- /**
54
- * if a key is an association and it poins to another association,
55
- * the foreign keys of the target association become primary keys
56
- * in the current association
57
- */
58
- function takeoverForeignKeysOfTargetAssociations(assoc, path, generatedForeignKeyNamesForPath) {
59
- let newResult = [];
60
- assoc.keys.forEach( (key, keyIndex) => {
61
- let keyPath = path.concat('keys', keyIndex);
62
- let resolved = csnUtils.inspectRef(keyPath)
63
- let targetElement = resolved.art;
64
- if (targetElement) {
65
- if (csnUtils.isAssociation(targetElement.type)) {
66
- // association key
67
- expandAssociationKey(key);
68
- } else {
69
- newResult.push(key);
70
- }
71
- } else {
72
- // target element does not exist, warning is already reported, pass the key anyway
73
- newResult.push(key);
74
- }
75
- });
76
-
77
- function expandAssociationKey(key) {
78
- let paths = key.$paths;
79
- if (!paths) return;
80
- let lastPath = paths[paths.length - 1];
81
- let transitionPath = referenceFlattener.getElementTransition(lastPath)
82
- if (transitionPath)
83
- lastPath = transitionPath;
84
- let generatedKeys = generatedForeignKeyNamesForPath[lastPath.join('/')];
85
- if (!generatedKeys) return;
86
- generatedKeys.forEach(fkName => {
87
- let newFkRef = { ref: [fkName] };
88
- if (key.as) {
89
- let alias = fkName.replace(key.ref[0], key.as);
90
- setProp(newFkRef, 'as', alias);
91
- }
92
- newResult.push(newFkRef);
93
- })
94
- } // expandAssociationKey
95
-
96
- assoc.keys = newResult;
97
-
98
- }
99
-
100
- function fixCardinality(assoc) {
101
- if (assoc.notNull) {
102
- if (!assoc.cardinality) {
103
- assoc.cardinality = {};
104
- }
105
- if (assoc.cardinality.min === undefined) {
106
- assoc.cardinality.min = 1;
107
- }
108
- }
109
- }
110
-
111
- /**
112
- * Generates foreign keys and returns their names as an array
113
- */
114
- function generateForeignKeys(definitionName, structuralNodeName, assocName, assoc, parent, path) {
115
- let foreignKeyElements = Object.create(null);
116
-
117
- // First, loop over the keys array of the association and generate the FKs.
118
- // The result of all the FKs for the given association is accumulated
119
- // in the 'foreignKeyElements' dictionary
120
- assoc.keys.forEach( (key, keyIndex) => {
121
- let keyPath = path.concat('keys', keyIndex);
122
-
123
- let foreignKeyElementsForKey = generateForeignKeysForRef(assoc, assocName, key, keyPath);
124
- Object.assign(foreignKeyElements, foreignKeyElementsForKey);
125
- });
126
-
127
- // After that, add the new elements to the definition.
128
- // At the same time:
129
- // -> Check for coliding element's name
130
- // &
131
- // -> Propagate annotations from the association
132
- if (parent.items) // proceed to items of such
133
- parent = parent.items;
134
- if (parent.returns)
135
- parent = parent.returns.items || parent.returns;
136
-
137
- const dictionary = parent[structuralNodeName];
138
- let currElementsNames = Object.keys(parent[structuralNodeName]);
139
- for (const [foreignKeyName, foreignKey] of Object.entries(foreignKeyElements)) {
140
- copyAnnotations(assoc, foreignKey, true);
141
- // Insert artificial element into artifact, with all cross-links
142
- if (dictionary[foreignKeyName]) {
143
- if (!(dictionary[foreignKeyName]['@odata.foreignKey4'] || isDeepEqual(dictionary[foreignKeyName], foreignKey))) {
144
- const path = dictionary[foreignKeyName].$path;
145
- error(null, path, { name: foreignKeyName, art: assocName }, 'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element');
146
- }
147
- }
148
- }
149
-
150
- // make sure the generated foreign key(s) is added right after the association (that it belongs to) in the elements dictionary
151
- const assocIndex = currElementsNames.findIndex(elemName => elemName === assocName);
152
- // if (flatKeys)
153
- currElementsNames.splice(assocIndex + 1, 0, ...Object.keys(foreignKeyElements));
154
-
155
- parent[structuralNodeName] = currElementsNames.reduce((previous, name) => {
156
- previous[name] = dictionary[name] || foreignKeyElements[name];
157
- return previous;
158
- }, Object.create(null));
159
-
160
- return Object.keys(foreignKeyElements);
161
- }
162
-
163
- // FIXME: Very similar code to
164
- // transformUtilsNew::getForeignKeyArtifact & createForeignKeyElement
165
- // Can this be streamlined?
166
- function generateForeignKeysForRef(assoc, assocName, foreignKeyRef, pathInKeysArr, foreignKey4 = assocName) {
167
- // in structured OData, might be more than one generated FKs
168
- let generatedFks = Object.create(null);
169
- const fkArtifact = csnUtils.inspectRef(pathInKeysArr).art;
170
- if(fkArtifact) {
171
- if (csnUtils.isStructured(fkArtifact)) {
172
- processStucturedKey(fkArtifact, assocName, foreignKeyRef);
173
- } else {
174
- // built-in
175
- const foreignKeyElementName = `${assocName.replace(/\./g, '_')}_${foreignKeyRef.as || foreignKeyRef.ref.join('_')}`;
176
- newForeignKey(fkArtifact, foreignKeyElementName);
177
- }
178
- }
179
-
180
- return generatedFks;
181
-
182
- function processStucturedKey(fkArtifact, assocName, foreignKeyRef) {
183
- const subStruct = fkArtifact.elements ? fkArtifact : csnUtils.getFinalBaseType(fkArtifact.type);
184
- const flatElements = flattenStructure(subStruct.elements, subStruct.$path, csnUtils, options, error, undefined, fkArtifact.$path.slice(-1) || []).newFlatElements;
185
- for (const [flatElemName, flatElem] of Object.entries(flatElements)) {
186
- const foreignKeyElementName =
187
- `${assocName.replace(/\./g, '_')}_${foreignKeyRef.as ? flatElemName.replace(implicitAs(foreignKeyRef.ref), foreignKeyRef.as) : flatElemName}`;
188
- newForeignKey(flatElem, foreignKeyElementName);
189
- }
190
- }
191
-
192
- function newForeignKey(fkArtifact, foreignKeyElementName) {
193
- if (fkArtifact.type === 'cds.Association' || fkArtifact.type === 'cds.Composition') {
194
- processAssociationOrComposition(fkArtifact, foreignKeyElementName);
195
- return;
196
- }
197
-
198
- // FIXME: better use transformUtlsNew::createRealFK(...);
199
- let foreignKeyElement = Object.create(null);
200
-
201
- // Transfer selected type properties from target key element
202
- // FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
203
- for (let prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
204
- if (fkArtifact[prop] !== undefined) {
205
- foreignKeyElement[prop] = fkArtifact[prop];
206
- }
207
- }
208
- // If the association is non-fkArtifact resp. key, so should be the foreign key field
209
- for (let prop of ['notNull', 'key']) {
210
- if (assoc[prop] !== undefined) {
211
- foreignKeyElement[prop] = assoc[prop];
212
- }
213
- }
214
-
215
- foreignKeyElement['@odata.foreignKey4'] = foreignKey4;
216
- if (flatKeys) foreignKeyRef.$generatedFieldName = foreignKeyElementName;
217
- setProp(foreignKeyElement, '$path', pathInKeysArr); // attach $path to the newly created element - used for inspectRef in processAssociationOrComposition
218
- if (assoc.$location) {
219
- setProp(foreignKeyElement, '$location', assoc.$location);
220
- }
221
- generatedFks[foreignKeyElementName] = foreignKeyElement;
222
- }
223
-
224
- function processAssociationOrComposition(fkArtifact, foreignKeyElementName) {
225
- fkArtifact.keys.forEach((keyRef,keyId) => {
226
- const path = fkArtifact.$path.concat('keys').concat(keyId);
227
- const fksForAssoc = generateForeignKeysForRef(assoc, foreignKeyElementName, keyRef, path, foreignKey4);
228
- Object.assign(generatedFks, fksForAssoc);
229
- })
230
- }
231
- }
232
- }
233
-
234
- /**
235
- *
236
- * @param {object} obj
237
- * @param {*} other
238
- * @returns {boolean} Whether 'obj' and 'other' are deeply equal. We need the
239
- * deep comparison because of annotations that have structured values and they
240
- * are propagated to the generated foreign keys.
241
- */
242
- function isDeepEqual(obj, other) {
243
- const objectKeys = Object.keys(obj);
244
- const otherKeys = Object.keys(other);
245
-
246
- if (objectKeys.length !== otherKeys.length)
247
- return false;
248
-
249
- for (let key of objectKeys) {
250
- const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
251
- && (other[key] !== null && typeof other[key] === 'object');
252
-
253
- if (areValuesObjects) {
254
- if (!isDeepEqual(obj[key], other[key]))
255
- return false;
256
- } else if (obj[key] !== other[key]) {
257
- return false;
258
- }
259
- }
260
- return true;
261
- }
@@ -1,296 +0,0 @@
1
- const { applyTransformations } = require('../../model/csnUtils');
2
- const { setProp } = require('../../base/model');
3
- const { implicitAs } = require('../../model/csnRefs');
4
- const { structuralPath } = require('./structuralPath');
5
-
6
- /** This class is used for generic reference flattening.
7
- * It provides the following functionality:
8
- * - attach the CSN location paths of object as $path property
9
- * - resolve all references found in the CSN
10
- * The resolved references and their paths are stored in the $paths property which is attached to the reference
11
- * In addition, the items of the references will be checked if they point to structured elements,
12
- * and the result will be cached in the "structuredReference" member variable.
13
- * - register flattened elements - stored in the member variable "flattenedElementPaths"
14
- * - check if a specific path points to a structured element
15
- * - flatten of all references - combines already stored information to flatten the references
16
- *
17
- * Generic reference flattening works as follows:
18
- * - resolve all references and there the single items
19
- * - attach the resolved paths to the elements
20
- * - during flattening of elements, transformers register all flattened paths
21
- * - final flattening detects reference items which point to flattened elements
22
- * and joins both previous and current reference items
23
- *
24
- * Element transitions - when a structure is flattened, elements are moved from one location to another.
25
- * Those transitions are registered in elementTransitions, where the origin path as key is mapped to the new path location.
26
- * Used structure: elementTransitions[fromPath.toString()]=toPath.asPath()
27
- * The key is the origin path joined with slash, new path is stored as path - array of strings.
28
- * The corresponding functions are: registerElementTransition and getElementTransition.
29
- */
30
-
31
- class ReferenceFlattener {
32
-
33
- /**
34
- * Reference flattening helper.
35
- * @constructor
36
- */
37
- constructor() {
38
- this.flattenedElementPaths = {};
39
- this.structuredReference = {};
40
- this.generatedElementsForPath = {};
41
- this.elementTransitions = {};
42
- this.elementNamesWithDots = {};
43
- }
44
-
45
- /**
46
- * Resolves all references in the specified CSN and attaches the paths of resolved reference items
47
- * as non-enumerable array of paths property called $paths.
48
- * In addition stores information in structuredReference about each item in a reference if it points to a structered element in the CSN.
49
- *
50
- * @param {*} csn Specifies the CSN to process.
51
- * @param {*} inspectRef Callback function performing the inspection of the references.
52
- * @param {*} isStructured Callback function checking of an artifact is a structured element.
53
- */
54
- resolveAllReferences(csn, inspectRef, isStructured) {
55
- applyTransformations(csn, {
56
- ref: (node, prop, _ref, path) => {
57
- if (!path) return;
58
- let resolved;
59
- try {
60
- resolved = inspectRef(path);
61
- } catch (ex) {
62
- return; // TODO: fix tests: throw Error("Could not inspectRef: " + path.join("/"));
63
- }
64
- if (!resolved)
65
- return; // TODO: fix tests: throw Error("Could not resolve: " + path.join("/"));
66
- if (!resolved.links)
67
- return; // TODO: fix tests: throw Error("Could not resolve links: " + path.join("/"));
68
- let paths = [];
69
- resolved.links.forEach((element) => {
70
- if (!element.art)
71
- paths = undefined; // not resolved -> no paths
72
- if (paths) {
73
- paths.push(element.art.$path);
74
- }
75
- });
76
- if (paths)
77
- setProp(node, '$paths', paths);
78
- // cache if structured or not
79
- let structured = resolved.links.map(link => link.art ? (isStructured(link.art)) : undefined);
80
- this.structuredReference[path.join('/')] = structured;
81
- }
82
- })
83
- }
84
-
85
- /**
86
- * Checks if the provided path identifies a structured node in a CSN.
87
- * It reuses the information collected from the resolveAllReferences function.
88
- *
89
- * @param {Array.<string>} path Array of node names identifying a node in a CSN.
90
- * @returns {boolean}
91
- */
92
- isStructured(path) {
93
- return this.structuredReference[path.join('/')];
94
- }
95
-
96
- /**
97
- * During flattening of structured elements in a CSN,
98
- * leaf-nodes are moved in the root the the artifact.
99
- * This information is stored in form of source and target paths.
100
- *
101
- * @param {Array.<string>} fromPath Path of the source location.
102
- * @param {Array.<string>} toPath Path of the destination location.
103
- */
104
- registerElementTransition(fromPath, toPath) {
105
- let sFromPath = fromPath.join('/');
106
- let sToPath = toPath.join('/');
107
- if (sFromPath === sToPath) return; // same path -> no transition
108
- this.elementTransitions[sFromPath] = toPath;
109
- }
110
-
111
- /**
112
- * For specified path of a node in CSN,
113
- * returns the transition path where the element was moved during the structure flattening.
114
- * It reuses the collected information from function registerElementTransition.
115
- *
116
- * @param {Array.<string>} path The originating path of the node of interest.
117
- * @returns {Array.<string>} The path of the target location in case of element transition.
118
- */
119
- getElementTransition(path) {
120
- let sPath = path.join('/');
121
- return this.elementTransitions[sPath];
122
- }
123
-
124
- /**
125
- * During structure flattening inner structures will be joined.
126
- * Each element joined with its children will be registered using its path in the CSN tree.
127
- * This information will be used in the reference flattener to identify which items to join.
128
- *
129
- * @param {Array.<string>} path Path of the element in the CSN tree.
130
- * @param {Array.<string>} originPath Path of the originating element.
131
- */
132
- registerFlattenedElement(path, originPath) {
133
- let spath = path.join('/');
134
- this.flattenedElementPaths[spath] = true;
135
- if (originPath) {
136
- let sOriginPath = originPath.join('/');
137
- this.flattenedElementPaths[sOriginPath] = true;
138
- }
139
- }
140
-
141
- /**
142
- * During the flattening of structured elements new elements will be produced.
143
- * The list of the new elements is stored under the path of the originating element used as key.
144
- * The information will be used by the foreign key processing module.
145
- *
146
- * @param {Array.<string>} path Path of originating element.
147
- * @param {Array.<string>} elementNames List of generated element names.
148
- */
149
- registerGeneratedElementsForPath(path, elementNames) {
150
- this.generatedElementsForPath[path.join('/')] = elementNames;
151
- }
152
-
153
- /**
154
- * Provides information about all generated elements for specific path in the CSN tree.
155
- *
156
- * @param {Array.<string>} path Path of the element to get the generated elements for.
157
- * @returns {Array.<string>} List of generated names for the specified element path.
158
- */
159
- getGeneratedElementsForPath(path) {
160
- return this.generatedElementsForPath[path.join('/')];
161
- }
162
-
163
- setElementNameWithDots(path, elementNameWithDots) {
164
- this.elementNamesWithDots[path.join('/')] = elementNameWithDots;
165
- }
166
-
167
- getElementNameWithDots(path) {
168
- return this.elementNamesWithDots[path.join('/')];
169
- }
170
-
171
- /**
172
- * Generic reference flattener as {@link ReferenceFlattener described}.
173
- *
174
- * @param {*} csn
175
- */
176
- flattenAllReferences(csn) {
177
- applyTransformations(csn, {
178
- ref: (node, prop, ref, path) => {
179
- if (node.$paths) {
180
- let newRef = []; // flattened reference
181
- let flattenWithPrevious = false;
182
- let lastFlattenedID = null; // The variable will be set to the index of the last flattened path
183
- node.$paths.forEach((path, i) => {
184
- if (path === undefined || !ref[i]) return;
185
- let spath = path.join('/');
186
- let movedTo = this.elementTransitions[spath]; // detect element transition
187
- let flattened = this.flattenedElementPaths[spath];
188
- if (flattenWithPrevious) {
189
- newRef[newRef.length - 1] = newRef[newRef.length - 1] + '_' + (ref[i].id || ref[i]);
190
- // if we have a filter or args in an assoc, it needs to be kept, therefore
191
- // the id of the ref is updated with the flattened version
192
- if (ref[i].id) {
193
- ref[i].id = newRef[newRef.length - 1];
194
- newRef[newRef.length - 1] = ref[i];
195
- }
196
- lastFlattenedID = i;
197
- } else if(movedTo && i===0) { // handle local scope reference which is transitioned - replace first item in reference
198
- let movedToElementName = movedTo[movedTo.length-1];
199
- newRef.push(movedToElementName);
200
- lastFlattenedID = i;
201
- } else {
202
- newRef.push(ref[i]);
203
- }
204
- flattenWithPrevious = flattened;
205
- });
206
- if (newRef !== undefined && lastFlattenedID !== null) { // make sure the reference changed and only then replace it with the new one
207
- // check if this is a column and add alias if missing
208
- let structural = structuralPath(csn, path);
209
- if (isColumnInSelectOrProjection(structural)) {
210
- if (!node.as && lastFlattenedID === ref.length-1) // attach alias only if there is none and it is the last item in the reference that was flattened
211
- node.as = node.ref[node.ref.length - 1];
212
- }
213
- setProp(newRef, '$path', path.concat('ref'));
214
- if (!node.as) {
215
- if (isPartOfKeysStructure(structural))
216
- node.as = implicitAs(node.ref);
217
- else
218
- setProp(node, 'as', implicitAs(node.ref))
219
- }
220
- node.ref = newRef;
221
- }
222
- }
223
- }
224
- })
225
- }
226
-
227
- applyAliasesInOnCond(csn, inspectRef) {
228
- applyTransformations(csn, {
229
- ref: (node, prop, ref, path) => {
230
- // Process only on-conditions of associations
231
- let structural = structuralPath(csn, path);
232
- if(!isOnCondition(structural)) return;
233
- let { links } = inspectRef(path);
234
- if(!links) return; // $user not resolvable
235
- let keysOfPreviousStepWhenManagedAssoc = undefined;
236
-
237
- let aliasedRef = [...ref];
238
-
239
- for (let idx = 0; idx < ref.length; idx++) {
240
- const currArt = links[idx].art;
241
- if (keysOfPreviousStepWhenManagedAssoc) {
242
- const usedKey = keysOfPreviousStepWhenManagedAssoc.find(key => key.ref[0] === ref[idx]);
243
- if (usedKey && usedKey.as) {
244
- aliasedRef.splice(idx, usedKey.ref.length, usedKey.as);
245
- idx += usedKey.ref.length - 1;
246
- keysOfPreviousStepWhenManagedAssoc = undefined;
247
- }
248
- } else {
249
- keysOfPreviousStepWhenManagedAssoc =
250
- (currArt.type === 'cds.Association' || currArt.type === 'cds.Composition') && currArt.keys;
251
- }
252
- }
253
- node.ref = aliasedRef;
254
- }
255
- })
256
- }
257
- }
258
-
259
- /**
260
- * Checks if the provided path is a column inside a key node
261
- * by exploring the possibility of the structural path to ends with 'elements' and 'keys'.
262
- *
263
- * @param structural structural path to explore
264
- * @returns {boolean} True if the provided path matched the requirements to be part of a key node.
265
- */
266
- function isPartOfKeysStructure(structural) {
267
- return structural[structural.length-2] === 'elements'
268
- && structural[structural.length-1] === 'keys';
269
- }
270
-
271
- /**
272
- * Checks if the provided path is a column inside a select or a projection node
273
- * by exploring the possibility of the structural path to contain 'SELECT' or 'projection'
274
- * and ends with 'columns' or 'columns' and 'expand'.
275
- *
276
- * @param structural structural path to explore
277
- * @returns {boolean} True if the provided path matched the requirements to be a select node.
278
- */
279
- function isColumnInSelectOrProjection(structural) {
280
- return (structural.includes('SELECT') || structural.includes('projection'))
281
- && (structural[structural.length-1] === 'columns' || (structural[structural.length-2] === 'columns' && structural[structural.length-1] === 'expand'));
282
- }
283
-
284
- /**
285
- * Checks if the provided path is a column inside an on-condition of an element
286
- * by exploring the possibility of the structural path to ends with 'elements' and 'on'.
287
- *
288
- * @param structural structural path to explore
289
- * @returns {boolean} True if the provided path matched the requirements to be part of an element's on-condition.
290
- */
291
- function isOnCondition(structural) {
292
- return structural[structural.length-2] === 'elements'
293
- && structural[structural.length-1] === 'on';
294
- }
295
-
296
- module.exports = ReferenceFlattener;