Skip to content

Commit

Permalink
fix: When a numeric type with an initial value of 0 or a boolean type…
Browse files Browse the repository at this point in the history
… with a value of false is created or deleted, the changelog cannot be generated. (#118)

Fix issue:
1. #111
  • Loading branch information
Sv7enNowitzki authored Sep 24, 2024
1 parent a8f2284 commit 457a2a0
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 1 deletion.
5 changes: 4 additions & 1 deletion lib/change-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,9 @@ function getAssociationDetails (entity) {
return { ID, foreignKey, parentEntity };
}

function isEmpty(value) {
return value === null || value === undefined || value === "";
}

async function track_changes (req) {
let diff = await req.diff()
Expand Down Expand Up @@ -486,7 +489,7 @@ async function track_changes (req) {
entity: dbEntity.name,
entityKey: entityKey,
serviceEntity: target.name || target,
changes: changes.filter(c => c.valueChangedFrom || c.valueChangedTo).map((c) => ({
changes: changes.filter(c => !isEmpty(c.valueChangedFrom) || !isEmpty(c.valueChangedTo)).map((c) => ({
...c,
valueChangedFrom: `${c.valueChangedFrom ?? ''}`,
valueChangedTo: `${c.valueChangedTo ?? ''}`,
Expand Down
1 change: 1 addition & 0 deletions tests/bookshop/db/schema.cds
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ entity Order : cuid {
orderItems : Composition of many OrderItem
on orderItems.order = $self;
netAmount : Decimal(19, 2);
isUsed : Boolean;
status : String;
Items : Composition of many {
key ID : UUID;
Expand Down
83 changes: 83 additions & 0 deletions tests/integration/fiori-draft-disabled.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,89 @@ describe("change log draft disabled test", () => {
delete cds.services.AdminService.entities.Level2Object["@changelog"];
});

it("1.7 When creating or deleting a record with a numeric type of 0 and a boolean type of false, a changelog should also be generated", async () => {
cds.env.requires["change-tracking"].preserveDeletes = true;
cds.services.AdminService.entities.Order.elements.netAmount["@changelog"] = true;
cds.services.AdminService.entities.Order.elements.isUsed["@changelog"] = true;

await POST(`/odata/v4/admin/Order`, {
ID: "3e745e35-5974-4383-b60a-2f5c9bdd31ac",
isUsed: false,
netAmount: 0,
});

let changes = await adminService.run(SELECT.from(ChangeView));

expect(changes).to.have.length(2);
expect(
changes.map((change) => ({
entityKey: change.entityKey,
entity: change.entity,
valueChangedFrom: change.valueChangedFrom,
valueChangedTo: change.valueChangedTo,
modification: change.modification,
attribute: change.attribute
}))
).to.have.deep.members([
{
entityKey: "3e745e35-5974-4383-b60a-2f5c9bdd31ac",
modification: "Create",
entity: "sap.capire.bookshop.Order",
attribute: "netAmount",
valueChangedFrom: "",
valueChangedTo: "0"
},
{
entityKey: "3e745e35-5974-4383-b60a-2f5c9bdd31ac",
modification: "Create",
entity: "sap.capire.bookshop.Order",
attribute: "isUsed",
valueChangedFrom: "",
valueChangedTo: "false"
},
]);

await DELETE("/odata/v4/admin/Order(ID=3e745e35-5974-4383-b60a-2f5c9bdd31ac)");

changes = await adminService.run(
SELECT.from(ChangeView).where({
modification: "delete",
})
);

expect(changes).to.have.length(2);
expect(
changes.map((change) => ({
entityKey: change.entityKey,
entity: change.entity,
valueChangedFrom: change.valueChangedFrom,
valueChangedTo: change.valueChangedTo,
modification: change.modification,
attribute: change.attribute
}))
).to.have.deep.members([
{
entityKey: "3e745e35-5974-4383-b60a-2f5c9bdd31ac",
modification: "Delete",
entity: "sap.capire.bookshop.Order",
attribute: "netAmount",
valueChangedFrom: "0",
valueChangedTo: ""
},
{
entityKey: "3e745e35-5974-4383-b60a-2f5c9bdd31ac",
modification: "Delete",
entity: "sap.capire.bookshop.Order",
attribute: "isUsed",
valueChangedFrom: "false",
valueChangedTo: ""
},
]);

delete cds.services.AdminService.entities.Order.elements.netAmount["@changelog"];
delete cds.services.AdminService.entities.Order.elements.isUsed["@changelog"];
});

it("3.1 Composition creatition by odata request on draft disabled entity - should log changes for root entity (ERP4SMEPREPWORKAPPPLAT-670)", async () => {
await POST(
`/odata/v4/admin/Order(ID=0a41a187-a2ff-4df6-bd12-fae8996e6e31)/orderItems(ID=9a61178f-bfb3-4c17-8d17-c6b4a63e0097)/notes`,
Expand Down
82 changes: 82 additions & 0 deletions tests/integration/fiori-draft-enabled.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,88 @@ describe("change log integration test", () => {
expect(afterChanges.length).to.equal(14);
});

it("1.7 When creating or deleting a record with a numeric type of 0 and a boolean type of false, a changelog should also be generated", async () => {
cds.services.AdminService.entities.Books.elements.price["@changelog"] = true;

let action = POST.bind(
{},
`/odata/v4/admin/BookStores(ID=64625905-c234-4d0d-9bc1-283ee8946770,IsActiveEntity=false)/books`,
{
ID: "01234567-89ab-cdef-0123-987654fedcba",
price: 0,
isUsed: false
}
);
await utils.apiAction("admin", "BookStores", "64625905-c234-4d0d-9bc1-283ee8946770", "AdminService", action);
let changes = await adminService.run(SELECT.from(ChangeView));
expect(changes).to.have.length(2);
expect(
changes.map((change) => ({
entityKey: change.entityKey,
entity: change.entity,
valueChangedFrom: change.valueChangedFrom,
valueChangedTo: change.valueChangedTo,
modification: change.modification,
attribute: change.attribute
}))
).to.have.deep.members([
{
entityKey: "64625905-c234-4d0d-9bc1-283ee8946770",
modification: "Create",
entity: "Book",
attribute: "price",
valueChangedFrom: "",
valueChangedTo: "0"
},
{
entityKey: "64625905-c234-4d0d-9bc1-283ee8946770",
modification: "Create",
entity: "Book",
attribute: "isUsed",
valueChangedFrom: "",
valueChangedTo: "false"
},
]);

action = DELETE.bind({}, `/odata/v4/admin/Books(ID=01234567-89ab-cdef-0123-987654fedcba,IsActiveEntity=false)`);
await utils.apiAction("admin", "BookStores", "64625905-c234-4d0d-9bc1-283ee8946770", "AdminService", action);
changes = await adminService.run(
SELECT.from(ChangeView).where({
modification: "delete",
})
);
expect(changes).to.have.length(2);
expect(
changes.map((change) => ({
entityKey: change.entityKey,
entity: change.entity,
valueChangedFrom: change.valueChangedFrom,
valueChangedTo: change.valueChangedTo,
modification: change.modification,
attribute: change.attribute
}))
).to.have.deep.members([
{
entityKey: "64625905-c234-4d0d-9bc1-283ee8946770",
modification: "Delete",
entity: "Book",
attribute: "price",
valueChangedFrom: "0",
valueChangedTo: ""
},
{
entityKey: "64625905-c234-4d0d-9bc1-283ee8946770",
modification: "Delete",
entity: "Book",
attribute: "isUsed",
valueChangedFrom: "false",
valueChangedTo: ""
},
]);

delete cds.services.AdminService.entities.Books.elements.price["@changelog"];
});

it("2.1 Child entity creation - should log basic data type changes (ERP4SMEPREPWORKAPPPLAT-32 ERP4SMEPREPWORKAPPPLAT-613)", async () => {
const action = POST.bind(
{},
Expand Down
83 changes: 83 additions & 0 deletions tests/integration/service-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,89 @@ describe("change log integration test", () => {

const afterChanges = await adminService.run(SELECT.from(ChangeView));
expect(afterChanges.length).to.equal(6);
});

it("1.8 When creating or deleting a record with a numeric type of 0 and a boolean type of false, a changelog should also be generated", async () => {
cds.env.requires["change-tracking"].preserveDeletes = true;
cds.services.AdminService.entities.Order.elements.netAmount["@changelog"] = true;
cds.services.AdminService.entities.Order.elements.isUsed["@changelog"] = true;

const ordersData = {
ID: "0faaff2d-7e0e-4494-97fe-c815ee973fa1",
isUsed: false,
netAmount: 0
};

await INSERT.into(adminService.entities.Order).entries(ordersData);
let changes = await adminService.run(SELECT.from(ChangeView));

expect(changes).to.have.length(2);
expect(
changes.map((change) => ({
entityKey: change.entityKey,
entity: change.entity,
valueChangedFrom: change.valueChangedFrom,
valueChangedTo: change.valueChangedTo,
modification: change.modification,
attribute: change.attribute
}))
).to.have.deep.members([
{
entityKey: "0faaff2d-7e0e-4494-97fe-c815ee973fa1",
modification: "Create",
entity: "sap.capire.bookshop.Order",
attribute: "netAmount",
valueChangedFrom: "",
valueChangedTo: "0"
},
{
entityKey: "0faaff2d-7e0e-4494-97fe-c815ee973fa1",
modification: "Create",
entity: "sap.capire.bookshop.Order",
attribute: "isUsed",
valueChangedFrom: "",
valueChangedTo: "false"
},
]);

await DELETE.from(adminService.entities.Order).where({ ID: "0faaff2d-7e0e-4494-97fe-c815ee973fa1" });
changes = await adminService.run(
SELECT.from(ChangeView).where({
modification: "delete",
})
);

expect(changes).to.have.length(2);
expect(
changes.map((change) => ({
entityKey: change.entityKey,
entity: change.entity,
valueChangedFrom: change.valueChangedFrom,
valueChangedTo: change.valueChangedTo,
modification: change.modification,
attribute: change.attribute
}))
).to.have.deep.members([
{
entityKey: "0faaff2d-7e0e-4494-97fe-c815ee973fa1",
modification: "Delete",
entity: "sap.capire.bookshop.Order",
attribute: "netAmount",
valueChangedFrom: "0",
valueChangedTo: ""
},
{
entityKey: "0faaff2d-7e0e-4494-97fe-c815ee973fa1",
modification: "Delete",
entity: "sap.capire.bookshop.Order",
attribute: "isUsed",
valueChangedFrom: "false",
valueChangedTo: ""
},
]);

delete cds.services.AdminService.entities.Order.elements.netAmount["@changelog"];
delete cds.services.AdminService.entities.Order.elements.isUsed["@changelog"];
});

it("2.5 Root entity deep creation by service API - should log changes on root entity (ERP4SMEPREPWORKAPPPLAT-32 ERP4SMEPREPWORKAPPPLAT-613)", async () => {
Expand Down

0 comments on commit 457a2a0

Please sign in to comment.