@cap-js/change-tracking 1.1.4 → 2.0.0-beta.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 +40 -1
- package/README.md +45 -71
- package/_i18n/i18n.properties +10 -22
- package/_i18n/i18n_ar.properties +3 -3
- package/_i18n/i18n_bg.properties +3 -3
- package/_i18n/i18n_cs.properties +3 -3
- package/_i18n/i18n_da.properties +3 -3
- package/_i18n/i18n_de.properties +3 -3
- package/_i18n/i18n_el.properties +3 -3
- package/_i18n/i18n_en.properties +3 -3
- package/_i18n/i18n_en_US_saptrc.properties +3 -32
- package/_i18n/i18n_es.properties +3 -3
- package/_i18n/i18n_es_MX.properties +3 -3
- package/_i18n/i18n_fi.properties +3 -3
- package/_i18n/i18n_fr.properties +3 -3
- package/_i18n/i18n_he.properties +3 -3
- package/_i18n/i18n_hr.properties +3 -3
- package/_i18n/i18n_hu.properties +3 -3
- package/_i18n/i18n_it.properties +3 -3
- package/_i18n/i18n_ja.properties +3 -3
- package/_i18n/i18n_kk.properties +3 -3
- package/_i18n/i18n_ko.properties +3 -3
- package/_i18n/i18n_ms.properties +3 -3
- package/_i18n/i18n_nl.properties +3 -3
- package/_i18n/i18n_no.properties +3 -3
- package/_i18n/i18n_pl.properties +3 -3
- package/_i18n/i18n_pt.properties +3 -3
- package/_i18n/i18n_ro.properties +3 -3
- package/_i18n/i18n_ru.properties +3 -3
- package/_i18n/i18n_sh.properties +3 -3
- package/_i18n/i18n_sk.properties +3 -3
- package/_i18n/i18n_sl.properties +3 -3
- package/_i18n/i18n_sv.properties +3 -3
- package/_i18n/i18n_th.properties +3 -3
- package/_i18n/i18n_tr.properties +3 -3
- package/_i18n/i18n_uk.properties +3 -3
- package/_i18n/i18n_vi.properties +3 -3
- package/_i18n/i18n_zh_CN.properties +3 -3
- package/_i18n/i18n_zh_TW.properties +3 -3
- package/cds-plugin.js +16 -263
- package/index.cds +187 -76
- package/lib/TriggerCQN2SQL.js +42 -0
- package/lib/h2/java-codegen.js +833 -0
- package/lib/h2/register.js +27 -0
- package/lib/h2/triggers.js +41 -0
- package/lib/hana/composition.js +248 -0
- package/lib/hana/register.js +28 -0
- package/lib/hana/sql-expressions.js +213 -0
- package/lib/hana/triggers.js +253 -0
- package/lib/localization.js +53 -117
- package/lib/model-enhancer.js +266 -0
- package/lib/postgres/composition.js +190 -0
- package/lib/postgres/register.js +44 -0
- package/lib/postgres/sql-expressions.js +261 -0
- package/lib/postgres/triggers.js +113 -0
- package/lib/skipHandlers.js +34 -0
- package/lib/sqlite/composition.js +234 -0
- package/lib/sqlite/register.js +28 -0
- package/lib/sqlite/sql-expressions.js +228 -0
- package/lib/sqlite/triggers.js +163 -0
- package/lib/utils/change-tracking.js +394 -0
- package/lib/utils/composition-helpers.js +67 -0
- package/lib/utils/entity-collector.js +297 -0
- package/lib/utils/session-variables.js +276 -0
- package/lib/utils/trigger-utils.js +94 -0
- package/package.json +17 -7
- package/lib/change-log.js +0 -538
- package/lib/entity-helper.js +0 -217
- package/lib/format-options.js +0 -66
- package/lib/template-processor.js +0 -115
package/lib/entity-helper.js
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
const cds = require('@sap/cds');
|
|
2
|
-
const LOG = cds.log('change-log');
|
|
3
|
-
|
|
4
|
-
const getNameFromPathVal = function (pathVal) {
|
|
5
|
-
return /^(.+?)\(/.exec(pathVal)?.[1] || '';
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const getUUIDFromPathVal = function (pathVal) {
|
|
9
|
-
const regRes = /\((.+?)\)/.exec(pathVal);
|
|
10
|
-
return regRes ? regRes[1] : '';
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const getEntityByContextPath = function (aPath, hasComp = false) {
|
|
14
|
-
if (hasComp) return cds.model.definitions[aPath[aPath.length - 1]];
|
|
15
|
-
let entity = cds.model.definitions[aPath[0]];
|
|
16
|
-
for (let each of aPath.slice(1)) {
|
|
17
|
-
entity = entity.elements[each]?._target;
|
|
18
|
-
}
|
|
19
|
-
return entity;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const getObjIdElementNamesInArray = function (elements) {
|
|
23
|
-
if (Array.isArray(elements))
|
|
24
|
-
return elements.map((e) => {
|
|
25
|
-
const splitted = (e['='] || e).split('.');
|
|
26
|
-
splitted.shift();
|
|
27
|
-
return splitted.join('.');
|
|
28
|
-
});
|
|
29
|
-
else return [];
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const getCurObjFromDbQuery = async function (entityName, whereXpr) {
|
|
33
|
-
if (!Object.keys(whereXpr)) return {};
|
|
34
|
-
// REVISIT: This always reads all elements -> should read required ones only!
|
|
35
|
-
const obj = await SELECT.one.from(entityName).where(whereXpr);
|
|
36
|
-
return obj || {};
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const getCurObjFromReqData = function (reqData, nodePathVal, pathVal) {
|
|
40
|
-
const pathVals = splitPath(pathVal);
|
|
41
|
-
const rootNodePathVal = pathVals[0];
|
|
42
|
-
let curReqObj = reqData || {};
|
|
43
|
-
|
|
44
|
-
if (nodePathVal === rootNodePathVal) return curReqObj;
|
|
45
|
-
else pathVals.shift();
|
|
46
|
-
|
|
47
|
-
let parentSrvObjName = getNameFromPathVal(rootNodePathVal);
|
|
48
|
-
|
|
49
|
-
for (const subNodePathVal of pathVals) {
|
|
50
|
-
const srvObjName = getNameFromPathVal(subNodePathVal);
|
|
51
|
-
const curSrvObjUUID = getUUIDFromPathVal(subNodePathVal);
|
|
52
|
-
const associationName = _getAssociationName(parentSrvObjName, srvObjName);
|
|
53
|
-
if (curReqObj) {
|
|
54
|
-
let associationData = curReqObj[associationName];
|
|
55
|
-
if (!Array.isArray(associationData)) associationData = [associationData];
|
|
56
|
-
curReqObj = associationData?.find((x) => x?.ID === curSrvObjUUID) || {};
|
|
57
|
-
}
|
|
58
|
-
if (subNodePathVal === nodePathVal) return curReqObj || {};
|
|
59
|
-
parentSrvObjName = srvObjName;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return curReqObj;
|
|
63
|
-
|
|
64
|
-
function _getAssociationName(entity, target) {
|
|
65
|
-
const source = cds.model.definitions[entity];
|
|
66
|
-
const assocs = source.associations;
|
|
67
|
-
for (const each in assocs) {
|
|
68
|
-
if (assocs[each].target === target) return each;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
async function getObjectId(reqData, entityName, fields, curObj) {
|
|
74
|
-
let all = [],
|
|
75
|
-
{ curObjFromReqData: req_data = {}, curObjFromDbQuery: db_data = {} } = curObj;
|
|
76
|
-
let entity = cds.model.definitions[entityName];
|
|
77
|
-
if (!fields?.length) fields = entity['@changelog']?.map?.((k) => k['='] || k) || [];
|
|
78
|
-
for (let field of fields) {
|
|
79
|
-
let path = field.split('.');
|
|
80
|
-
if (path.length > 1) {
|
|
81
|
-
let current = entity,
|
|
82
|
-
_db_data = db_data;
|
|
83
|
-
while (path.length > 1) {
|
|
84
|
-
let assoc = current.elements[path[0]];
|
|
85
|
-
if (!assoc?.isAssociation) break;
|
|
86
|
-
let foreignKey = assoc.keys?.[0]?.$generatedFieldName;
|
|
87
|
-
let IDval = req_data[foreignKey] && current.name === entityName ? req_data[foreignKey] : _db_data[foreignKey];
|
|
88
|
-
if (!IDval) {
|
|
89
|
-
_db_data = {};
|
|
90
|
-
} else
|
|
91
|
-
try {
|
|
92
|
-
// REVISIT: This always reads all elements -> should read required ones only!
|
|
93
|
-
let ID = assoc.keys?.[0]?.ref[0] || 'ID';
|
|
94
|
-
const isComposition = hasComposition(assoc._target, current);
|
|
95
|
-
// Peer association and composition are distinguished by the value of isComposition.
|
|
96
|
-
if (isComposition) {
|
|
97
|
-
// This function can recursively retrieve the desired information from reqData without having to read it from db.
|
|
98
|
-
_db_data = _getCompositionObjFromReq(reqData, IDval);
|
|
99
|
-
// 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.
|
|
100
|
-
const entityKeys = reqData ? Object.keys(reqData).filter((item) => !Object.keys(assoc._target.keys).some((ele) => item === ele)) : [];
|
|
101
|
-
if (!_db_data || JSON.stringify(_db_data) === '{}' || entityKeys.length === 0) {
|
|
102
|
-
_db_data = IDval ? await getCurObjFromDbQuery(assoc._target, { [ID]: IDval }) : {};
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
_db_data = IDval ? await getCurObjFromDbQuery(assoc._target, { [ID]: IDval }) : {};
|
|
106
|
-
}
|
|
107
|
-
} catch (e) {
|
|
108
|
-
LOG.error('Failed to generate object Id for an association entity.', e);
|
|
109
|
-
throw new Error('Failed to generate object Id for an association entity.', e);
|
|
110
|
-
}
|
|
111
|
-
current = assoc._target;
|
|
112
|
-
path.shift();
|
|
113
|
-
}
|
|
114
|
-
field = path.join('_');
|
|
115
|
-
let obj = current.name === entityName && req_data[field] ? req_data[field] : _db_data[field];
|
|
116
|
-
if (obj) all.push(obj);
|
|
117
|
-
} else {
|
|
118
|
-
let e = entity.elements[field];
|
|
119
|
-
if (e?.isAssociation) field = e.keys?.[0]?.$generatedFieldName;
|
|
120
|
-
let obj = req_data[field] || db_data[field];
|
|
121
|
-
if (obj) all.push(obj);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return all.join(', ');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const getDBEntity = (entity) => {
|
|
128
|
-
if (typeof entity === 'string') entity = cds.model.definitions[entity];
|
|
129
|
-
let proto = Reflect.getPrototypeOf(entity);
|
|
130
|
-
if (proto instanceof cds.entity) return proto;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const getValueEntityType = function (entityName, fields) {
|
|
134
|
-
const types = [],
|
|
135
|
-
entity = cds.model.definitions[entityName];
|
|
136
|
-
for (let field of fields) {
|
|
137
|
-
let current = entity,
|
|
138
|
-
path = field.split('.');
|
|
139
|
-
if (path.length > 1) {
|
|
140
|
-
for (;;) {
|
|
141
|
-
let target = current.elements[path[0]]?._target;
|
|
142
|
-
if (target) current = target;
|
|
143
|
-
else break;
|
|
144
|
-
path.shift();
|
|
145
|
-
}
|
|
146
|
-
field = path.join('_');
|
|
147
|
-
}
|
|
148
|
-
let e = current.elements[field];
|
|
149
|
-
if (e) types.push(e.type);
|
|
150
|
-
}
|
|
151
|
-
return types.join(', ');
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const hasComposition = function (parentEntity, subEntity) {
|
|
155
|
-
if (!parentEntity.compositions) {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const compositions = Object.values(parentEntity.compositions);
|
|
160
|
-
|
|
161
|
-
for (const composition of compositions) {
|
|
162
|
-
if (composition.target === subEntity.name) {
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return false;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const _getCompositionObjFromReq = function (obj, targetID) {
|
|
171
|
-
if (obj?.ID === targetID) {
|
|
172
|
-
return obj;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
for (const key in obj) {
|
|
176
|
-
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
177
|
-
const result = _getCompositionObjFromReq(obj[key], targetID);
|
|
178
|
-
if (result) {
|
|
179
|
-
return result;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return null;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
function splitPath(path) {
|
|
188
|
-
let result = [];
|
|
189
|
-
let buf = '';
|
|
190
|
-
let paren = 0;
|
|
191
|
-
for (let i = 0; i < path.length; i++) {
|
|
192
|
-
const c = path[i];
|
|
193
|
-
if (c === '(') paren++;
|
|
194
|
-
if (c === ')') paren--;
|
|
195
|
-
if (c === '/' && paren === 0) {
|
|
196
|
-
result.push(buf);
|
|
197
|
-
buf = '';
|
|
198
|
-
} else {
|
|
199
|
-
buf += c;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if (buf) result.push(buf);
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
module.exports = {
|
|
207
|
-
getCurObjFromReqData,
|
|
208
|
-
getCurObjFromDbQuery,
|
|
209
|
-
getObjectId,
|
|
210
|
-
getNameFromPathVal,
|
|
211
|
-
getUUIDFromPathVal,
|
|
212
|
-
getDBEntity,
|
|
213
|
-
getEntityByContextPath,
|
|
214
|
-
getObjIdElementNamesInArray,
|
|
215
|
-
getValueEntityType,
|
|
216
|
-
splitPath
|
|
217
|
-
};
|
package/lib/format-options.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const formatOptions = {
|
|
2
|
-
'cds.Date': {
|
|
3
|
-
de: { day: '2-digit', month: '2-digit', year: 'numeric' },
|
|
4
|
-
en: { day: 'numeric', month: 'short', year: 'numeric' },
|
|
5
|
-
es: { day: '2-digit', month: 'short', year: 'numeric' },
|
|
6
|
-
fi: { year: 'numeric', month: '2-digit', day: '2-digit' },
|
|
7
|
-
fr: { day: '2-digit', month: 'short', year: 'numeric' },
|
|
8
|
-
it: { day: '2-digit', month: 'short', year: 'numeric' },
|
|
9
|
-
ja: { year: 'numeric', month: '2-digit', day: '2-digit' },
|
|
10
|
-
pl: { day: '2-digit', month: 'short', year: 'numeric' },
|
|
11
|
-
pt: { day: '2-digit', month: 'short', year: 'numeric' },
|
|
12
|
-
ru: { day: '2-digit', month: 'short', year: 'numeric' },
|
|
13
|
-
'zh-CN': { year: 'numeric', month: 'long', day: 'numeric' }
|
|
14
|
-
},
|
|
15
|
-
'cds.DateTime': {
|
|
16
|
-
de: { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
17
|
-
el: { day: 'numeric', month: 'short', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
18
|
-
en: { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
19
|
-
es: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
20
|
-
es_MX: { day: '2-digit', month: 'short', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit', hour12: true },
|
|
21
|
-
fi: { year: 'numeric', month: '2-digit', day: '2-digit', hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
22
|
-
fr: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
23
|
-
it: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
24
|
-
ja: { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
25
|
-
pl: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
26
|
-
pt: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
27
|
-
ru: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
28
|
-
'zh-CN': { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }
|
|
29
|
-
},
|
|
30
|
-
'cds.Timestamp': {
|
|
31
|
-
cs: { day: 'numeric', month: 'short', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
32
|
-
de: { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
33
|
-
el: { day: 'numeric', month: 'short', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
34
|
-
en: { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
35
|
-
es: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
36
|
-
es_MX: { day: '2-digit', month: 'short', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit', hour12: true },
|
|
37
|
-
fi: { year: 'numeric', month: '2-digit', day: '2-digit', hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
38
|
-
fr: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
39
|
-
it: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
40
|
-
ja: { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
41
|
-
pl: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
42
|
-
pt: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
43
|
-
ru: { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
44
|
-
'zh-CN': { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }
|
|
45
|
-
},
|
|
46
|
-
'cds.Time': {
|
|
47
|
-
cs: { hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
48
|
-
fi: { hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
49
|
-
de: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
50
|
-
el: { hour: 'numeric', minute: '2-digit', second: '2-digit' },
|
|
51
|
-
en: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
52
|
-
es: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
53
|
-
es_MX: { hour: 'numeric', minute: '2-digit', second: '2-digit', hour12: true },
|
|
54
|
-
fr: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
55
|
-
it: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
56
|
-
ja: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
57
|
-
pl: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
58
|
-
pt: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
59
|
-
ru: { hour: '2-digit', minute: '2-digit', second: '2-digit' },
|
|
60
|
-
'zh-CN': { hour: '2-digit', minute: '2-digit', second: '2-digit' }
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
module.exports = {
|
|
65
|
-
formatOptions
|
|
66
|
-
};
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
// Enhanced class based on cds v5.5.5 @sap/cds/libx/_runtime/common/utils/templateProcessor
|
|
2
|
-
|
|
3
|
-
const DELIMITER = require('@sap/cds/libx/_runtime/common/utils/templateDelimiter');
|
|
4
|
-
|
|
5
|
-
const _formatRowContext = (tKey, keyNames, row) => {
|
|
6
|
-
const keyValuePairs = keyNames.map((key) => `${key}=${row[key]}`);
|
|
7
|
-
const keyValuePairsSerialized = keyValuePairs.join(',');
|
|
8
|
-
return `${tKey}(${keyValuePairsSerialized})`;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const _processElement = (processFn, row, key, elements, isRoot, pathSegments, picked = {}) => {
|
|
12
|
-
const element = elements[key];
|
|
13
|
-
const { plain } = picked;
|
|
14
|
-
|
|
15
|
-
// do not change-track personal data
|
|
16
|
-
const isPersonalData = element && Object.keys(element).some((key) => key.startsWith('@PersonalData'));
|
|
17
|
-
if (plain && !isPersonalData) {
|
|
18
|
-
/**
|
|
19
|
-
* @type import('../../types/api').templateProcessorProcessFnArgs
|
|
20
|
-
*/
|
|
21
|
-
const elementInfo = { row, key, element, plain, isRoot, pathSegments };
|
|
22
|
-
processFn(elementInfo);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function retrieveFirstKey(target) {
|
|
27
|
-
const keys = [];
|
|
28
|
-
for (const key of Object.keys(target.keys)) {
|
|
29
|
-
if (target.keys[key].type !== 'cds.Association' && key !== 'IsActiveEntity') {
|
|
30
|
-
keys.push(key);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
//Revisit: For test 3.5
|
|
35
|
-
return keys.filter((k) => !k.startsWith('up_'))[0];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const _processRow = (processFn, row, template, tKey, tValue, isRoot, pathOptions) => {
|
|
39
|
-
const { template: subTemplate, picked } = tValue;
|
|
40
|
-
const key = tKey.split(DELIMITER).pop();
|
|
41
|
-
const { segments: pathSegments } = pathOptions;
|
|
42
|
-
|
|
43
|
-
if (!subTemplate && pathSegments) {
|
|
44
|
-
pathSegments.push(key);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
_processElement(processFn, row, key, template.target.elements, isRoot, pathSegments, picked);
|
|
48
|
-
|
|
49
|
-
// process deep
|
|
50
|
-
if (subTemplate) {
|
|
51
|
-
let subRows = row && row[key];
|
|
52
|
-
|
|
53
|
-
subRows = Array.isArray(subRows) ? subRows : [subRows];
|
|
54
|
-
|
|
55
|
-
// Build entity path
|
|
56
|
-
subRows.forEach((subRow) => {
|
|
57
|
-
if (subRow && row && row._path) {
|
|
58
|
-
/** Enhancement by SME: Support CAP Change Histroy
|
|
59
|
-
* Construct path from root entity to current entity.
|
|
60
|
-
*/
|
|
61
|
-
const serviceNodeName = template.target.elements[key].target;
|
|
62
|
-
const serviceNode = template.target.elements[key]._target;
|
|
63
|
-
subRow._path = `${row._path}/${serviceNodeName}(${subRow[retrieveFirstKey(serviceNode)]})`;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
_processComplex(processFn, subRows, subTemplate, key, pathOptions);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const _processComplex = (processFn, rows, template, tKey, pathOptions) => {
|
|
72
|
-
if (rows.length === 0) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const segments = pathOptions.segments;
|
|
77
|
-
let keyNames;
|
|
78
|
-
|
|
79
|
-
for (const row of rows) {
|
|
80
|
-
if (row == null) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const args = { processFn, row, template, isRoot: false, pathOptions };
|
|
85
|
-
|
|
86
|
-
if (pathOptions.includeKeyValues) {
|
|
87
|
-
keyNames = keyNames || (template.target.keys && Object.keys(template.target.keys)) || [];
|
|
88
|
-
pathOptions.rowKeysGenerator(keyNames, row, template);
|
|
89
|
-
const pathSegment = _formatRowContext(tKey, keyNames, { ...row, ...pathOptions.extraKeys });
|
|
90
|
-
args.pathOptions.segments = segments ? [...segments, pathSegment] : [pathSegment];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
templateProcessor(args);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* @param {import("../../types/api").TemplateProcessor} args
|
|
99
|
-
*/
|
|
100
|
-
const templateProcessor = ({ processFn, row, template, isRoot = true, pathOptions = {} }) => {
|
|
101
|
-
const segments = pathOptions.segments && [...pathOptions.segments];
|
|
102
|
-
|
|
103
|
-
for (const [tKey, tValue] of template.elements) {
|
|
104
|
-
// When a managed association is marked with @changelog, both foreign key and assoc are in the template. Skip the association as only the foreign key has a change.
|
|
105
|
-
if (template.target.elements[tKey]?.type === 'cds.Association') {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
if (segments) {
|
|
109
|
-
pathOptions.segments = [...segments];
|
|
110
|
-
}
|
|
111
|
-
_processRow(processFn, row, template, tKey, tValue, isRoot, pathOptions);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
module.exports = { templateProcessor, retrieveFirstKey };
|