diff --git a/QLACK-Fuse/qlack-fuse-acv/README.md b/QLACK-Fuse/qlack-fuse-acv/README.md index abefb338..5cf2081e 100644 --- a/QLACK-Fuse/qlack-fuse-acv/README.md +++ b/QLACK-Fuse/qlack-fuse-acv/README.md @@ -3,17 +3,20 @@ This module provides Object Auditing, Comparison and Versioning operations. ## Integration -Add following property in your projects application.properties file, in order to persist the +Add following property in your projects application.properties file, in order to persist the object versions in the database used by your application. -`javers.sqlSchema.sqlSchemaManagementEnabled=true` +`javers.sqlSchemaManagementEnabled=true` + +This is Javers' default, so the property only needs to be set explicitly when it has been +disabled elsewhere. ### Add qlack-fuse-acv dependency to your pom.xml: ```xml - com.eurodyn.qlack.fuse - qlack-fuse-acv - ${qlack.version} - + com.eurodyn.qlack.fuse + qlack-fuse-acv + ${qlack.version} + ``` ### Add the packages in the Spring boot application main class declaration: diff --git a/QLACK-Fuse/qlack-fuse-acv/pom.xml b/QLACK-Fuse/qlack-fuse-acv/pom.xml index 711878bd..e7b6e704 100644 --- a/QLACK-Fuse/qlack-fuse-acv/pom.xml +++ b/QLACK-Fuse/qlack-fuse-acv/pom.xml @@ -13,7 +13,7 @@ - 5.15.0 + 7.11.7 diff --git a/QLACK-Fuse/qlack-fuse-acv/src/main/java/com/eurodyn/qlack/fuse/acv/service/CompareService.java b/QLACK-Fuse/qlack-fuse-acv/src/main/java/com/eurodyn/qlack/fuse/acv/service/CompareService.java index ddf01c41..e9db86d4 100644 --- a/QLACK-Fuse/qlack-fuse-acv/src/main/java/com/eurodyn/qlack/fuse/acv/service/CompareService.java +++ b/QLACK-Fuse/qlack-fuse-acv/src/main/java/com/eurodyn/qlack/fuse/acv/service/CompareService.java @@ -7,6 +7,8 @@ import lombok.NonNull; import org.javers.core.Javers; import org.javers.core.diff.Diff; +import org.javers.core.diff.changetype.InitialValueChange; +import org.javers.core.diff.changetype.TerminalValueChange; import org.javers.core.diff.changetype.ValueChange; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -56,12 +58,32 @@ public List compare(@NonNull T obj1, @NonNull T obj2) { Diff result = javers.compare(obj1, obj2); List changes = result.getChangesByType(ValueChange.class) - .parallelStream() - .map(this::convertToChangeDTO).collect(Collectors.toList()); + .parallelStream() + .filter(CompareService::isValueChange) + .map(this::convertToChangeDTO).collect(Collectors.toList()); return Collections.unmodifiableList(changes); } + /** + * Keeps only real, before/after property value changes. + *

