@cap-js/change-tracking 1.0.8 → 1.1.0

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,6 +4,23 @@ 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.0 - TBD
8
+
9
+ ### Added
10
+
11
+ - License entry
12
+ - Added translations for the UI labels for more languages
13
+
14
+ ### Fixed
15
+
16
+ - Handling of multiple records in one request
17
+ - Handle cases where the key contains '/'
18
+ - Instantiate the changes association correctly so it does not impact other `@cap-js` plugins
19
+
20
+ ### Changed
21
+
22
+ - Prepare for CDS9 in tests
23
+
7
24
  ## Version 1.0.8 - 28.03.25
8
25
 
9
26
  ### Added
package/README.md CHANGED
@@ -5,7 +5,7 @@ a [CDS plugin](https://cap.cloud.sap/docs/node.js/cds-plugins#cds-plugin-package
5
5
  [![REUSE status](https://api.reuse.software/badge/github.com/cap-js/change-tracking)](https://api.reuse.software/info/github.com/cap-js/change-tracking)
6
6
 
7
7
  > [!IMPORTANT]
8
- > Following the CAP best practices, the new release now requires CDS8 as minimum version in the peer dependencies. If you want to use the plugin with an older version of CAP (CDS7), you will need to manually update the peer dependency in `package.json`. Please be aware that there will be no support for this version of the plugin with a CDS version below 8!
8
+ > Following the CAP best practices, the new release now requires CDS8 or CDS9 as minimum versions. If you want to use the plugin with an older version of CAP (CDS7), you will need to manually update the peer dependency in `package.json`. Please be aware that there will be no support for this version of the plugin with a CDS version below 8!
9
9
 
10
10
  > [!IMPORTANT]
11
11
  > This release establishes support for multi-tenant deployments using MTX and extensibility.
@@ -45,6 +45,10 @@ ChangeLog.modification.create=Create
45
45
  ChangeLog.modification.update=Update
46
46
  #XFLD: Field label
47
47
  ChangeLog.modification.delete=Delete
48
+ #XFLD: Field label
49
+ ChangeLog.createdAt=Changed at
50
+ #XFLD: Field label
51
+ ChangeLog.createdBy=Changed by
48
52
 
49
53
  ## Change History Table##
50
54
  ########################################
@@ -45,7 +45,11 @@ ChangeLog.modification.create=Anlegen
45
45
  ChangeLog.modification.update=Aktualisieren
46
46
  #XFLD: Field label
47
47
  ChangeLog.modification.delete=L\u00F6schen
48
+ #XFLD: Field label
49
+ ChangeLog.createdAt=\u00C4nderung am
50
+ #XFLD: Field label
51
+ ChangeLog.createdBy=\u00C4nderung von
48
52
 
49
53
  ## Change History Table##
50
54
  ########################################
51
- ChangeHistory=Änderungshistorie
55
+ ChangeHistory=\u00C4nderungshistorie
@@ -45,6 +45,10 @@ ChangeLog.modification.create=Create
45
45
  ChangeLog.modification.update=Update
46
46
  #XFLD: Field label
47
47
  ChangeLog.modification.delete=Delete
48
+ #XFLD: Field label
49
+ ChangeLog.createdAt=Changed at
50
+ #XFLD: Field label
51
+ ChangeLog.createdBy=Changed by
48
52
 
49
53
  ## Change History Table##
50
54
  ########################################
package/cds-plugin.js CHANGED
@@ -180,7 +180,7 @@ function enhanceModel (m) {
180
180
 
181
181
  // Add association to ChangeView...
182
182
  const keys = entityKey4(entity); if (!keys.length) continue // If no key attribute is defined for the entity, the logic to add association to ChangeView should be skipped.
183
- const assoc = { ...changes, on: [ ...changes.on, ...keys ] }
183
+ const assoc = new cds.builtin.classes.Association({ ...changes, on: [ ...changes.on, ...keys ] })
184
184
 
185
185
  // --------------------------------------------------------------------
186
186
  // PARKED: Add auto-exposed projection on ChangeView to service if applicable
package/index.cds CHANGED
@@ -40,8 +40,8 @@ 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
42
  entityKey : UUID @title: '{i18n>ChangeLog.entityKey}'; // primary key of target entity, e.g. Incidents.ID
43
- createdAt : managed:createdAt;
44
- createdBy : managed:createdBy;
43
+ createdAt : managed:createdAt @title : '{i18n>ChangeLog.createdAt}';
44
+ createdBy : managed:createdBy @title : '{i18n>ChangeLog.createdBy}';
45
45
  changes : Composition of many Changes on changes.changeLog = $self;
46
46
  }
47
47
 
package/lib/change-log.js CHANGED
@@ -13,6 +13,7 @@ const {
13
13
  getEntityByContextPath,
14
14
  getObjIdElementNamesInArray,
15
15
  getValueEntityType,
16
+ splitPath,
16
17
  } = require("./entity-helper")
17
18
  const { localizeLogFields } = require("./localization")
18
19
  const isRoot = "change-tracking-isRootEntity"
@@ -193,7 +194,7 @@ const _formatCompositionContext = async function (changes, reqData) {
193
194
  }
194
195
  for (const childNodeChange of change.valueChangedTo) {
195
196
  const curChange = Object.assign({}, change)
196
- const path = childNodeChange._path.split('/')
197
+ const path = splitPath(childNodeChange._path)
197
198
  const curNodePathVal = path.pop()
198
199
  curChange.modification = childNodeChange._op
199
200
  const objId = await _getChildChangeObjId(
@@ -270,7 +271,7 @@ const _getObjectIdByPath = async function (
270
271
  const _formatObjectID = async function (changes, reqData) {
271
272
  const objectIdCache = new Map()
272
273
  for (const change of changes) {
273
- const path = change.serviceEntityPath.split('/')
274
+ const path = splitPath(change.serviceEntityPath)
274
275
  const curNodePathVal = path.pop()
275
276
  const parentNodePathVal = path.pop()
276
277
 
@@ -502,6 +503,18 @@ async function track_changes (req) {
502
503
  let diff = await req.diff()
503
504
  if (!diff) return
504
505
 
506
+ const diffs = Array.isArray(diff) ? diff : [diff];
507
+ const changes = (
508
+ await Promise.all(diffs.map(item => trackChangesForDiff(item, req, this)))
509
+ ).filter(Boolean);
510
+
511
+ if (changes.length > 0) {
512
+ await INSERT.into("sap.changelog.ChangeLog").entries(changes);
513
+ }
514
+
515
+ }
516
+
517
+ async function trackChangesForDiff(diff, req, that){
505
518
  let target = req.target
506
519
  let compContext = null;
507
520
  let entityKey = diff.ID
@@ -518,10 +531,11 @@ async function track_changes (req) {
518
531
  target[isRoot] &&
519
532
  !cds.env.requires["change-tracking"]?.preserveDeletes
520
533
  ) {
521
- return await DELETE.from(`sap.changelog.ChangeLog`).where({ entityKey });
534
+ await DELETE.from(`sap.changelog.ChangeLog`).where({ entityKey });
535
+ return;
522
536
  }
523
537
 
524
- let changes = _trackedChanges4(this, target, diff)
538
+ let changes = _trackedChanges4(that, target, diff)
525
539
  if (!changes) return
526
540
 
527
541
  await _formatChangeLog(changes, req)
@@ -538,7 +552,7 @@ async function track_changes (req) {
538
552
  [ target, entityKey ] = await _prepareChangeLogForComposition(target, entityKey, changes, reqInfo)
539
553
  }
540
554
  const dbEntity = getDBEntity(target)
541
- await INSERT.into("sap.changelog.ChangeLog").entries({
555
+ return {
542
556
  entity: dbEntity.name,
543
557
  entityKey: entityKey,
544
558
  serviceEntity: target.name || target,
@@ -547,7 +561,7 @@ async function track_changes (req) {
547
561
  valueChangedFrom: `${c.valueChangedFrom ?? ''}`,
548
562
  valueChangedTo: `${c.valueChangedTo ?? ''}`,
549
563
  })),
550
- })
564
+ };
551
565
  }
552
566
 
553
567
  module.exports = { track_changes, _afterReadChangeView }
@@ -37,7 +37,7 @@ const getCurObjFromDbQuery = async function (entityName, queryVal, /**optional*/
37
37
  }
38
38
 
39
39
  const getCurObjFromReqData = function (reqData, nodePathVal, pathVal) {
40
- const pathVals = pathVal.split('/')
40
+ const pathVals = splitPath(pathVal);
41
41
  const rootNodePathVal = pathVals[0]
42
42
  let curReqObj = reqData || {}
43
43
 
@@ -183,6 +183,25 @@ const _getCompositionObjFromReq = function (obj, targetID) {
183
183
  return null;
184
184
  };
185
185
 
186
+ function splitPath (path) {
187
+ let result = [];
188
+ let buf = "";
189
+ let paren = 0;
190
+ for (let i = 0; i < path.length; i++) {
191
+ const c = path[i];
192
+ if (c === "(") paren++;
193
+ if (c === ")") paren--;
194
+ if (c === "/" && paren === 0) {
195
+ result.push(buf);
196
+ buf = "";
197
+ } else {
198
+ buf += c;
199
+ }
200
+ }
201
+ if (buf) result.push(buf);
202
+ return result;
203
+ }
204
+
186
205
  module.exports = {
187
206
  getCurObjFromReqData,
188
207
  getCurObjFromDbQuery,
@@ -193,4 +212,5 @@ module.exports = {
193
212
  getEntityByContextPath,
194
213
  getObjIdElementNamesInArray,
195
214
  getValueEntityType,
215
+ splitPath,
196
216
  }
@@ -1,6 +1,6 @@
1
1
  const cds = require("@sap/cds/lib");
2
2
  const LOG = cds.log("change-log");
3
- const { getNameFromPathVal, getDBEntity } = require("./entity-helper");
3
+ const { getNameFromPathVal, getDBEntity, splitPath } = require("./entity-helper");
4
4
 
5
5
  const MODIF_I18N_MAP = {
6
6
  create: "{i18n>ChangeLog.modification.create}",
@@ -36,7 +36,7 @@ const _localizeDefaultObjectID = function (change, locale) {
36
36
  change.objectID = change.entity ? change.entity : "";
37
37
  }
38
38
  if (change.objectID && change.serviceEntityPath && !change.parentObjectID && change.parentKey) {
39
- const path = change.serviceEntityPath.split('/');
39
+ const path = splitPath(change.serviceEntityPath);
40
40
  const parentNodePathVal = path[path.length - 2];
41
41
  const parentEntityName = getNameFromPathVal(parentNodePathVal);
42
42
  const dbEntity = getDBEntity(parentEntityName);
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@cap-js/change-tracking",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
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)",
7
- "license": "SEE LICENSE IN LICENSE",
7
+ "license": "Apache-2.0",
8
8
  "main": "cds-plugin.js",
9
9
  "files": [
10
10
  "lib",
@@ -20,16 +20,15 @@
20
20
  "peerDependencies": {
21
21
  "@sap/cds": ">=8"
22
22
  },
23
+ "engines": {
24
+ "node": ">=20.0.0"
25
+ },
23
26
  "devDependencies": {
24
27
  "@cap-js/change-tracking": "file:.",
25
- "@cap-js/sqlite": "^1",
26
- "axios": "^1",
27
- "chai": "^4.3.10",
28
- "chai-as-promised": "^7.1.1",
29
- "chai-subset": "^1.6.0",
30
- "eslint": "^8",
31
- "express": "^4",
32
- "jest": "^29"
28
+ "@cap-js/attachments": "^2",
29
+ "@cap-js/sqlite": "^1 || ^2",
30
+ "@cap-js/cds-test": "*",
31
+ "express": "^4"
33
32
  },
34
33
  "cds": {
35
34
  "requires": {