@sap/cds-compiler 2.13.6 → 2.15.4

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 (78) hide show
  1. package/CHANGELOG.md +128 -4
  2. package/bin/cdsc.js +112 -37
  3. package/lib/api/main.js +20 -22
  4. package/lib/api/options.js +2 -3
  5. package/lib/api/validate.js +6 -6
  6. package/lib/base/message-registry.js +92 -17
  7. package/lib/base/messages.js +85 -64
  8. package/lib/base/optionProcessorHelper.js +19 -0
  9. package/lib/checks/annotationsOData.js +11 -32
  10. package/lib/checks/arrayOfs.js +1 -34
  11. package/lib/checks/validator.js +2 -4
  12. package/lib/compiler/assert-consistency.js +1 -0
  13. package/lib/compiler/base.js +1 -0
  14. package/lib/compiler/builtins.js +11 -0
  15. package/lib/compiler/checks.js +22 -70
  16. package/lib/compiler/define.js +59 -11
  17. package/lib/compiler/extend.js +20 -3
  18. package/lib/compiler/finalize-parse-cdl.js +26 -20
  19. package/lib/compiler/index.js +75 -26
  20. package/lib/compiler/populate.js +6 -5
  21. package/lib/compiler/propagator.js +4 -1
  22. package/lib/compiler/resolve.js +104 -16
  23. package/lib/compiler/shared.js +61 -27
  24. package/lib/compiler/tweak-assocs.js +7 -1
  25. package/lib/edm/annotations/genericTranslation.js +93 -21
  26. package/lib/edm/csn2edm.js +216 -98
  27. package/lib/edm/edm.js +305 -226
  28. package/lib/edm/edmPreprocessor.js +499 -423
  29. package/lib/edm/edmUtils.js +22 -22
  30. package/lib/gen/Dictionary.json +98 -22
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +3 -1
  33. package/lib/gen/languageParser.js +4636 -4368
  34. package/lib/json/csnVersion.js +10 -11
  35. package/lib/json/from-csn.js +3 -2
  36. package/lib/json/to-csn.js +0 -2
  37. package/lib/language/docCommentParser.js +2 -2
  38. package/lib/language/genericAntlrParser.js +47 -2
  39. package/lib/language/language.g4 +59 -27
  40. package/lib/main.d.ts +19 -1
  41. package/lib/main.js +6 -0
  42. package/lib/model/csnRefs.js +33 -6
  43. package/lib/model/csnUtils.js +193 -75
  44. package/lib/model/enrichCsn.js +1 -0
  45. package/lib/model/revealInternalProperties.js +2 -2
  46. package/lib/modelCompare/compare.js +6 -6
  47. package/lib/optionProcessor.js +62 -26
  48. package/lib/render/toCdl.js +844 -679
  49. package/lib/render/toHdbcds.js +189 -243
  50. package/lib/render/toSql.js +180 -198
  51. package/lib/render/utils/common.js +131 -15
  52. package/lib/transform/db/.eslintrc.json +1 -1
  53. package/lib/transform/db/associations.js +2 -2
  54. package/lib/transform/db/constraints.js +3 -1
  55. package/lib/transform/db/expansion.js +15 -10
  56. package/lib/transform/db/flattening.js +95 -68
  57. package/lib/transform/db/transformExists.js +7 -7
  58. package/lib/transform/db/views.js +6 -3
  59. package/lib/transform/forHanaNew.js +43 -26
  60. package/lib/transform/forOdataNew.js +43 -42
  61. package/lib/transform/localized.js +12 -7
  62. package/lib/transform/odata/toFinalBaseType.js +8 -6
  63. package/lib/transform/odata/typesExposure.js +145 -197
  64. package/lib/transform/transformUtilsNew.js +9 -12
  65. package/lib/transform/translateAssocsToJoins.js +5 -1
  66. package/lib/transform/universalCsn/coreComputed.js +5 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
  68. package/lib/utils/moduleResolve.js +13 -6
  69. package/package.json +1 -1
  70. package/share/messages/message-explanations.json +2 -1
  71. package/share/messages/syntax-expected-integer.md +37 -0
  72. package/lib/transform/odata/attachPath.js +0 -96
  73. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  74. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  75. package/lib/transform/odata/referenceFlattener.js +0 -296
  76. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  77. package/lib/transform/odata/structuralPath.js +0 -72
  78. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -28,13 +28,18 @@ const extensions = [ '.cds', '.csn', '.json' ];
28
28
  * - Why a global? The Umbrella could pass it as an option.
29
29
  *
30
30
  * @param {string} modulePath
31
+ * @param {CSN.Options} options
31
32
  * @returns {string}
32
33
  */
