-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathentity-helper.js
196 lines (170 loc) · 6.42 KB
/
entity-helper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
const cds = require("@sap/cds")
const LOG = cds.log("change-log")
const getNameFromPathVal = function (pathVal) {
return /^(.+?)\(/.exec(pathVal)?.[1] || ""
}
const getUUIDFromPathVal = function (pathVal) {
const regRes = /\((.+?)\)/.exec(pathVal)
return regRes ? regRes[1] : ""
}
const getEntityByContextPath = function (aPath, hasComp = false) {
if (hasComp) return cds.model.definitions[aPath[aPath.length - 1]]
let entity = cds.model.definitions[aPath[0]]
for (let each of aPath.slice(1)) {
entity = entity.elements[each]?._target
}
return entity
}
const getObjIdElementNamesInArray = function (elements) {
if (Array.isArray(elements)) return elements.map(e => {
const splitted = (e["="]||e).split('.')
splitted.shift()
return splitted.join('.')
})
else return []
}
const getCurObjFromDbQuery = async function (entityName, queryVal, /**optional*/ queryKey='ID') {
if (!queryVal) return {}
// REVISIT: This always reads all elements -> should read required ones only!
const obj = await SELECT.one.from(entityName).where({[queryKey]: queryVal})
return obj || {}
}
const getCurObjFromReqData = function (reqData, nodePathVal, pathVal) {
const pathVals = pathVal.split('/')
const rootNodePathVal = pathVals[0]
let curReqObj = reqData || {}
if (nodePathVal === rootNodePathVal) return curReqObj
else pathVals.shift()
let parentSrvObjName = getNameFromPathVal(rootNodePathVal)
for (const subNodePathVal of pathVals) {
const srvObjName = getNameFromPathVal(subNodePathVal)
const curSrvObjUUID = getUUIDFromPathVal(subNodePathVal)
const associationName = _getAssociationName(parentSrvObjName, srvObjName)
if (curReqObj) {
let associationData = curReqObj[associationName]
if (!Array.isArray(associationData)) associationData = [associationData]
curReqObj = associationData?.find(x => x?.ID === curSrvObjUUID) || {}
}
if (subNodePathVal === nodePathVal) return curReqObj || {}
parentSrvObjName = srvObjName
}
return curReqObj
function _getAssociationName(entity, target) {
const source = cds.model.definitions[entity]
const assocs = source.associations
for (const each in assocs) {
if (assocs[each].target === target) return each
}
}
}
async function getObjectId (reqData, entityName, fields, curObj) {
let all = [], { curObjFromReqData: req_data={}, curObjFromDbQuery: db_data={} } = curObj
let entity = cds.model.definitions[entityName]
if (!fields?.length) fields = entity["@changelog"]?.map?.(k => k['='] || k) || []
for (let field of fields) {
let path = field.split('.')
if (path.length > 1) {
let current = entity, _db_data = db_data
while (path.length > 1) {
let assoc = current.elements[path[0]]; if (!assoc?.isAssociation) break
let foreignKey = assoc.keys?.[0]?.$generatedFieldName
let IDval =
req_data[foreignKey] && current.name === entityName
? req_data[foreignKey]
: _db_data[foreignKey]
if (!IDval) {
_db_data = {};
} else try {
// REVISIT: This always reads all elements -> should read required ones only!
let ID = assoc.keys?.[0]?.ref[0] || 'ID'
const isComposition = hasComposition(assoc._target, current)
// Peer association and composition are distinguished by the value of isComposition.
if (isComposition) {
// This function can recursively retrieve the desired information from reqData without having to read it from db.
_db_data = _getCompositionObjFromReq(reqData, IDval)
// When multiple layers of child nodes are deleted at the same time, the deep layer of child nodes will lose the information of the upper nodes, so data needs to be extracted from the db.
const entityKeys = reqData ? Object.keys(reqData).filter(item => !Object.keys(assoc._target.keys).some(ele => item === ele)) : [];
if (!_db_data || JSON.stringify(_db_data) === '{}' || entityKeys.length === 0) {
_db_data = await getCurObjFromDbQuery(assoc._target, IDval, ID);
}
} else {
_db_data = await getCurObjFromDbQuery(assoc._target, IDval, ID);
}
} catch (e) {
LOG.error("Failed to generate object Id for an association entity.", e)
throw new Error("Failed to generate object Id for an association entity.", e)
}
current = assoc._target
path.shift()
}
field = path.join('_')
let obj = current.name === entityName && req_data[field] ? req_data[field] : _db_data[field]
if (obj) all.push(obj)
} else {
let e = entity.elements[field]
if (e?.isAssociation) field = e.keys?.[0]?.$generatedFieldName
let obj = req_data[field] || db_data[field]
if (obj) all.push(obj)
}
}
return all.join(', ')
}
const getDBEntity = (entity) => {
if (typeof entity === 'string') entity = cds.model.definitions[entity]
let proto = Reflect.getPrototypeOf(entity)
if (proto instanceof cds.entity) return proto
}
const getValueEntityType = function (entityName, fields) {
const types=[], entity = cds.model.definitions[entityName]
for (let field of fields) {
let current = entity, path = field.split('.')
if (path.length > 1) {
for (;;) {
let target = current.elements[path[0]]?._target
if (target) current = target; else break
path.shift()
}
field = path.join('_')
}
let e = current.elements[field]
if (e) types.push(e.type)
}
return types.join(', ')
}
const hasComposition = function (parentEntity, subEntity) {
if (!parentEntity.compositions) {
return false
}
const compositions = Object.values(parentEntity.compositions);
for (const composition of compositions) {
if (composition.target === subEntity.name) {
return true;
}
}
return false
}
const _getCompositionObjFromReq = function (obj, targetID) {
if (obj?.ID === targetID) {
return obj;
}
for (const key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
const result = _getCompositionObjFromReq(obj[key], targetID);
if (result) {
return result;
}
}
}
return null;
};
module.exports = {
getCurObjFromReqData,
getCurObjFromDbQuery,
getObjectId,
getNameFromPathVal,
getUUIDFromPathVal,
getDBEntity,
getEntityByContextPath,
getObjIdElementNamesInArray,
getValueEntityType,
}