@cap-js/change-tracking 1.1.1 → 1.1.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 CHANGED
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
6
6
 
7
- ## Version 1.1.2 - TBD
7
+ ## Version 1.1.3 - TBD
8
8
 
9
9
  ### Added
10
10
 
@@ -13,6 +13,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
13
13
  ### Changed
14
14
 
15
15
 
16
+ ## Version 1.1.2 - 23.10.25
17
+
18
+ ### Fixed
19
+
20
+ - Support single keys which are not named `ID`
21
+
22
+
16
23
  ## Version 1.1.1 - 17.10.25
17
24
 
18
25
  ### Added
package/index.cds CHANGED
@@ -13,7 +13,7 @@ namespace sap.changelog;
13
13
  }]) {
14
14
  // Essentially: Association to many Changes on changes.changeLog.entityKey = ID;
15
15
  changes : Association to many ChangeView on changes.entityKey = ID;
16
- key ID : UUID;
16
+ key ID : String;
17
17
  }
18
18
 
19
19
 
@@ -39,7 +39,7 @@ view ChangeView as
39
39
  entity ChangeLog : managed, cuid {
40
40
  serviceEntity : String(5000) @title: '{i18n>ChangeLog.serviceEntity}'; // definition name of target entity (on service level) - e.g. ProcessorsService.Incidents
41
41
  entity : String(5000) @title: '{i18n>ChangeLog.entity}'; // definition name of target entity (on db level) - e.g. sap.capire.incidents.Incidents
42
- entityKey : UUID @title: '{i18n>ChangeLog.entityKey}'; // primary key of target entity, e.g. Incidents.ID
42
+ entityKey : String @title: '{i18n>ChangeLog.entityKey}'; // primary key of target entity, e.g. Incidents.ID
43
43
  createdAt : managed:createdAt @title : '{i18n>ChangeLog.createdAt}';
44
44
  createdBy : managed:createdBy @title : '{i18n>ChangeLog.createdBy}';
45
45
  changes : Composition of many Changes on changes.changeLog = $self;
@@ -64,7 +64,7 @@ entity Changes {
64
64
 
65
65
  // Business meaningful parent object id
66
66
  parentEntityID : String(5000) @title: '{i18n>Changes.parentEntityID}';
67
- parentKey : UUID @title: '{i18n>Changes.parentKey}';
67
+ parentKey : String @title: '{i18n>Changes.parentKey}';
68
68
  serviceEntityPath : String(5000) @title: '{i18n>Changes.serviceEntityPath}';
69
69
 
70
70
  @title: '{i18n>Changes.modification}'
package/lib/change-log.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const cds = require("@sap/cds")
2
2
  const getTemplate = require("@sap/cds/libx/_runtime/common/utils/template") // REVISIT: bad usage of internal stuff
3
- const templateProcessor = require("./template-processor")
3
+ const {templateProcessor, retrieveFirstKey} = require("./template-processor")
4
4
  const LOG = cds.log("change-log")
5
5
 
6
6
  const {
@@ -173,7 +173,8 @@ const _getChildChangeObjId = async function (
173
173
  change,
174
174
  childNodeChange,
175
175
  curNodePathVal,
176
- reqData
176
+ reqData,
177
+ target
177
178
  ) {
178
179
  const composition = cds.model.definitions[change.serviceEntity].elements[change.attribute]
179
180
  const objIdElements = composition ? composition["@changelog"] : null
@@ -183,11 +184,12 @@ const _getChildChangeObjId = async function (
183
184
  reqData,
184
185
  curNodePathVal,
185
186
  childNodeChange._path,
187
+ target,
186
188
  objIdElementNames
187
189
  )
188
190
  }
189
191
 
190
- const _formatCompositionContext = async function (changes, reqData) {
192
+ const _formatCompositionContext = async function (changes, reqData, target) {
191
193
  const childNodeChanges = []
192
194
 
193
195
  for (const change of changes) {
@@ -204,7 +206,8 @@ const _formatCompositionContext = async function (changes, reqData) {
204
206
  change,
205
207
  childNodeChange,
206
208
  curNodePathVal,
207
- reqData
209
+ reqData,
210
+ target
208
211
  )
209
212
  _formatCompositionValue(curChange, objId, childNodeChange, childNodeChanges)
210
213
  }
@@ -261,17 +264,18 @@ const _getObjectIdByPath = async function (
261
264
  reqData,
262
265
  nodePathVal,
263
266
  serviceEntityPath,
267
+ target,
264
268
  /**optional*/ objIdElementNames
265
269
  ) {
266
270
  const curObjFromReqData = getCurObjFromReqData(reqData, nodePathVal, serviceEntityPath)
267
271
  const entityName = getNameFromPathVal(nodePathVal)
268
272
  const entityUUID = getUUIDFromPathVal(nodePathVal)
269
- const obj = await getCurObjFromDbQuery(entityName, entityUUID)
273
+ const obj = await getCurObjFromDbQuery(entityName, {[retrieveFirstKey(target)]: entityUUID})
270
274
  const curObj = { curObjFromReqData, curObjFromDbQuery: obj }
271
275
  return getObjectId(reqData, entityName, objIdElementNames, curObj)
272
276
  }
273
277
 
274
- const _formatObjectID = async function (changes, reqData) {
278
+ const _formatObjectID = async function (changes, reqData, target) {
275
279
  const objectIdCache = new Map()
276
280
  for (const change of changes) {
277
281
  const path = splitPath(change.serviceEntityPath)
@@ -283,7 +287,8 @@ const _formatObjectID = async function (changes, reqData) {
283
287
  curNodeObjId = await _getObjectIdByPath(
284
288
  reqData,
285
289
  curNodePathVal,
286
- change.serviceEntityPath
290
+ change.serviceEntityPath,
291
+ target
287
292
  )
288
293
  objectIdCache.set(curNodePathVal, curNodeObjId)
289
294
  }
@@ -293,7 +298,8 @@ const _formatObjectID = async function (changes, reqData) {
293
298
  parentNodeObjId = await _getObjectIdByPath(
294
299
  reqData,
295
300
  parentNodePathVal,
296
- change.serviceEntityPath
301
+ change.serviceEntityPath,
302
+ target
297
303
  )
298
304
  objectIdCache.set(parentNodePathVal, parentNodeObjId)
299
305
  }
@@ -315,9 +321,9 @@ const _isCompositionContextPath = function (aPath, hasComp) {
315
321
  }
316
322
 
317
323
  const _formatChangeLog = async function (changes, req) {
318
- await _formatObjectID(changes, req.data)
324
+ await _formatObjectID(changes, req.data, req.target)
319
325
  await _formatAssociationContext(changes, req.data)
320
- await _formatCompositionContext(changes, req.data)
326
+ await _formatCompositionContext(changes, req.data, req.target)
321
327
  }
322
328
 
323
329
  const _afterReadChangeView = function (data, req) {
@@ -326,13 +332,12 @@ const _afterReadChangeView = function (data, req) {
326
332
  localizeLogFields(data, req.locale)
327
333
  }
328
334
 
329
-
330
335
  function _trackedChanges4 (srv, target, diff) {
331
336
  const template = getTemplate("change-logging", srv, target, { pick: e => e['@changelog'] })
332
337
  if (!template.elements.size) return
333
338
 
334
339
  const changes = []
335
- diff._path = `${target.name}(${diff.ID})`
340
+ diff._path = `${target.name}(${diff[retrieveFirstKey(target)]})`
336
341
 
337
342
  templateProcessor({
338
343
  template, row: diff, processFn: ({ row, key, element }) => {
@@ -416,7 +421,7 @@ const _prepareChangeLogForComposition = async function (entity, entityKey, chang
416
421
  .join('/')
417
422
 
418
423
  for (const change of changes) {
419
- change.parentEntityID = await _getObjectIdByPath(req.data, parentEntityPathVal, parentServiceEntityPath)
424
+ change.parentEntityID = await _getObjectIdByPath(req.data, parentEntityPathVal, parentServiceEntityPath, entity)
420
425
  change.parentKey = parentKey
421
426
  change.serviceEntityPath = serviceEntityPath
422
427
  }
@@ -520,7 +525,7 @@ async function track_changes (req) {
520
525
  async function trackChangesForDiff(diff, req, that){
521
526
  let target = req.target
522
527
  let compContext = null;
523
- let entityKey = diff.ID
528
+ let entityKey = diff[retrieveFirstKey(req.target)]
524
529
  const params = convertSubjectToParams(req.subject);
525
530
  if (req.subject.ref.length === 1 && params.length === 1 && !target[isRoot]) {
526
531
  compContext = await generatePathAndParams(req, entityKey);
@@ -29,10 +29,10 @@ const getObjIdElementNamesInArray = function (elements) {
29
29
  else return []
30
30
  }
31
31
 
32
- const getCurObjFromDbQuery = async function (entityName, queryVal, /**optional*/ queryKey='ID') {
33
- if (!queryVal) return {}
32
+ const getCurObjFromDbQuery = async function (entityName, whereXpr) {
33
+ if (!Object.keys(whereXpr)) return {}
34
34
  // REVISIT: This always reads all elements -> should read required ones only!
35
- const obj = await SELECT.one.from(entityName).where({[queryKey]: queryVal})
35
+ const obj = await SELECT.one.from(entityName).where(whereXpr)
36
36
  return obj || {}
37
37
  }
38
38
 
@@ -99,10 +99,10 @@ async function getObjectId (reqData, entityName, fields, curObj) {
99
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
100
  const entityKeys = reqData ? Object.keys(reqData).filter(item => !Object.keys(assoc._target.keys).some(ele => item === ele)) : [];
101
101
  if (!_db_data || JSON.stringify(_db_data) === '{}' || entityKeys.length === 0) {
102
- _db_data = await getCurObjFromDbQuery(assoc._target, IDval, ID);
102
+ _db_data = IDval ? await getCurObjFromDbQuery(assoc._target, {[ID]: IDval}) : {};
103
103
  }
104
104
  } else {
105
- _db_data = await getCurObjFromDbQuery(assoc._target, IDval, ID);
105
+ _db_data = IDval ? await getCurObjFromDbQuery(assoc._target, {[ID]: IDval}) : {};
106
106
  }
107
107
  } catch (e) {
108
108
  LOG.error("Failed to generate object Id for an association entity.", e)
@@ -23,6 +23,18 @@ const _processElement = (processFn, row, key, elements, isRoot, pathSegments, pi
23
23
  }
24
24
  };
25
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
+
26
38
  const _processRow = (processFn, row, template, tKey, tValue, isRoot, pathOptions) => {
27
39
  const { template: subTemplate, picked } = tValue;
28
40
  const key = tKey.split(DELIMITER).pop();
@@ -47,7 +59,8 @@ const _processRow = (processFn, row, template, tKey, tValue, isRoot, pathOptions
47
59
  * Construct path from root entity to current entity.
48
60
  */
49
61
  const serviceNodeName = template.target.elements[key].target;
50
- subRow._path = `${row._path}/${serviceNodeName}(${subRow.ID})`;
62
+ const serviceNode = template.target.elements[key]._target;
63
+ subRow._path = `${row._path}/${serviceNodeName}(${subRow[retrieveFirstKey(serviceNode)]})`;
51
64
  }
52
65
  });
53
66
 
@@ -95,4 +108,4 @@ const templateProcessor = ({ processFn, row, template, isRoot = true, pathOption
95
108
  }
96
109
  };
97
110
 
98
- module.exports = templateProcessor;
111
+ module.exports = {templateProcessor, retrieveFirstKey};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/change-tracking",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "CDS plugin providing out-of-the box support for automatic capturing, storing, and viewing of the change records of modeled entities.",
5
5
  "repository": "cap-js/change-tracking",
6
6
  "author": "SAP SE (https://www.sap.com)",