33
- function adaptCdsModule(modulePath) {
34
- // eslint-disable-next-line
35
- if (global['cds'] && global['cds'].home && modulePath.startsWith( '@sap/cds/' ))
34
+ function adaptCdsModule(modulePath, options = {}) {
35
+ if (modulePath.startsWith( '@sap/cds/' )) {
36
+ if (options.cdsHome)
37
+ return options.cdsHome + modulePath.slice(8);
36
38
  // eslint-disable-next-line
37
- return global['cds'].home + modulePath.slice(8)
39
+ if (global['cds'] && global['cds'].home)
40
+ // eslint-disable-next-line
41
+ return global['cds'].home + modulePath.slice(8);
42
+ }
38
43
  return modulePath;
39
44
  }
40
45
 
@@ -42,6 +47,7 @@ function adaptCdsModule(modulePath) {
42
47
  * @param {object} dep
43
48
  * @param {object} fileCache
44
49
  * @param {CSN.Options} options
50
+ * @param {object} messageFunctions
45
51
  */
46
52
  function resolveModule( dep, fileCache, options, messageFunctions ) {
47
53
  const _fs = cdsFs(fileCache, options.traceFs);
@@ -58,7 +64,7 @@ function resolveModule( dep, fileCache, options, messageFunctions ) {
58
64
  realpath: _fs.realpath,
59
65
  };
60
66
  return new Promise( (fulfill, reject) => {
61
- const lookupPath = adaptCdsModule(dep.module);
67
+ const lookupPath = adaptCdsModule(dep.module, options);
62
68
  resolveCDS( lookupPath, opts, (err, res) => {
63
69
  // console.log('RESOLVE', dep, res, err)
64
70
  if (err) {
@@ -105,6 +111,7 @@ function resolveModule( dep, fileCache, options, messageFunctions ) {
105
111
  * @param {object} dep
106
112
  * @param {object} fileCache
107
113
  * @param {CSN.Options} options
114
+ * @param {object} messageFunctions
108
115
  */
109
116
  function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
110
117
  const _fs = cdsFs(fileCache, options.traceFs);
@@ -118,7 +125,7 @@ function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
118
125
 
119
126
  let result = null;
120
127
  let error = null;
121
- const lookupPath = adaptCdsModule(dep.module);
128
+ const lookupPath = adaptCdsModule(dep.module, options);
122
129
 
123
130
  resolveCDS( lookupPath, opts, (err, res) => {
124
131
  if (err)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "2.13.6",
3
+ "version": "2.15.4",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -9,6 +9,7 @@
9
9
  "extend-unrelated-layer",
10
10
  "redirected-to-ambiguous",
11
11
  "redirected-to-unrelated",
12
- "rewrite-not-supported"
12
+ "rewrite-not-supported",
13
+ "syntax-expected-integer"
13
14
  ]
14
15
  }
@@ -0,0 +1,37 @@
1
+ # syntax-expected-integer
2
+
3
+ The compiler expects a safe integer here.
4
+ The last safe Integer is `2^53 - 1` or `9007199254740991`.
5
+
6
+ A safe integer is an integer that
7
+
8
+ - can be exactly represented as an IEEE-754 double precision number, and
9
+ - whose IEEE-754 representation cannot be the result of rounding any
10
+ other integer to fit the IEEE-754 representation.
11
+
12
+ The message's severity is `Error`.
13
+
14
+ ## Example
15
+
16
+ Erroneous code example:
17
+
18
+ ```cdl
19
+ type LengthIsUnsafe : String(9007199254740992);
20
+ type NotAnInteger : String(42.1);
21
+ ```
22
+
23
+ In the above example, the string length for the type `LengthIsUnsafe` is not a
24
+ safe Integer. It is too large.
25
+ Likewise, the string length for the type `NotAnInteger` is a decimal.
26
+
27
+ ## How to Fix
28
+
29
+ To fix the issue, you have to provide a safe integer:
30
+
31
+ ```cdl
32
+ type LengthIsSafe : String(9007199254740991);
33
+ type AnInteger : String(42);
34
+ ```
35
+
36
+ If not feasible, a string representation of the number needs to be used,
37
+ e.g. in annotation values.
@@ -1,96 +0,0 @@
1
- // The module traverses over a CSN or partial one and sets $path on the non-structural nodes and references.
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: traverseRef,
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
- }
30
-
31
- function attachPath(csn) {
32
- traverseDict(csn.definitions, ['definitions']);
33
- }
34
-
35
- function attachPathOnPartialCSN(csnPart, pathPrefix) {
36
- if(Array.isArray(csnPart))
37
- traverseArray(csnPart, pathPrefix);
38
- else
39
- traverseDict(csnPart, pathPrefix);
40
- }
41
-
42
- function traverseRef(obj, path) {
43
- if(!obj) return;
44
- setPath(obj, path);
45
- traverseArray(obj, path);
46
- }
47
-
48
- function traverseArray(obj, path) {
49
- if(!Array.isArray(obj)) return;
50
- obj.forEach( ( element, index ) => traverseTyped(element, path.concat(index)));
51
- }
52
-
53
- function traverseDict(obj, path) {
54
- if(!obj || typeof obj !== 'object') return;
55
- forAllEnumerableProperties(obj, name => {
56
- const ipath = path.concat(name);
57
- setPath(obj[name], ipath);
58
- traverseTyped(obj[name], ipath);
59
- })
60
- }
61
-
62
- function traverseDictArray(obj, path) {
63
- if(!obj || typeof obj !== 'object') return;
64
- forAllEnumerableProperties(obj, name => {
65
- const ipath = path.concat(name);
66
- setPath(obj[name], ipath);
67
- traverseArray(obj[name], ipath);
68
- })
69
- }
70
-
71
- function traverseTyped(obj, path) {
72
- if(!obj || typeof obj !== 'object') return;
73
- forAllEnumerableProperties(obj, name => {
74
- if(name[0]==='@') return; // skip annotations
75
- const func = structuralNodeHandlers[name];
76
- if(func)
77
- func(obj[name], path.concat(name));
78
- else if(path[path.length-2] === 'columns')
79
- traverseDictArray(obj[name], path.concat(name)); // for columns
80
- })
81
- }
82
-
83
- function setPath(obj, path) {
84
- if(!obj || typeof obj !== 'object') return;
85
- if(path.length>0)
86
- Object.defineProperty( obj, '$path', { value: path, configurable: true, writable: true, enumerable: false } );
87
- }
88
-
89
- function forAllEnumerableProperties(obj, callback) {
90
- Object.keys(obj).forEach(callback);
91
- }
92
-
93
- module.exports = {
94
- attachPath,
95
- attachPathOnPartialCSN,
96
- }
@@ -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
- }