Skip to content

Commit ef1d62e

Browse files
akshay11298Akshay AvinashSv7enNowitzkinkaputnik
authored
feat: Support change logs for bulk insert (#166)
Addresses- #145 - [x] Capture change log for each row - [x] Add test Currently on bulk insert diff comes as an array instead of object, so extracting properties like ID (for entity key), or old_value (for change detection) returns undefined, since they need to be extracted from element of array. Updated the logic to iterate over diff if array, to treat each diff (row) individually, accumulating the changes and doing a bulk insert in ChangeLog entity. --------- Co-authored-by: Akshay Avinash <[email protected]> Co-authored-by: I560824 <[email protected]> Co-authored-by: Nick Josipovic <[email protected]> Co-authored-by: Nick Josipovic <[email protected]>
1 parent f56999d commit ef1d62e

File tree

3 files changed

+202
-4
lines changed

3 files changed

+202
-4
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org/).
55
The format is based on [Keep a Changelog](http://keepachangelog.com/).
66

7+
## Version 1.0.9 - TBD
8+
9+
### Added
10+
11+
-
12+
13+
### Fixed
14+
15+
- Handling of multiple records in one request
16+
17+
18+
### Changed
19+
20+
-
21+
722
## Version 1.0.8 - 28.03.25
823

924
### Added

lib/change-log.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,18 @@ async function track_changes (req) {
502502
let diff = await req.diff()
503503
if (!diff) return
504504

505+
const diffs = Array.isArray(diff) ? diff : [diff];
506+
const changes = (
507+
await Promise.all(diffs.map(item => trackChangesForDiff(item, req, this)))
508+
).filter(Boolean);
509+
510+
if (changes.length > 0) {
511+
await INSERT.into("sap.changelog.ChangeLog").entries(changes);
512+
}
513+
514+
}
515+
516+
async function trackChangesForDiff(diff, req, that){
505517
let target = req.target
506518
let compContext = null;
507519
let entityKey = diff.ID
@@ -518,10 +530,11 @@ async function track_changes (req) {
518530
target[isRoot] &&
519531
!cds.env.requires["change-tracking"]?.preserveDeletes
520532
) {
521-
return await DELETE.from(`sap.changelog.ChangeLog`).where({ entityKey });
533+
await DELETE.from(`sap.changelog.ChangeLog`).where({ entityKey });
534+
return;
522535
}
523536

524-
let changes = _trackedChanges4(this, target, diff)
537+
let changes = _trackedChanges4(that, target, diff)
525538
if (!changes) return
526539

527540
await _formatChangeLog(changes, req)
@@ -538,7 +551,7 @@ async function track_changes (req) {
538551
[ target, entityKey ] = await _prepareChangeLogForComposition(target, entityKey, changes, reqInfo)
539552
}
540553
const dbEntity = getDBEntity(target)
541-
await INSERT.into("sap.changelog.ChangeLog").entries({
554+
return {
542555
entity: dbEntity.name,
543556
entityKey: entityKey,
544557
serviceEntity: target.name || target,
@@ -547,7 +560,7 @@ async function track_changes (req) {
547560
valueChangedFrom: `${c.valueChangedFrom ?? ''}`,
548561
valueChangedTo: `${c.valueChangedTo ?? ''}`,
549562
})),
550-
})
563+
};
551564
}
552565

553566
module.exports = { track_changes, _afterReadChangeView }

