@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.
- package/CHANGELOG.md +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
|
@@ -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.isManagedAssociationElement(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
|
-
}
|