@sap/cds-compiler 2.12.0 → 2.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  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 -290
  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
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -1,290 +0,0 @@
1
- const { forEachRef } = 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
- forEachRef(csn, (_ref, node, path) => {
56
- if (!path) return;
57
- let resolved;
58
- try {
59
- resolved = inspectRef(path);
60
- } catch (ex) {
61
- return; // TODO: fix tests: throw Error("Could not inspectRef: " + path.join("/"));
62
- }
63
- if (!resolved)
64
- return; // TODO: fix tests: throw Error("Could not resolve: " + path.join("/"));
65
- if (!resolved.links)
66
- return; // TODO: fix tests: throw Error("Could not resolve links: " + path.join("/"));
67
- let paths = [];
68
- resolved.links.forEach((element) => {
69
- if (!element.art)
70
- paths = undefined; // not resolved -> no paths
71
- if (paths) {
72
- paths.push(element.art.$path);
73
- }
74
- });
75
- if (paths)
76
- setProp(node, '$paths', paths);
77
- // cache if structured or not
78
- let structured = resolved.links.map(link => link.art ? (isStructured(link.art)) : undefined);
79
- this.structuredReference[path.join('/')] = structured;
80
- });
81
- }
82
-
83
- /**
84
- * Checks if the provided path identifies a structured node in a CSN.
85
- * It reuses the information collected from the resolveAllReferences function.
86
- *
87
- * @param {Array.<string>} path Array of node names identifying a node in a CSN.
88
- * @returns {boolean}
89
- */
90
- isStructured(path) {
91
- return this.structuredReference[path.join('/')];
92
- }
93
-
94
- /**
95
- * During flattening of structured elements in a CSN,
96
- * leaf-nodes are moved in the root the the artifact.
97
- * This information is stored in form of source and target paths.
98
- *
99
- * @param {Array.<string>} fromPath Path of the source location.
100
- * @param {Array.<string>} toPath Path of the destination location.
101
- */
102
- registerElementTransition(fromPath, toPath) {
103
- let sFromPath = fromPath.join('/');
104
- let sToPath = toPath.join('/');
105
- if (sFromPath === sToPath) return; // same path -> no transition
106
- this.elementTransitions[sFromPath] = toPath;
107
- }
108
-
109
- /**
110
- * For specified path of a node in CSN,
111
- * returns the transition path where the element was moved during the structure flattening.
112
- * It reuses the collected information from function registerElementTransition.
113
- *
114
- * @param {Array.<string>} path The originating path of the node of interest.
115
- * @returns {Array.<string>} The path of the target location in case of element transition.
116
- */
117
- getElementTransition(path) {
118
- let sPath = path.join('/');
119
- return this.elementTransitions[sPath];
120
- }
121
-
122
- /**
123
- * During structure flattening inner structures will be joined.
124
- * Each element joined with its children will be registered using its path in the CSN tree.
125
- * This information will be used in the reference flattener to identify which items to join.
126
- *
127
- * @param {Array.<string>} path Path of the element in the CSN tree.
128
- * @param {Array.<string>} originPath Path of the originating element.
129
- */
130
- registerFlattenedElement(path, originPath) {
131
- let spath = path.join('/');
132
- this.flattenedElementPaths[spath] = true;
133
- if (originPath) {
134
- let sOriginPath = originPath.join('/');
135
- this.flattenedElementPaths[sOriginPath] = true;
136
- }
137
- }
138
-
139
- /**
140
- * During the flattening of structured elements new elements will be produced.
141
- * The list of the new elements is stored under the path of the originating element used as key.
142
- * The information will be used by the foreign key processing module.
143
- *
144
- * @param {Array.<string>} path Path of originating element.
145
- * @param {Array.<string>} elementNames List of generated element names.
146
- */
147
- registerGeneratedElementsForPath(path, elementNames) {
148
- this.generatedElementsForPath[path.join('/')] = elementNames;
149
- }
150
-
151
- /**
152
- * Provides information about all generated elements for specific path in the CSN tree.
153
- *
154
- * @param {Array.<string>} path Path of the element to get the generated elements for.
155
- * @returns {Array.<string>} List of generated names for the specified element path.
156
- */
157
- getGeneratedElementsForPath(path) {
158
- return this.generatedElementsForPath[path.join('/')];
159
- }
160
-
161
- setElementNameWithDots(path, elementNameWithDots) {
162
- this.elementNamesWithDots[path.join('/')] = elementNameWithDots;
163
- }
164
-
165
- getElementNameWithDots(path) {
166
- return this.elementNamesWithDots[path.join('/')];
167
- }
168
-
169
- /**
170
- * Generic reference flattener as {@link ReferenceFlattener described}.
171
- *
172
- * @param {*} csn
173
- */
174
- flattenAllReferences(csn) {
175
- forEachRef(csn, (ref, node, path) => {
176
- if (node.$paths) {
177
- let newRef = []; // flattened reference
178
- let flattenWithPrevious = false;
179
- let lastFlattenedID = null; // The variable will be set to the index of the last flattened path
180
- node.$paths.forEach((path, i) => {
181
- if (path === undefined || !ref[i]) return;
182
- let spath = path.join('/');
183
- let movedTo = this.elementTransitions[spath]; // detect element transition
184
- let flattened = this.flattenedElementPaths[spath];
185
- if (flattenWithPrevious) {
186
- newRef[newRef.length - 1] = newRef[newRef.length - 1] + '_' + (ref[i].id || ref[i]);
187
- // if we have a filter or args in an assoc, it needs to be kept, therefore
188
- // the id of the ref is updated with the flattened version
189
- if (ref[i].id) {
190
- ref[i].id = newRef[newRef.length - 1];
191
- newRef[newRef.length - 1] = ref[i];
192
- }
193
- lastFlattenedID = i;
194
- } else if(movedTo && i===0) { // handle local scope reference which is transitioned - replace first item in reference
195
- let movedToElementName = movedTo[movedTo.length-1];
196
- newRef.push(movedToElementName);
197
- lastFlattenedID = i;
198
- } else {
199
- newRef.push(ref[i]);
200
- }
201
- flattenWithPrevious = flattened;
202
- });
203
- if (newRef !== undefined && lastFlattenedID !== null) { // make sure the reference changed and only then replace it with the new one
204
- // check if this is a column and add alias if missing
205
- let structural = structuralPath(csn, path);
206
- if (isColumnInSelectOrProjection(structural)) {
207
- 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
208
- node.as = node.ref[node.ref.length - 1];
209
- }
210
- setProp(newRef, '$path', path.concat('ref'));
211
- if (!node.as) {
212
- if (isPartOfKeysStructure(structural))
213
- node.as = implicitAs(node.ref);
214
- else
215
- setProp(node, 'as', implicitAs(node.ref))
216
- }
217
- node.ref = newRef;
218
- }
219
- }
220
- })
221
- }
222
-
223
- applyAliasesInOnCond(csn, inspectRef) {
224
- forEachRef(csn, (ref, node, path) => {
225
- // Process only on-conditions of associations
226
- let structural = structuralPath(csn, path);
227
- if(!isOnCondition(structural)) return;
228
- let { links } = inspectRef(path);
229
- if(!links) return; // $user not resolvable
230
- let keysOfPreviousStepWhenManagedAssoc = undefined;
231
-
232
- let aliasedRef = [...ref];
233
-
234
- for (let idx = 0; idx < ref.length; idx++) {
235
- const currArt = links[idx].art;
236
- if (keysOfPreviousStepWhenManagedAssoc) {
237
- const usedKey = keysOfPreviousStepWhenManagedAssoc.find(key => key.ref[0] === ref[idx]);
238
- if (usedKey && usedKey.as) {
239
- aliasedRef.splice(idx, usedKey.ref.length, usedKey.as);
240
- idx += usedKey.ref.length - 1;
241
- keysOfPreviousStepWhenManagedAssoc = undefined;
242
- }
243
- } else {
244
- keysOfPreviousStepWhenManagedAssoc =
245
- (currArt.type === 'cds.Association' || currArt.type === 'cds.Composition') && currArt.keys;
246
- }
247
- }
248
- node.ref = aliasedRef;
249
- })
250
- }
251
- }
252
-
253
- /**
254
- * Checks if the provided path is a column inside a key node
255
- * by exploring the possibility of the structural path to ends with 'elements' and 'keys'.
256
- *
257
- * @param structural structural path to explore
258
- * @returns {boolean} True if the provided path matched the requirements to be part of a key node.
259
- */
260
- function isPartOfKeysStructure(structural) {
261
- return structural[structural.length-2] === 'elements'
262
- && structural[structural.length-1] === 'keys';
263
- }
264
-
265
- /**
266
- * Checks if the provided path is a column inside a select or a projection node
267
- * by exploring the possibility of the structural path to contain 'SELECT' or 'projection'
268
- * and ends with 'columns' or 'columns' and 'expand'.
269
- *
270
- * @param structural structural path to explore
271
- * @returns {boolean} True if the provided path matched the requirements to be a select node.
272
- */
273
- function isColumnInSelectOrProjection(structural) {
274
- return (structural.includes('SELECT') || structural.includes('projection'))
275
- && (structural[structural.length-1] === 'columns' || (structural[structural.length-2] === 'columns' && structural[structural.length-1] === 'expand'));
276
- }
277
-
278
- /**
279
- * Checks if the provided path is a column inside an on-condition of an element
280
- * by exploring the possibility of the structural path to ends with 'elements' and 'on'.
281
- *
282
- * @param structural structural path to explore
283
- * @returns {boolean} True if the provided path matched the requirements to be part of an element's on-condition.
284
- */
285
- function isOnCondition(structural) {
286
- return structural[structural.length-2] === 'elements'
287
- && structural[structural.length-1] === 'on';
288
- }
289
-
290
- module.exports = ReferenceFlattener;
@@ -1,105 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * In the OData transformer, managed associations produce foreign keys.
5
- * If an association is also a primary key, the additionally created foreign keys become also primary keys in the parent artifact.
6
- * Associations with partial foreign keys force other non-key elements and associations to become virtual primary keys.
7
- * Proper FK generation requires specific order of performing that.
8
- * This module provides functionality to sort managed associations according to their dependencies.
9
- */
10
-
11
- const {
12
- forEachDefinition,
13
- forEachMemberRecursively
14
- } = require('../../model/csnUtils');
15
-
16
- const {
17
- isAssociationOrComposition
18
- } = require('./utils');
19
- const { forEach } = require('../../utils/objectUtils.js');
20
-
21
- function buildDependenciesFor(csn, referenceFlattener, isExternalServiceMember) {
22
-
23
- let dependencies = {};
24
- forEachDefinition(csn, (def, definitionName) => {
25
- /** @type {CSN.Path} */
26
- let root = ['definitions', definitionName];
27
- forEachMemberRecursively(def, (element, elementName, structuralNodeName, subpath, parent) => {
28
- let path = root.concat(subpath);
29
- // go only through managed associations and compositions
30
- if (isAssociationOrComposition(element) && element.keys && !element.on) { // check association FKs
31
- let elementDependencies = []
32
- element.keys.forEach(iForeignKey => {
33
- let paths = iForeignKey.$paths;
34
- if (!paths) return; // invalid references can not be resolved thus no $paths -> test/odataTransformation/negative/ForeignKeys.cds
35
- let targetElementPath = paths[paths.length - 1];
36
- if (!targetElementPath) return; // TODO check why
37
- let targetElement = getElementForPath(csn, targetElementPath);
38
- if (!targetElement) { // element was moved
39
- targetElementPath = referenceFlattener.getElementTransition(targetElementPath);
40
- if (!targetElementPath) return; // TODO check why
41
- targetElement = getElementForPath(csn, targetElementPath);
42
- }
43
- if (targetElement && isAssociationOrComposition(targetElement)) {
44
- elementDependencies.push(targetElementPath);
45
- }
46
- })
47
- dependencies[path.join("/")] = { structuralNodeName, definitionName, elementName, element, path, parent, dependencies: elementDependencies };
48
- }
49
- }) // forEachMemberRecursively
50
- }, { skipArtifact: isExternalServiceMember }) // forEachDefinition
51
-
52
- let result = []; // final list of sorted association items
53
- let inResult = {}; // paths of associations which were added in the result
54
- let maxLoops = Object.keys(dependencies).length;
55
- let loops = 0;
56
- let done = false;
57
- // walk over all dependencies and add to the result if all dependents are already in the result
58
- while (!done) {
59
- if (loops > maxLoops) {
60
- throw Error('Failed to process the association dependencies - max loops reached');
61
- }
62
- done = true;
63
- let toDelete = [];
64
- forEach(dependencies, (name, item) => {
65
- let countOfUnprocessedDependents = 0;
66
- item.dependencies.forEach(path => {
67
- let spath = path.join('/');
68
- if (!inResult[spath]) countOfUnprocessedDependents++;
69
- })
70
- if (countOfUnprocessedDependents === 0) { // all dependents processed?
71
- let spath = item.path.join('/');
72
- if (!inResult[spath]) {
73
- inResult[spath] = true;
74
- result.push(item);
75
- }
76
- toDelete.push(name); // mark association to be removed from the processing list
77
- } else {
78
- done = false;
79
- }
80
- })
81
- // delete already processed associations
82
- toDelete.forEach(name => {
83
- delete dependencies[name];
84
- });
85
- loops++;
86
- } // while not done
87
-
88
- // check if all dependencies were processed
89
- if (Object.keys(dependencies).length !== 0) {
90
- throw Error('Failed to process the association dependencies - there are more dependencies left');
91
- }
92
- return result;
93
-
94
- function getElementForPath(node, path) {
95
- path.forEach(name => {
96
- if (!node) return;
97
- node = node[name];
98
- })
99
- return node;
100
- }
101
-
102
- }
103
-
104
- module.exports = buildDependenciesFor
105
-
@@ -1,72 +0,0 @@
1
- // The module traverses a given CSN using a specific path, collects structural node names and returns them.
2
-
3
- const structuralNodeHandlers = {
4
- definitions: traverseDict,
5
- elements: traverseDict,
6
- actions: traverseDict,
7
- params: traverseDict,
8
- items: traverseTyped,
9
- enum: traverseDict,
10
- returns: traverseTyped,
11
- on: traverseArray,
12
- keys: traverseArray,
13
- ref: traverseArray,
14
- query: traverseTyped,
15
- SELECT: traverseTyped,
16
- SET: traverseTyped,
17
- args: traverseArray,
18
- columns: traverseArray,
19
- projection: traverseTyped,
20
- from: traverseTyped,
21
- mixin: traverseDict,
22
- where: traverseArray,
23
- orderBy: traverseArray,
24
- groupBy: traverseArray,
25
- having: traverseArray,
26
- xpr: traverseArray,
27
- expand: traverseArray,
28
- inline: traverseArray,
29
- cast: traverseTyped,
30
- }
31
-
32
- function structuralPath(csn, path) {
33
- return traverseDict(csn.definitions, path, 1, ['definitions']);
34
- }
35
-
36
- function traverseArray(obj, path, index, typeStack) {
37
- if(!Array.isArray(obj)) return typeStack;
38
- const name = path[index];
39
- const element = obj[name];
40
- return traverseTyped(element, path, index+1, typeStack);
41
- }
42
-
43
- function traverseDict(obj, path, index, typeStack) {
44
- if(typeof obj !== 'object') return typeStack;
45
- const name = path[index];
46
- if(name === undefined) return typeStack;
47
- return traverseTyped(obj[name], path, index+1, typeStack);
48
- }
49
-
50
- function traverseDictArray(obj, path, index, typeStack) {
51
- if(typeof obj !== 'object') return typeStack;
52
- const name = path[index];
53
- if(name === undefined) return typeStack;
54
- return traverseArray(obj[name], path, index+1, typeStack);
55
- }
56
-
57
- function traverseTyped(obj, path, index, typeStack) {
58
- if(!obj) return typeStack;
59
- const name = path[index];
60
- if(name === undefined) return typeStack;
61
- if(name[0] === '@') return typeStack; // skip annotations
62
- const func = structuralNodeHandlers[name];
63
- if(func) return func(obj[name], path, index+1, typeStack.concat(name));
64
- // not typed -> columns?
65
- if(typeStack[typeStack.length-1] === 'columns')
66
- return traverseDictArray(obj, path, index, typeStack);
67
- return typeStack;
68
- }
69
-
70
- module.exports = {
71
- structuralPath
72
- }
@@ -1,171 +0,0 @@
1
- 'use strict';
2
-
3
- const { copyAnnotations } = require('../../model/csnUtils');
4
- const { cloneCsn, forEachDefinition } = require('../../model/csnUtils');
5
- const { attachPathOnPartialCSN } = require('./attachPath');
6
-
7
- // These functions are used for propagation of the annotations, virtual, key,
8
- // notNull attributes collected along the path during flattening.
9
- const { addPropsForPropagationFromElement, propagatePropsToElement, resetPropsForPropagation } = function () {
10
- let toBePropagated = Object.create(null);
11
- return {
12
- addPropsForPropagationFromElement: function (element) {
13
- copyAnnotations(element, toBePropagated);
14
- if (element.virtual) toBePropagated.virtual = element.virtual;
15
- if (element.key) toBePropagated.key = element.key;
16
- },
17
- propagatePropsToElement: function (element) {
18
- copyAnnotations(toBePropagated, element);
19
- if (toBePropagated.virtual) element.virtual = toBePropagated.virtual;
20
- if (toBePropagated.key) element.key = toBePropagated.key;
21
- },
22
- resetPropsForPropagation: function () {
23
- toBePropagated = Object.create(null);
24
- }
25
- }
26
- }();
27
-
28
- // keep here the state of the 'notNull' attribute
29
- // this is needed because during flattening all the elements
30
- // along the chain need to be assigned with not null so
31
- // the resulting element to be not null as well
32
- const { isNotNull, setNotNull, setUpNotNull } = function () {
33
- let notNull = undefined;
34
- return {
35
- isNotNull: function () {
36
- return notNull;
37
- },
38
- setNotNull: function (value) {
39
- notNull = value;
40
- },
41
- setUpNotNull: function (element, isParentNotNull) {
42
- if (isParentNotNull && element.notNull) setNotNull(element.notNull);
43
- else if (isNotNull() && !element.notNull || (isNotNull() === false && element.notNull !== false)) setNotNull(undefined);
44
- }
45
- }
46
- }();
47
-
48
- /**
49
- * During the OData transformations in flat-mode, all structured elements will be flattened.
50
- * This module performs the complete flattening.
51
- * It also provides information to the reference flattener: elements produced for specific path in the CSN structure.
52
- * @param {CSN.Model} csn CSN-object to flatten
53
- * @param {*} csnUtils instances of utility functions
54
- * @param {*} options
55
- * @param {*} referenceFlattener
56
- * @param {Function} error
57
- * @param {Function} isExternalServiceMember returns true for an artifact that is part of an external service
58
- */
59
- function flattenCSN(csn, csnUtils, options, referenceFlattener, error, isExternalServiceMember) {
60
- forEachDefinition(csn, (def, defName, propertyName, path) =>
61
- flattenDefinition(def, path, csnUtils, options, referenceFlattener, error), { skipArtifact: isExternalServiceMember });
62
- }
63
-
64
- /**
65
- * Flattens one single definition and all structures in it. Modifies the definition in place.
66
- * @param {CSN.Definition} definition definition object to flatten
67
- * @param {CSN.Path} definitionPath path in CSN object
68
- * @param {*} csnUtils utility functions
69
- * @param {*} options
70
- * @param {*} referenceFlattener
71
- * @param {Function} error
72
- */
73
- function flattenDefinition(definition, definitionPath, csnUtils, options, referenceFlattener, error) {
74
- if (definition.kind !== 'entity' && definition.kind !== 'view')
75
- return;
76
-
77
- let { newFlatElements } = flattenStructure(definition.elements, definitionPath, csnUtils, options, error, referenceFlattener);
78
-
79
- attachPathOnPartialCSN(newFlatElements, definitionPath.concat('elements'));
80
- definition.elements = newFlatElements;
81
-
82
- if (definition.params) {
83
- let { newFlatElements } = flattenStructure(definition.params, definitionPath, csnUtils, options, error, referenceFlattener);
84
- attachPathOnPartialCSN(newFlatElements, definitionPath.concat('params'));
85
- definition.params = newFlatElements;
86
- }
87
- } // flattenDefinition
88
-
89
- /**
90
- * Flattens structured element by calling element flattener for each structured child.
91
- * Returns a dictionary containing all the new elements for the given structure.
92
- * @param {*} dictionary to flatten
93
- * @param {CSN.Path} path the path of the structure in the CSN tree
94
- * @param {*} csnUtils
95
- * @param {Function} error Error message function
96
- * @param {*} [referenceFlattener]
97
- * @param {string[]} [elementPathInStructure] list of parent element names
98
- * @param {*} [newFlatElements]
99
- * @param {boolean} [isTopLevelElement] states if this is a top level element
100
- */
101
- function flattenStructure(dictionary, path, csnUtils, options, error, referenceFlattener = undefined, elementPathInStructure = [],
102
- newFlatElements = Object.create(null), isTopLevelElement = true, isParentNotNull = false) {
103
-
104
- if (!isTopLevelElement) addPropsForPropagationFromElement(dictionary);
105
-
106
- let generatedNewFlatElementsNames = []; // holds the names of all new child elements of the structure
107
-
108
- dictionary && Object.entries(dictionary).forEach(([elementName, element]) => {
109
- let currPath = path.concat('elements', elementName);
110
-
111
- if (isTopLevelElement) {
112
- resetPropsForPropagation();
113
- setNotNull(element.notNull)
114
- } else {
115
- setUpNotNull(element, isParentNotNull);
116
- }
117
-
118
- // flat elements when structured and NOT empty (allow incomplete structures - cds-compiler#4337)
119
- if (csnUtils.isStructured(element) && !(element.elements && Object.keys(element.elements).length === 0)) {
120
-
121
- if (referenceFlattener) referenceFlattener.registerFlattenedElement(currPath, element.$path);
122
-
123
- addPropsForPropagationFromElement(element);
124
-
125
- // if the child element is structured itself -> needs to be flattened
126
- const elements = element.elements || csnUtils.getFinalBaseType(element.type).elements;
127
- let result = flattenStructure(elements, currPath, csnUtils, options, error, referenceFlattener, elementPathInStructure.concat(elementName), newFlatElements, false, isNotNull());
128
- generatedNewFlatElementsNames.push(...result.generatedNewFlatElementsNames); // accomulate names of produced elements
129
-
130
- } else { // when we do not need to flat, this is scalar or empty (cds-compiler#4337) -> needs to be registered in referenceFlattener
131
- let newElementName = elementPathInStructure.concat(elementName).join('_');
132
- let elementNameWithDots = elementPathInStructure.concat(elementName).join('.');
133
- addNewElementToResult(element, newElementName, elementNameWithDots, currPath);
134
- }
135
- });
136
-
137
- if (referenceFlattener) {
138
- referenceFlattener.registerGeneratedElementsForPath(path, generatedNewFlatElementsNames);
139
- }
140
- return { newFlatElements, generatedNewFlatElementsNames };
141
-
142
- // adds newly created element into the final dictionary of elements
143
- function addNewElementToResult(element, elementName, elementNameWithDots, path) {
144
- if (newFlatElements[elementName]) {
145
- error(null, path, `Generated element ${elementName} conflicts with other generated element`);
146
- } else {
147
- let newPath = path.slice(0, 2).concat('elements', elementName);
148
- let newElement = createNewElement(element, elementNameWithDots, newPath);
149
- newFlatElements[elementName] = newElement;
150
- generatedNewFlatElementsNames.push(elementName);
151
-
152
- if (referenceFlattener) {
153
- referenceFlattener.registerElementTransition(path, newPath);
154
- }
155
- }
156
- } // addNewElementToResult
157
-
158
- // creates new element by copying the properties of the originating element
159
- function createNewElement(element, elementNameWithDots, path) {
160
- let newElement = cloneCsn(element, options);
161
- if (!isTopLevelElement) propagatePropsToElement(newElement);
162
- if (isNotNull() === undefined) delete newElement.notNull;
163
- if (!isTopLevelElement && referenceFlattener) {
164
- referenceFlattener.setElementNameWithDots(path, elementNameWithDots);
165
- }
166
- return newElement;
167
- } // createNewElement
168
-
169
- } // flattenStructure
170
-
171
- module.exports = { flattenCSN, flattenStructure };