tests/integration/service-api.test.js

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,4 +620,174 @@ describe("change log integration test", () => {
620620

621621
expect(changes.length).to.equal(0);
622622
});
623+
624+
it("When creating multiple root records, change tracking for each entity should also be generated", async () => {
625+
cds.env.requires["change-tracking"].preserveDeletes = true;
626+
cds.services.AdminService.entities.Order.elements.netAmount["@changelog"] = true;
627+
cds.services.AdminService.entities.Order.elements.isUsed["@changelog"] = true;
628+
629+
const ordersData = [
630+
{
631+
ID: "fa4d0140-efdd-4c32-aafd-efb7f1d0c8e1",
632+
isUsed: false,
633+
netAmount: 0,
634+
orderItems: [
635+
{
636+
ID: "f35b2d4c-9b21-4b9a-9b3c-ca1ad32a0d1a",
637+
quantity: 10,
638+
},
639+
{
640+
ID: "f35b2d4c-9b21-4b9a-9b3c-ca1ad32a1c2b",
641+
quantity: 12,
642+
}
643+
],
644+
},
645+
{
646+
ID: "ec365b25-b346-4444-8f03-8f5b7d94f040",
647+
isUsed: true,
648+
netAmount: 10,
649+
orderItems: [
650+
{
651+
ID: "f35b2d4c-9b21-4b9a-9b3c-ca1ad32a2c2a",
652+
quantity: 10,
653+
},
654+
{
655+
ID: "f35b2d4c-9b21-4b9a-9b3c-ca1ad32a2b3b",
656+
quantity: 12,
657+
}
658+
],
659+
},
660+
{
661+
ID: "ab9e5510-a60b-4dfc-b026-161c5c2d4056",
662+
isUsed: false,
663+
netAmount: 20,
664+
orderItems: [
665+
{
666+
ID: "f35b2d4c-9b21-4b9a-9b3c-ca1ad32a2c1a",
667+
quantity: 10,
668+
},
669+
{
670+
ID: "f35b2d4c-9b21-4b9a-9b3c-ca1ad32a4c1b",
671+
quantity: 12,
672+
}
673+
],
674+
}
675+
];
676+
677+
await INSERT.into(adminService.entities.Order).entries(ordersData);
678+
let changes = await adminService.run(SELECT.from(ChangeView));
679+
680+
expect(changes).to.have.length(12);
681+
expect(
682+
changes.map((change) => ({
683+
entityKey: change.entityKey,
684+
entity: change.entity,
685+
valueChangedFrom: change.valueChangedFrom,
686+
valueChangedTo: change.valueChangedTo,
687+
modification: change.modification,
688+
attribute: change.attribute
689+
}))
690+
).to.have.deep.members([
691+
{
692+
entityKey: "fa4d0140-efdd-4c32-aafd-efb7f1d0c8e1",
693+
modification: "Create",
694+
entity: "sap.capire.bookshop.Order",
695+
attribute: "netAmount",
696+
valueChangedFrom: "",
697+
valueChangedTo: "0"
698+
},
699+
{
700+
entityKey: "fa4d0140-efdd-4c32-aafd-efb7f1d0c8e1",
701+
modification: "Create",
702+
entity: "sap.capire.bookshop.Order",
703+
attribute: "isUsed",
704+
valueChangedFrom: "",
705+
valueChangedTo: "false"
706+
},
707+
{
708+
entityKey: "fa4d0140-efdd-4c32-aafd-efb7f1d0c8e1",
709+
modification: "Create",
710+
entity: "sap.capire.bookshop.OrderItem",
711+
attribute: "quantity",
712+
valueChangedFrom: "",
713+
valueChangedTo: "10"
714+
},
715+
{
716+
entityKey: "fa4d0140-efdd-4c32-aafd-efb7f1d0c8e1",
717+
modification: "Create",
718+
entity: "sap.capire.bookshop.OrderItem",
719+
attribute: "quantity",
720+
valueChangedFrom: "",
721+
valueChangedTo: "12"
722+
},
723+
{
724+
entityKey: "ec365b25-b346-4444-8f03-8f5b7d94f040",
725+
modification: "Create",
726+
entity: "sap.capire.bookshop.Order",
727+
attribute: "netAmount",
728+
valueChangedFrom: "",
729+
valueChangedTo: "10"
730+
},
731+
{
732+
entityKey: "ec365b25-b346-4444-8f03-8f5b7d94f040",
733+
modification: "Create",
734+
entity: "sap.capire.bookshop.Order",
735+
attribute: "isUsed",
736+
valueChangedFrom: "",
737+
valueChangedTo: "true"
738+
},
739+
{
740+
entityKey: "ec365b25-b346-4444-8f03-8f5b7d94f040",
741+
modification: "Create",
742+
entity: "sap.capire.bookshop.OrderItem",
743+
attribute: "quantity",
744+
valueChangedFrom: "",
745+
valueChangedTo: "10"
746+
},
747+
{
748+
entityKey: "ec365b25-b346-4444-8f03-8f5b7d94f040",
749+
modification: "Create",
750+
entity: "sap.capire.bookshop.OrderItem",
751+
attribute: "quantity",
752+
valueChangedFrom: "",
753+
valueChangedTo: "12"
754+
},
755+
{
756+
entityKey: "ab9e5510-a60b-4dfc-b026-161c5c2d4056",
757+
modification: "Create",
758+
entity: "sap.capire.bookshop.Order",
759+
attribute: "netAmount",
760+
valueChangedFrom: "",
761+
valueChangedTo: "20"
762+
},
763+
{
764+
entityKey: "ab9e5510-a60b-4dfc-b026-161c5c2d4056",
765+
modification: "Create",
766+
entity: "sap.capire.bookshop.Order",
767+
attribute: "isUsed",
768+
valueChangedFrom: "",
769+
valueChangedTo: "false"
770+
},
771+
{
772+
entityKey: "ab9e5510-a60b-4dfc-b026-161c5c2d4056",
773+
modification: "Create",
774+
entity: "sap.capire.bookshop.OrderItem",
775+
attribute: "quantity",
776+
valueChangedFrom: "",
777+
valueChangedTo: "10"
778+
},
779+
{
780+
entityKey: "ab9e5510-a60b-4dfc-b026-161c5c2d4056",
781+
modification: "Create",
782+
entity: "sap.capire.bookshop.OrderItem",
783+
attribute: "quantity",
784+
valueChangedFrom: "",
785+
valueChangedTo: "12"
786+
}
787+
]);
788+
789+
cds.env.requires["change-tracking"].preserveDeletes = false;
790+
delete cds.services.AdminService.entities.Order.elements.netAmount["@changelog"];
791+
delete cds.services.AdminService.entities.Order.elements.isUsed["@changelog"];
792+
});
623793
});

0 commit comments

Comments
 (0)