diff --git a/repository/src/main/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceErrorCode.java b/repository/src/main/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceErrorCode.java index ba296fc7..fc648e3e 100644 --- a/repository/src/main/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceErrorCode.java +++ b/repository/src/main/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceErrorCode.java @@ -8,7 +8,6 @@ public class CompasSclDataServiceErrorCode { throw new UnsupportedOperationException("CompasSclDataRepositoryErrorCode class"); } - public static final String UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE = "SDS-0001"; public static final String CREATION_ERROR_CODE = "SDS-0002"; public static final String UNMARSHAL_ERROR_CODE = "SDS-0003"; public static final String HEADER_NOT_FOUND_ERROR_CODE = "SDS-0004"; diff --git a/repository/src/main/java/org/lfenergy/compas/scl/data/model/Version.java b/repository/src/main/java/org/lfenergy/compas/scl/data/model/Version.java index d52ef126..10ea5948 100644 --- a/repository/src/main/java/org/lfenergy/compas/scl/data/model/Version.java +++ b/repository/src/main/java/org/lfenergy/compas/scl/data/model/Version.java @@ -4,14 +4,13 @@ package org.lfenergy.compas.scl.data.model; import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.lfenergy.compas.scl.data.exception.CompasSclDataServiceException; import java.util.Objects; -import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE; - @Schema(description = "Presenting the version logic used in CoMPAS.") -public class Version { +public class Version implements Comparable { + public static final String PATTERN = "([1-9]\\d*)\\.(\\d+)\\.(\\d+)"; + @Schema(description = "The major version.", example = "2") private final int majorVersion; @Schema(description = "The minor version.", example = "1") @@ -40,7 +39,7 @@ private void validate(String version) { if (version == null || version.isEmpty()) { throw new IllegalArgumentException("Version can't be null or empty"); } - if (!version.matches("([1-9]\\d*)\\.(\\d+)\\.(\\d+)")) { + if (!version.matches(PATTERN)) { throw new IllegalArgumentException("Version is in the wrong format. Must consist of 3 number separated by dot (1.3.5)"); } } @@ -50,16 +49,11 @@ public Version getNextVersion(ChangeSetType changeSetType) { throw new IllegalArgumentException("ChangeSetType can't be null or empty"); } - switch (changeSetType) { - case MAJOR: - return new Version(majorVersion + 1, 0, 0); - case MINOR: - return new Version(majorVersion, minorVersion + 1, 0); - case PATCH: - return new Version(majorVersion, minorVersion, patchVersion + 1); - default: - throw new CompasSclDataServiceException(UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE, "Unhandled ChangeSetType " + changeSetType); - } + return switch (changeSetType) { + case MAJOR -> new Version(majorVersion + 1, 0, 0); + case MINOR -> new Version(majorVersion, minorVersion + 1, 0); + case PATCH -> new Version(majorVersion, minorVersion, patchVersion + 1); + }; } public int getMajorVersion() { @@ -92,6 +86,17 @@ public boolean equals(final Object obj) { } } + @Override + public int compareTo(Version otherVersion) { + if (this.majorVersion == otherVersion.getMajorVersion()) { + if (this.minorVersion == otherVersion.getMinorVersion()) { + return Integer.compare(this.patchVersion, otherVersion.getPatchVersion()); + } + return Integer.compare(this.minorVersion, otherVersion.getMinorVersion()); + } + return Integer.compare(this.majorVersion, otherVersion.getMajorVersion()); + } + @Override public String toString() { return majorVersion + "." + minorVersion + "." + patchVersion; diff --git a/repository/src/main/java/org/lfenergy/compas/scl/data/util/SclElementProcessor.java b/repository/src/main/java/org/lfenergy/compas/scl/data/util/SclElementProcessor.java index 08e65afc..88d6db2c 100644 --- a/repository/src/main/java/org/lfenergy/compas/scl/data/util/SclElementProcessor.java +++ b/repository/src/main/java/org/lfenergy/compas/scl/data/util/SclElementProcessor.java @@ -98,9 +98,50 @@ public Element addCompasElement(Element compasPrivate, String localName, String return element; } + /** + * The method will remove all newer Hitem Element, including the version passed from the History Element. + * It will search for Hitem Element where the Revision Attribute is empty and the version has the + * pattern "Major version"."Minor version"."Patch version". + *

+ * If the version is the same or newer the Hitem will be removed from the History Element. + * + * @param headerElement The Header Element containing the History Items. + * @param version The version from which to remove. + */ + public void cleanupHistoryItem(Element headerElement, Version version) { + var history = getChildNodesByName(headerElement, SCL_HISTORY_ELEMENT_NAME, SCL_NS_URI).stream().findFirst(); + history.ifPresent(historyElement -> + getChildNodesByName(historyElement, SCL_HITEM_ELEMENT_NAME, SCL_NS_URI) + .stream() + .filter(hItemElement -> hItemElement.getAttribute("revision").isBlank()) + .forEach(hItemElement -> { + if (shouldRemoveHItem(hItemElement, version)) { + historyElement.removeChild(hItemElement); + } + }) + ); + } + + /** + * Check if the version uses the pattern that matches the one used by CoMPAS and if this is the case + * compare the two versions and determine if the HItem version is smaller or the same as the new one + * being created. + * + * @param hItemElement The HItem Element to check the version attribute from. + * @param version The new version that will be created. + * @return True if the HItem has a smaller or the same version and should be removed. + */ + boolean shouldRemoveHItem(Element hItemElement, Version version) { + var hItemVersion = hItemElement.getAttribute("version"); + if (hItemVersion.isBlank() || !hItemVersion.matches(Version.PATTERN)) { + return false; + } + return version.compareTo(new Version(hItemVersion)) <= 0; + } + /** * Add a Hitem to the History Element from the Header. If the Header doesn't contain a History Element - * this Element will also be created. The revision attribute will be empty, the when will be set to the + * this Element will also be created. The revision attribute will be empty, the "when" will be set to the * current date. * * @param header The Header Element from SCL under which the History Element can be found/added. diff --git a/repository/src/test/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceExceptionTest.java b/repository/src/test/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceExceptionTest.java index 1dce9665..5ba8184d 100644 --- a/repository/src/test/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceExceptionTest.java +++ b/repository/src/test/java/org/lfenergy/compas/scl/data/exception/CompasSclDataServiceExceptionTest.java @@ -6,14 +6,14 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE; +import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.CREATION_ERROR_CODE; class CompasSclDataServiceExceptionTest { @Test void constructor_WhenCalledWithOnlyMessage_ThenMessageCanBeRetrieved() { String expectedMessage = "The message"; CompasSclDataServiceException exception = - new CompasSclDataServiceException(UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE, expectedMessage); + new CompasSclDataServiceException(CREATION_ERROR_CODE, expectedMessage); assertEquals(expectedMessage, exception.getMessage()); } @@ -23,7 +23,7 @@ void constructor_WhenCalledWithCauseAndMessage_ThenCauseAndMessageCanBeRetrieved String expectedMessage = "The message"; Exception expectedCause = new RuntimeException(); CompasSclDataServiceException exception = - new CompasSclDataServiceException(UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE, expectedMessage, expectedCause); + new CompasSclDataServiceException(CREATION_ERROR_CODE, expectedMessage, expectedCause); assertEquals(expectedMessage, exception.getMessage()); assertEquals(expectedCause, exception.getCause()); diff --git a/repository/src/test/java/org/lfenergy/compas/scl/data/model/VersionTest.java b/repository/src/test/java/org/lfenergy/compas/scl/data/model/VersionTest.java index 90f4a9c8..ff96f076 100644 --- a/repository/src/test/java/org/lfenergy/compas/scl/data/model/VersionTest.java +++ b/repository/src/test/java/org/lfenergy/compas/scl/data/model/VersionTest.java @@ -13,7 +13,6 @@ void constructor_WhenCalledWithNullOrEmptyValue_ThenExceptionThrown() { var expectedMessage = "Version can't be null or empty"; assertThrows(IllegalArgumentException.class, () -> new Version(null), expectedMessage); - assertThrows(IllegalArgumentException.class, () -> new Version(""), expectedMessage); } @@ -93,4 +92,25 @@ void equals_WhenTwoSameObject_ThenEqualsShouldAlsoBeTheSame() { void toString_WhenCalled_ThenCorrectStringReturned() { assertEquals("1.5.8", new Version(1, 5, 8).toString()); } + + @Test + void compareTo_WhenWhenMajorVersionAreDifferent_ThenCorrectResult() { + assertEquals(-1, new Version("1.0.0").compareTo(new Version("2.0.0"))); + assertEquals(0, new Version("1.0.0").compareTo(new Version("1.0.0"))); + assertEquals(1, new Version("2.0.0").compareTo(new Version("1.0.0"))); + } + + @Test + void compareTo_WhenWhenMinorVersionAreDifferent_ThenCorrectResult() { + assertEquals(-1, new Version("1.1.0").compareTo(new Version("1.2.0"))); + assertEquals(0, new Version("1.1.0").compareTo(new Version("1.1.0"))); + assertEquals(1, new Version("1.2.0").compareTo(new Version("1.1.0"))); + } + + @Test + void compareTo_WhenWhenPathVersionAreDifferent_ThenCorrectResult() { + assertEquals(-1, new Version("1.1.1").compareTo(new Version("1.1.2"))); + assertEquals(0, new Version("1.1.1").compareTo(new Version("1.1.1"))); + assertEquals(1, new Version("1.1.2").compareTo(new Version("1.1.1"))); + } } \ No newline at end of file diff --git a/repository/src/test/java/org/lfenergy/compas/scl/data/util/SclElementProcessorTest.java b/repository/src/test/java/org/lfenergy/compas/scl/data/util/SclElementProcessorTest.java index 8c88e0c2..d5930ffc 100644 --- a/repository/src/test/java/org/lfenergy/compas/scl/data/util/SclElementProcessorTest.java +++ b/repository/src/test/java/org/lfenergy/compas/scl/data/util/SclElementProcessorTest.java @@ -9,6 +9,9 @@ import org.lfenergy.compas.scl.data.model.Version; import org.w3c.dom.Element; +import java.util.List; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.*; import static org.lfenergy.compas.scl.data.SclDataServiceConstants.*; import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.HEADER_NOT_FOUND_ERROR_CODE; @@ -188,6 +191,73 @@ void getAttributeValue_WhenCalledForNonExistingAttribute_ThenOptionalEmptyReturn assertFalse(result.isPresent()); } + @Test + void cleanupHistoryItem_WhenCalledWithVersion_ThenSameAndNewerVersionsAreRemoved() { + var scl = readSCL("scl_cleanup_history.scd"); + + assertEquals(7, getHItems(scl).size()); + processor.cleanupHistoryItem(processor.getSclHeader(scl).orElseThrow(), new Version("1.0.2")); + assertEquals(5, getHItems(scl).size()); + } + + @Test + void shouldRemoveHItem_WhenCalledWithInvalidVersion_ThenFalseReturned() { + var scl = readSCL("scl_cleanup_history.scd"); + var hItem = getHItem(scl, "Siemens"); + + assertFalse(processor.shouldRemoveHItem(hItem, new Version("1.0.2"))); + } + + @Test + void shouldRemoveHItem_WhenCalledWithEmptyVersion_ThenFalseReturned() { + var scl = readSCL("scl_cleanup_history.scd"); + var hItem = getHItem(scl, "Empty"); + + assertFalse(processor.shouldRemoveHItem(hItem, new Version("1.0.2"))); + } + + @Test + void shouldRemoveHItem_WhenCalledWithOlderVersion_ThenFalseReturned() { + var scl = readSCL("scl_cleanup_history.scd"); + var hItem = getHItem(scl, "Created"); + + assertFalse(processor.shouldRemoveHItem(hItem, new Version("1.0.2"))); + } + + @Test + void shouldRemoveHItem_WhenCalledWithSameVersion_ThenTrueReturned() { + var scl = readSCL("scl_cleanup_history.scd"); + var hItem = getHItem(scl, "Updated 1"); + + assertTrue(processor.shouldRemoveHItem(hItem, new Version("1.0.2"))); + } + + @Test + void shouldRemoveHItem_WhenCalledWithNewerVersion_ThenTrueReturned() { + var scl = readSCL("scl_cleanup_history.scd"); + var hItem = getHItem(scl, "Updated 2"); + + assertTrue(processor.shouldRemoveHItem(hItem, new Version("1.0.2"))); + } + + private List getHItems(Element scl) { + return processor.getSclHeader(scl) + .map(header -> processor.getChildNodeByName(header, SCL_HISTORY_ELEMENT_NAME, SCL_NS_URI)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(history -> processor.getChildNodesByName(history, SCL_HITEM_ELEMENT_NAME, SCL_NS_URI)) + .stream() + .flatMap(List::stream) + .toList(); + } + + private Element getHItem(Element scl, String what) { + return getHItems(scl).stream() + .filter(element -> element.getAttribute("what").equals(what)) + .findFirst() + .get(); + } + private Element readSCL(String sclFilename) { var inputStream = getClass().getResourceAsStream("/scl/" + sclFilename); assert inputStream != null; diff --git a/repository/src/test/resources/scl/scl_cleanup_history.scd b/repository/src/test/resources/scl/scl_cleanup_history.scd new file mode 100644 index 00000000..8f7dfcf3 --- /dev/null +++ b/repository/src/test/resources/scl/scl_cleanup_history.scd @@ -0,0 +1,17 @@ + + + + +

+ + + + + + + + + +
+ \ No newline at end of file diff --git a/service/src/main/java/org/lfenergy/compas/scl/data/service/CompasSclDataService.java b/service/src/main/java/org/lfenergy/compas/scl/data/service/CompasSclDataService.java index 2875c85c..fbae11dd 100644 --- a/service/src/main/java/org/lfenergy/compas/scl/data/service/CompasSclDataService.java +++ b/service/src/main/java/org/lfenergy/compas/scl/data/service/CompasSclDataService.java @@ -133,7 +133,8 @@ public String create(SclFileType type, String name, String who, String comment, // Update the Header of the SCL (or create if not exists.) var header = createOrUpdateHeader(scl, id, version); - createHistoryItem(header, "SCL created", who, comment, version); + sclElementProcessor.cleanupHistoryItem(header, version); + sclElementProcessor.addHistoryItem(header, who, createMessage("SCL created", comment), version); // Update or add the Compas Private Element to the SCL File. setSclCompasPrivateElement(scl, name, type); @@ -149,7 +150,7 @@ public String create(SclFileType type, String name, String who, String comment, /** * Create a new version of a specific SCL XML File. The content will be the passed SCL XML File. * The UUID and new version (depending on the passed ChangeSetType) are set and - * the CoMPAS Private elements will also be copied, the SCL Name will only be copied if not available in the passed + * the CoMPAS Private elements will also be copied, the SCL Name will only be copied if it isn't available in the * SCL XML File. * * @param type The type to update it for. @@ -180,7 +181,8 @@ public String update(SclFileType type, UUID id, ChangeSetType changeSetType, Str // Update the Header of the SCL (or create if not exists.) var header = createOrUpdateHeader(scl, id, version); - createHistoryItem(header, "SCL updated", who, comment, version); + sclElementProcessor.cleanupHistoryItem(header, version); + sclElementProcessor.addHistoryItem(header, who, createMessage("SCL updated", comment), version); // Update or add the Compas Private Element to the SCL File. var newSclName = newFileName.orElse(currentSclMetaInfo.getName()); @@ -278,20 +280,18 @@ private void setSclCompasPrivateElement(Element scl, String name, SclFileType fi } /** - * Add a Hitem to the current or added History Element from the Header. + * If a comment is added by the user, a standard message will be joined together with the comment from the user. * - * @param header The header of SCL file to edit. - * @param message The message set on the Hitem. - * @param who The user that made the change. - * @param comment If filled the comment that will be added to the message. - * @param version The version set on the Hitem. + * @param standardMessage The standard message. + * @param comment The comment a user may have added. + * @return The full message to be added to the HItem. */ - private void createHistoryItem(Element header, String message, String who, String comment, Version version) { - var fullmessage = message; + private String createMessage(String standardMessage, String comment) { + var message = standardMessage; if (comment != null && !comment.isBlank()) { - fullmessage += ", " + comment; + message += ", " + comment; } - sclElementProcessor.addHistoryItem(header, who, fullmessage, version); + return message; } /** diff --git a/service/src/test/java/org/lfenergy/compas/scl/data/service/CompasSclDataServiceTest.java b/service/src/test/java/org/lfenergy/compas/scl/data/service/CompasSclDataServiceTest.java index 7576b531..926d487d 100644 --- a/service/src/test/java/org/lfenergy/compas/scl/data/service/CompasSclDataServiceTest.java +++ b/service/src/test/java/org/lfenergy/compas/scl/data/service/CompasSclDataServiceTest.java @@ -127,7 +127,7 @@ void create_WhenCalledWithOutCompasExtension_ThenSCLReturnedWithCorrectCompasExt assertNotNull(scl); assertCompasExtension(scl, name); - assertHistoryItem(scl, INITIAL_VERSION, comment); + assertHistoryItem(scl, 2, INITIAL_VERSION, comment); verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), any(UUID.class), eq(name), anyString(), eq(INITIAL_VERSION), eq(who), eq(emptyList())); verify(compasSclDataRepository, times(1)).hasDuplicateSclName(SCL_TYPE, name); } @@ -148,7 +148,7 @@ void create_WhenCalledWithCompasExtension_ThenSCLReturnedWithCorrectCompasExtens assertNotNull(scl); assertCompasExtension(scl, name); - assertHistoryItem(scl, INITIAL_VERSION, comment); + assertHistoryItem(scl, 2, INITIAL_VERSION, comment); verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), any(UUID.class), eq(name), anyString(), eq(INITIAL_VERSION), eq(who), eq(emptyList())); verify(compasSclDataRepository, times(1)).hasDuplicateSclName(SCL_TYPE, name); } @@ -201,7 +201,7 @@ void update_WhenCalledWithoutCompasElements_ThenSCLReturnedWithCorrectCompasExte assertNotNull(scl); assertCompasExtension(scl, previousName); - assertHistoryItem(scl, nextVersion, null); + assertHistoryItem(scl, 4, nextVersion, null); verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), eq(uuid), eq(previousName), anyString(), eq(nextVersion), eq(who), eq(emptyList())); verify(compasSclDataRepository, times(1)).findMetaInfoByUUID(SCL_TYPE, uuid); verify(compasSclDataRepository, never()).hasDuplicateSclName(SCL_TYPE, previousName); @@ -228,7 +228,7 @@ void update_WhenCalledWithCompasElementsAndNewName_ThenSCLReturnedWithCorrectCom assertNotNull(scl); assertCompasExtension(scl, newName); - assertHistoryItem(scl, nextVersion, null); + assertHistoryItem(scl, 4, nextVersion, null); verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), eq(uuid), eq(newName), anyString(), eq(nextVersion), eq(who), eq(emptyList())); verify(compasSclDataRepository, times(1)).findMetaInfoByUUID(SCL_TYPE, uuid); verify(compasSclDataRepository, times(1)).hasDuplicateSclName(SCL_TYPE, newName); @@ -275,7 +275,7 @@ void update_WhenCalledWithCompasElementsAndSameName_ThenSCLReturnedWithCorrectCo assertNotNull(scl); assertCompasExtension(scl, previousName); - assertHistoryItem(scl, nextVersion, null); + assertHistoryItem(scl, 4, nextVersion, null); verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), eq(uuid), eq(previousName), anyString(), eq(nextVersion), eq(who), eq(emptyList())); verify(compasSclDataRepository, times(1)).findMetaInfoByUUID(SCL_TYPE, uuid); verify(compasSclDataRepository, never()).hasDuplicateSclName(SCL_TYPE, previousName); @@ -386,7 +386,7 @@ private void assertCompasExtension(String sclData, String name) { assertEquals(SCL_TYPE.toString(), typeElement.get().getTextContent()); } - private void assertHistoryItem(String sclData, Version version, String comment) { + private void assertHistoryItem(String sclData, int expectedHItems, Version version, String comment) { var scl = converter.convertToElement(sclData, SCL_ELEMENT_NAME, SCL_NS_URI); var header = processor.getSclHeader(scl); assertTrue(header.isPresent()); @@ -395,7 +395,7 @@ private void assertHistoryItem(String sclData, Version version, String comment) assertTrue(history.isPresent()); var items = processor.getChildNodesByName(history.get(), SCL_HITEM_ELEMENT_NAME, SCL_NS_URI); - assertFalse(items.isEmpty()); + assertEquals(expectedHItems, items.size()); // The last item should be the one added. var item = items.get(items.size() - 1); assertEquals(version.toString(), item.getAttribute(SCL_VERSION_ATTR)); diff --git a/service/src/test/resources/scl/scl_test_file.scd b/service/src/test/resources/scl/scl_test_file.scd index 4dc2b380..9434cc04 100644 --- a/service/src/test/resources/scl/scl_test_file.scd +++ b/service/src/test/resources/scl/scl_test_file.scd @@ -3,8 +3,17 @@ - -
+ +
+ + + + + + +
@@ -59,7 +68,8 @@ - +