+ * Javers 7 reports the whole state of an added or a removed object as + * {@link InitialValueChange} and {@link TerminalValueChange}. Both extend + * {@link ValueChange}, so they are also returned by + * {@link Diff#getChangesByType(Class)}, which would add one entry per + * property of every added/removed object, each having either the "from" or + * the "to" side always null. Such entries were not produced by earlier Javers + * versions and are filtered out here. + * + * @param change the change to examine + * @return true if the change is a plain value change, else false + */ + private static boolean isValueChange(ValueChange change) { + return !(change instanceof InitialValueChange) + && !(change instanceof TerminalValueChange); + } + /** * Compares two object versions and returns the changes. *

@@ -74,10 +96,10 @@ public List compare(@NonNull T obj1, @NonNull T obj2) { * @return a list with the changes between the two comparing objects */ public List compareVersions(Object object, long version1, - long version2) { + long version2) { return compare(versioningService.retrieveVersion(object, version1), - versioningService.retrieveVersion(object, version2)); + versioningService.retrieveVersion(object, version2)); } /** diff --git a/QLACK-Fuse/qlack-fuse-acv/src/test/java/com/eurodyn/qlack/fuse/acv/service/CompareServiceTest.java b/QLACK-Fuse/qlack-fuse-acv/src/test/java/com/eurodyn/qlack/fuse/acv/service/CompareServiceTest.java index 25d3cb16..12dc59fd 100644 --- a/QLACK-Fuse/qlack-fuse-acv/src/test/java/com/eurodyn/qlack/fuse/acv/service/CompareServiceTest.java +++ b/QLACK-Fuse/qlack-fuse-acv/src/test/java/com/eurodyn/qlack/fuse/acv/service/CompareServiceTest.java @@ -4,13 +4,17 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; +import com.eurodyn.qlack.fuse.acv.dto.ChangeDTO; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import org.javers.core.Javers; import org.javers.core.diff.Diff; +import org.javers.core.diff.changetype.InitialValueChange; import org.javers.core.diff.changetype.PropertyChangeMetadata; import org.javers.core.diff.changetype.PropertyChangeType; +import org.javers.core.diff.changetype.TerminalValueChange; import org.javers.core.diff.changetype.ValueChange; import org.javers.core.metamodel.object.GlobalId; import org.junit.jupiter.api.BeforeEach; @@ -69,7 +73,7 @@ public void compareVersionsTest() { when(diff.getChangesByType(ValueChange.class)).thenReturn(valueChangeList); assertEquals(Collections.emptyList(), - compareService.compareVersions(object1, 1L, 2L)); + compareService.compareVersions(object1, 1L, 2L)); } @Test @@ -82,7 +86,7 @@ public void testHasChanges() { @Test public void hasChangesNullObj1Test() { assertThrows(NullPointerException.class, () -> - compareService.hasChanges(null, object2)); + compareService.hasChanges(null, object2)); } @Test @@ -113,7 +117,7 @@ public void compareObjectWithVersionTest() { when(diff.getChangesByType(ValueChange.class)).thenReturn(valueChangeList); assertEquals(Collections.emptyList(), - compareService.compareObjectWithVersion(object1, 1L)); + compareService.compareObjectWithVersion(object1, 1L)); } @Test @@ -123,16 +127,48 @@ public void compareObjectWithLatestVersionTest() { when(diff.getChangesByType(ValueChange.class)).thenReturn(valueChangeList); assertEquals(Collections.emptyList(), - compareService.compareObjectWithLatestVersion(object1)); + compareService.compareObjectWithLatestVersion(object1)); } @Test public void convertToChangeDTOTest() { PropertyChangeMetadata metadata = new PropertyChangeMetadata(globalId, "property", Optional.empty(), - PropertyChangeType.PROPERTY_VALUE_CHANGED); + PropertyChangeType.PROPERTY_VALUE_CHANGED); ValueChange valueChange = new ValueChange(metadata, object1, object2); assertNotNull(compareService.convertToChangeDTO(valueChange)); } + /** + * Since Javers 7, {@link Diff#getChangesByType(Class)} also returns the + * {@link InitialValueChange} and {@link TerminalValueChange} subtypes, which + * describe the state of added/removed objects rather than an actual + * before/after change. Those must not end up in the reported changes. + */ + @Test + public void compareSkipsInitialAndTerminalValueChangesTest() { + ValueChange valueChange = new ValueChange( + propertyChangeMetadata("property"), object1, object2); + InitialValueChange initialValueChange = new InitialValueChange( + propertyChangeMetadata("addedProperty"), object2); + TerminalValueChange terminalValueChange = new TerminalValueChange( + propertyChangeMetadata("removedProperty"), object1); + + when(javers.compare(object1, object2)).thenReturn(diff); + when(diff.getChangesByType(ValueChange.class)).thenReturn( + Arrays.asList(initialValueChange, valueChange, terminalValueChange)); + + List changes = compareService.compare(object1, object2); + + assertEquals(1, changes.size()); + assertEquals("property", changes.get(0).getPropertyName()); + assertEquals(object1, changes.get(0).getFrom()); + assertEquals(object2, changes.get(0).getTo()); + } + + private PropertyChangeMetadata propertyChangeMetadata(String propertyName) { + return new PropertyChangeMetadata(globalId, propertyName, Optional.empty(), + PropertyChangeType.PROPERTY_VALUE_CHANGED); + } + } diff --git a/pom.xml b/pom.xml index d152ed55..11c60de3 100644 --- a/pom.xml +++ b/pom.xml @@ -60,10 +60,10 @@ 5.15.2 - 8.7.0 + 8.8.0 1.6.0 5.4.0 - 3.2.2 + 3.3.2 2.0b6 1.80 2.18.0