@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 +17 -0
- package/README.md +1 -1
- package/_i18n/i18n.properties +4 -0
- package/_i18n/i18n_de.properties +5 -1
- package/_i18n/i18n_en.properties +4 -0
- package/cds-plugin.js +1 -1
- package/index.cds +2 -2
- package/lib/change-log.js +20 -6
- package/lib/entity-helper.js +21 -1
- package/lib/localization.js +2 -2
- package/package.json +9 -10
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
|
[](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
|
|
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.
|
package/_i18n/i18n.properties
CHANGED
|
@@ -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/_i18n/i18n_de.properties
CHANGED
|
@@ -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
|
|
55
|
+
ChangeHistory=\u00C4nderungshistorie
|
package/_i18n/i18n_en.properties
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
534
|
+
await DELETE.from(`sap.changelog.ChangeLog`).where({ entityKey });
|
|
535
|
+
return;
|
|
522
536
|
}
|
|
523
537
|
|
|
524
|
-
let changes = _trackedChanges4(
|
|
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
|
-
|
|
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 }
|
package/lib/entity-helper.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/lib/localization.js
CHANGED
|
@@ -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
|
|
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
|
|
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": "
|
|
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/
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
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": {
|