diff --git a/dhis-2/dhis-api/pom.xml b/dhis-2/dhis-api/pom.xml index 4a7fa1a4189c..5a864693924f 100644 --- a/dhis-2/dhis-api/pom.xml +++ b/dhis-2/dhis-api/pom.xml @@ -105,10 +105,6 @@ org.springframework.security spring-security-core - - commons-codec - commons-codec - org.locationtech.jts jts-core diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/QueryKey.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/QueryKey.java index 2a3b605134f3..a1da5835f529 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/QueryKey.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/QueryKey.java @@ -30,8 +30,8 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import org.hisp.dhis.common.HashUtils; /** * @author Lars Helge Overland @@ -110,7 +110,7 @@ public String asPlainKey() { /** Returns a 40-character unique key. The key is a SHA-1 hash of the components of this key. */ public String build() { - return DigestUtils.sha1Hex(asPlainKey()); + return HashUtils.hashSHA1(asPlainKey().getBytes()); } /** Equal to {@link QueryKey#build()}. */ diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/HashUtils.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/HashUtils.java index d77a6cd72cef..d21c6b83967f 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/HashUtils.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/HashUtils.java @@ -43,6 +43,26 @@ private HashUtils() { throw new IllegalStateException("Utility class"); } + /** + * Calculates a MD5 hash for the given input string. + * + * @param bytes the input string. + * @return the hash. + */ + public static String hashMD5(@Nonnull byte[] bytes) { + return Hashing.md5().hashBytes(bytes).toString(); + } + + /** + * Calculates a SHA1 hash for the given input string. + * + * @param bytes the input string. + * @return the hash. + */ + public static String hashSHA1(@Nonnull byte[] bytes) { + return Hashing.sha1().hashBytes(bytes).toString(); + } + /** * Calculates a SHA256 hash for the given input string. * diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/dxf2/webmessage/responses/MergeWebResponse.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/dxf2/webmessage/responses/MergeWebResponse.java index 4e00bfb25e8f..17188062cb77 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/dxf2/webmessage/responses/MergeWebResponse.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/dxf2/webmessage/responses/MergeWebResponse.java @@ -46,8 +46,8 @@ public MergeWebResponse(@Nonnull MergeReport mergeReport) { MergeType mergeType = mergeReport.getMergeType(); this.mergeReport.setMessage( mergeReport.hasErrorMessages() - ? "%s merge has errors".formatted(mergeType) - : "%s merge complete".formatted(mergeType)); + ? "%s merge has errors".formatted(mergeType.getName()) + : "%s merge complete".formatted(mergeType.getName())); } @Nonnull diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java index 3f307a13ac40..dea0cc65ed49 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java @@ -89,33 +89,17 @@ public enum ErrorCode { E1522("User `{0}` is not allowed to move organisation `{1}` unit from parent `{2}`"), E1523("User `{0}` is not allowed to move organisation `{1}` unit to parent `{2}`"), - /* Indicator Type merge */ - E1530("At least one source indicator type must be specified"), - E1531("Target indicator type must be specified"), - E1532("Target indicator type cannot be a source indicator type"), - E1533("{0} indicator type does not exist: `{1}`"), - - /* Indicator merge */ - E1540("At least one source indicator must be specified"), - E1541("Target indicator must be specified"), - E1542("Target indicator cannot be a source indicator"), - E1543("{0} indicator does not exist: `{1}`"), + /* Generic merge errors */ + E1530("At least one source {0} must be specified"), + E1531("Target {0} must be specified"), + E1532("Target {0} cannot be a source {1}"), + E1533("{0} {1} does not exist: `{2}`"), + E1534("dataMergeStrategy field must be specified. With value `DISCARD` or `LAST_UPDATED`"), /* DataElement merge */ - E1550("At least one source data element must be specified"), - E1551("Target data element must be specified"), - E1552("Target data element cannot be a source data element"), - E1553("{0} data element does not exist: `{1}`"), - E1554("All source ValueTypes must match target ValueType: `{0}`. Other ValueTypes found: `{1}`"), - E1555( + E1550("All source ValueTypes must match target ValueType: `{0}`. Other ValueTypes found: `{1}`"), + E1551( "All source DataElementDomains must match target DataElementDomain: `{0}`. Other DataElementDomains found: `{1}`"), - E1556("dataMergeStrategy field must be specified. With value `DISCARD` or `LAST_UPDATED`"), - - /* CategoryOption merge */ - E1650("At least one source category option must be specified"), - E1651("Target category option must be specified"), - E1652("Target category option cannot be a source category option"), - E1653("{0} category option does not exist: `{1}`"), /* Data */ E2000("Query parameters cannot be null"), diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/MergeReport.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/MergeReport.java index 9d7c6dab1244..3a771fe1f91d 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/MergeReport.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/MergeReport.java @@ -54,10 +54,6 @@ public class MergeReport implements ErrorMessageContainer { @JsonProperty private Set sourcesDeleted = new HashSet<>(); @JsonProperty private String message; - public MergeReport(MergeType mergeType) { - this.mergeType = mergeType; - } - @Override public boolean hasErrorMessages() { return !mergeErrors.isEmpty(); diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeParams.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeParams.java index d0f9ef9c7734..d3b81b230d3c 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeParams.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeParams.java @@ -50,6 +50,4 @@ public class MergeParams { @JsonProperty private boolean deleteSources; @JsonProperty private DataMergeStrategy dataMergeStrategy; - - private MergeType mergeType; } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeProcessor.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeProcessor.java deleted file mode 100644 index c26c3bcfb7ff..000000000000 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeProcessor.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2004-2023, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.merge; - -import javax.annotation.Nonnull; -import org.hisp.dhis.common.IdentifiableObject; -import org.hisp.dhis.feedback.ConflictException; -import org.hisp.dhis.feedback.MergeReport; - -/** - * Interface with default method used to process merging of {@link IdentifiableObject}s uniformly, - * in a standardised fashion. It requires an implementation of {@link MergeService}, which will - * process the merging for its required use case.
- * It essentially calls each method of the {@link MergeService} in the following order:
- * - *
    - *
  1. validate - *
  2. merge - *
- */ -public interface MergeProcessor { - - /** - * @return implemented {@link MergeService} to process merge - */ - MergeService getMergeService(); - - /** - * Processes a merge in full, using the implemented {@link MergeService} retrieved. - * - * @param mergeParams {@link MergeParams} to process - * @return updated {@link MergeReport} with any errors - */ - default MergeReport processMerge(@Nonnull MergeParams mergeParams) throws ConflictException { - MergeReport mergeReport = new MergeReport(mergeParams.getMergeType()); - - MergeRequest mergeRequest = getMergeService().validate(mergeParams, mergeReport); - if (mergeReport.hasErrorMessages()) - throw new ConflictException("Merge validation error").setMergeReport(mergeReport); - - MergeReport report = getMergeService().merge(mergeRequest, mergeReport); - if (report.hasErrorMessages()) - throw new ConflictException("Merge error").setMergeReport(mergeReport); - - return report; - } -} diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeService.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeService.java index 913503f4dea1..f99bb188783e 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeService.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeService.java @@ -29,6 +29,7 @@ import javax.annotation.Nonnull; import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.MergeReport; import org.springframework.transaction.annotation.Transactional; @@ -39,6 +40,27 @@ */ public interface MergeService { + /** + * Processes a merge in full. + * + * @param mergeParams {@link MergeParams} to process + * @return updated {@link MergeReport} with any errors + */ + @Transactional + default MergeReport processMerge(@Nonnull MergeParams mergeParams) throws ConflictException { + MergeReport mergeReport = new MergeReport(); + + MergeRequest mergeRequest = validate(mergeParams, mergeReport); + if (mergeReport.hasErrorMessages()) + throw new ConflictException("Merge validation error").setMergeReport(mergeReport); + + merge(mergeRequest, mergeReport); + if (mergeReport.hasErrorMessages()) + throw new ConflictException("Merge error").setMergeReport(mergeReport); + + return mergeReport; + } + /** * This method transforms a {@link MergeParams} to a {@link MergeRequest}. If there are any * errors/issues with the params then the {@link MergeReport} should be updated. @@ -57,6 +79,5 @@ public interface MergeService { * @param mergeReport report to be updated if any issues/errors with the {@link MergeRequest} * @return {@link MergeReport} */ - @Transactional MergeReport merge(@Nonnull MergeRequest request, @Nonnull MergeReport mergeReport); } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeType.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeType.java index d86b662143d2..a19697ace91b 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeType.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeType.java @@ -27,16 +27,38 @@ */ package org.hisp.dhis.merge; +import com.fasterxml.jackson.annotation.JsonValue; +import org.hisp.dhis.category.CategoryOption; +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.dataelement.DataElement; +import org.hisp.dhis.indicator.Indicator; +import org.hisp.dhis.indicator.IndicatorType; + /** * Enum for merge type. * * @author david mackessy */ public enum MergeType { - ORG_UNIT, + INDICATOR_TYPE(IndicatorType.class), + INDICATOR(Indicator.class), + DATA_ELEMENT(DataElement.class), + CATEGORY_OPTION(CategoryOption.class); + + private final Class clazz; + private final String name; + + MergeType(Class clazz) { + this.clazz = clazz; + this.name = clazz.getSimpleName(); + } + + public Class getClazz() { + return this.clazz; + } - INDICATOR_TYPE, - INDICATOR, - DATA_ELEMENT, - CATEGORY_OPTION, + @JsonValue + public String getName() { + return this.name; + } } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeValidator.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeValidator.java index 471ecd7e6143..cc1e3fb5534c 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeValidator.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/merge/MergeValidator.java @@ -28,6 +28,7 @@ package org.hisp.dhis.merge; import java.util.Set; +import javax.annotation.Nonnull; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.UID; import org.hisp.dhis.feedback.MergeReport; @@ -37,18 +38,28 @@ */ public interface MergeValidator { + /** + * Validates source & target {@link UID}s passed in the params. Validation used by all merges. + * + * @param params {@link MergeParams} that contain {@link UID}s to validate + * @param mergeReport {@link MergeReport} to update + * @param mergeType {@link MergeType} + * @return {@link MergeRequest} to process + */ + MergeRequest validateUIDs( + @Nonnull MergeParams params, @Nonnull MergeReport mergeReport, @Nonnull MergeType mergeType); + /** * Verifies whether the source {@link UID}s map to valid {@link IdentifiableObject}s.
* - If they are valid then they are added to verifiedSources param.
* - If any are not valid then the {@link MergeReport} is updated with an error. * * @param paramSources {@link UID}s - * @param verifiedSources set to add verified source {@link UID}s * @param mergeReport to update if any error - * @param clazz {@link IdentifiableObject} type + * @param mergeType {@link MergeType} + * @return verified source {@link UID}s */ - void verifySources( - Set paramSources, Set verifiedSources, MergeReport mergeReport, Class clazz); + Set verifySources(Set paramSources, MergeReport mergeReport, MergeType mergeType); /** * Checks whether the target is referenced in the sources collection
@@ -57,10 +68,10 @@ void verifySources( * @param sources to check * @param target to check if in sources * @param mergeReport to update if any error - * @param clazz {@link IdentifiableObject} type + * @param mergeType {@link MergeType} */ - void checkIsTargetInSources( - Set sources, UID target, MergeReport mergeReport, Class clazz); + void checkIsTargetInSources( + Set sources, UID target, MergeReport mergeReport, MergeType mergeType); /** * Verifies whether the target {@link UID} maps to a valid {@link IdentifiableObject}.
@@ -71,9 +82,9 @@ void checkIsTargetInSources( * @param mergeReport to update if any error * @param sources to return in merge request * @param params merge params with target to verify - * @param clazz {@link IdentifiableObject} type + * @param mergeType {@link MergeType} * @return merge request */ - MergeRequest verifyTarget( - MergeReport mergeReport, Set sources, MergeParams params, Class clazz); + MergeRequest verifyTarget( + MergeReport mergeReport, Set sources, MergeParams params, MergeType mergeType); } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/setting/SystemSettings.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/setting/SystemSettings.java index 832fff4601e5..26b2a534cd47 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/setting/SystemSettings.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/setting/SystemSettings.java @@ -721,6 +721,13 @@ default String getGlobalShellAppName() { return asString("globalShellAppName", "global-app-shell"); } + /** + * @return true if email verification is enforced for all users. + */ + default boolean getEnforceVerifiedEmail() { + return asBoolean("enforceVerifiedEmail", false); + } + /** Combinators based on several settings. */ default boolean isEmailConfigured() { return !getEmailHostName().isBlank() && !getEmailUsername().isBlank(); diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/SystemUser.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/SystemUser.java index 8aa2f6d2b048..b238adac245f 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/SystemUser.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/SystemUser.java @@ -184,6 +184,11 @@ public boolean isTwoFactorEnabled() { return false; } + @Override + public boolean isEmailVerified() { + return true; + } + @Override public boolean hasAnyRestrictions(Collection restrictions) { return false; diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/User.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/User.java index 12e672f36f8c..1bb2880b6670 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/User.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/User.java @@ -41,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -1192,6 +1193,12 @@ public String getVerifiedEmail() { return this.verifiedEmail; } + @JsonProperty + @JacksonXmlProperty(namespace = DxfNamespaces.DXF_2_0) + public boolean isEmailVerified() { + return this.getEmail() != null && Objects.equals(this.getEmail(), this.getVerifiedEmail()); + } + public void setVerifiedEmail(String verifiedEmail) { this.verifiedEmail = verifiedEmail; } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java index 9c9db96a73b8..3b45942510b6 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java @@ -118,6 +118,7 @@ static UserDetails createUserDetails( .password(user.getPassword()) .externalAuth(user.isExternalAuth()) .isTwoFactorEnabled(user.isTwoFactorEnabled()) + .isEmailVerified(user.isEmailVerified()) .code(user.getCode()) .firstName(user.getFirstName()) .surname(user.getSurname()) @@ -243,6 +244,8 @@ static UserDetails createUserDetails( boolean isTwoFactorEnabled(); + boolean isEmailVerified(); + boolean hasAnyRestrictions(Collection restrictions); void setId(Long id); diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetailsImpl.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetailsImpl.java index e29438f00843..522bc1586093 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetailsImpl.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetailsImpl.java @@ -53,6 +53,7 @@ public class UserDetailsImpl implements UserDetails { private final String password; private final boolean externalAuth; private final boolean isTwoFactorEnabled; + private final boolean isEmailVerified; private final boolean enabled; private final boolean accountNonExpired; private final boolean accountNonLocked; @@ -82,6 +83,11 @@ public boolean canModifyUser(User other) { return auths.containsAll(other.getAllAuthorities()); } + @Override + public boolean isEmailVerified() { + return this.isEmailVerified; + } + @Override public boolean hasAnyRestrictions(Collection restrictions) { return getAllRestrictions().stream().anyMatch(restrictions::contains); diff --git a/dhis-2/dhis-api/src/test/java/org/hisp/dhis/common/HashUtilsTest.java b/dhis-2/dhis-api/src/test/java/org/hisp/dhis/common/HashUtilsTest.java index f9e8778914da..b1be79ba9ceb 100644 --- a/dhis-2/dhis-api/src/test/java/org/hisp/dhis/common/HashUtilsTest.java +++ b/dhis-2/dhis-api/src/test/java/org/hisp/dhis/common/HashUtilsTest.java @@ -37,6 +37,21 @@ * @author Morten Svanæs */ class HashUtilsTest { + + @Test + void testMd5Hex() { + String value = "10-05-2022T12:55:45"; + assertEquals(32, HashUtils.hashMD5(value.getBytes()).length()); + assertEquals("c149820871470e3ab15eb24d42b3561a", HashUtils.hashMD5(value.getBytes())); + } + + @Test + void testSha1Hex() { + String value = "/api/me"; + assertEquals(40, HashUtils.hashSHA1(value.getBytes()).length()); + assertEquals("4f8cc3f306852ecb642ba4375453be1a4b860e71", HashUtils.hashSHA1(value.getBytes())); + } + @Test void testIsValidSHA256HexFormat() { String validSHA256Hex = "635e253fddd8466788d9580983bda99c258e9dd8c5a60a032623fde6c3a2789d"; diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/DefaultMaintenanceService.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/DefaultMaintenanceService.java index 6f7df34de1d6..656854538283 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/DefaultMaintenanceService.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/DefaultMaintenanceService.java @@ -78,7 +78,7 @@ public class DefaultMaintenanceService implements MaintenanceService { private final ApplicationEventPublisher eventPublisher; - private final EventChangeLogService trackedEntityDataValueChangelogService; + private final EventChangeLogService eventChangeLogService; // ------------------------------------------------------------------------- // MaintenanceService implementation @@ -169,7 +169,8 @@ public boolean pruneData(DataElement dataElement) { return false; } - trackedEntityDataValueChangelogService.deleteTrackedEntityDataValueChangeLog(dataElement); + eventChangeLogService.deleteTrackedEntityDataValueChangeLog(dataElement); + eventChangeLogService.deleteEventChangeLog(dataElement); dataValueAuditService.deleteDataValueAudits(dataElement); dataValueService.deleteDataValues(dataElement); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/jdbc/JdbcMaintenanceStore.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/jdbc/JdbcMaintenanceStore.java index c778900de40a..7809f5bd4c64 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/jdbc/JdbcMaintenanceStore.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/maintenance/jdbc/JdbcMaintenanceStore.java @@ -122,6 +122,7 @@ public int deleteSoftDeletedEvents() { // delete other objects related to events "delete from relationshipitem where eventid in " + eventSelect, "delete from trackedentitydatavalueaudit where eventid in " + eventSelect, + "delete from eventchangelog where eventid in " + eventSelect, "delete from programmessage where eventid in " + eventSelect, // finally delete the events "delete from event where deleted is true" @@ -202,6 +203,7 @@ public int deleteSoftDeletedEnrollments() { // delete other entries linked to events "delete from relationshipitem where eventid in " + eventSelect, "delete from trackedentitydatavalueaudit where eventid in " + eventSelect, + "delete from eventchangelog where eventid in " + eventSelect, "delete from programmessage where eventid in " + eventSelect, // delete other entries linked to enrollments "delete from relationshipitem where enrollmentid in " + enrollmentSelect, @@ -288,6 +290,7 @@ public int deleteSoftDeletedTrackedEntities() { "delete from note where noteid not in (select noteid from event_notes union all select noteid from enrollment_notes)", // delete other objects related to obsolete events "delete from trackedentitydatavalueaudit where eventid in " + eventSelect, + "delete from eventchangelog where eventid in " + eventSelect, // delete other objects related to obsolete enrollments "delete from programmessage where enrollmentid in " + enrollmentSelect, "delete from event where enrollmentid in " + enrollmentSelect, diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/DefaultMergeValidator.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/DefaultMergeValidator.java index c6edd635a36a..f170b39348e2 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/DefaultMergeValidator.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/DefaultMergeValidator.java @@ -27,10 +27,13 @@ */ package org.hisp.dhis.merge; +import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; +import javax.annotation.Nonnull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.UID; @@ -48,46 +51,57 @@ * * @author david mackessy */ +@Slf4j @Component @RequiredArgsConstructor public class DefaultMergeValidator implements MergeValidator { private final IdentifiableObjectManager manager; - private static final String INDICATOR_TYPE = "IndicatorType"; - private static final String INDICATOR = "Indicator"; - private static final String DATA_ELEMENT = "DataElement"; - private static final String CATEGORY_OPTION = "CategoryOption"; - private static final String MERGE_ERROR = "Unexpected value retrieving merge error code: "; @Override - public void verifySources( - Set paramSources, Set verifiedSources, MergeReport mergeReport, Class clazz) { + public MergeRequest validateUIDs( + @Nonnull MergeParams params, @Nonnull MergeReport mergeReport, @Nonnull MergeType mergeType) { + log.info("Validating {} merge request", mergeType); + mergeReport.setMergeType(mergeType); + + Set verifiedSources = verifySources(params.getSources(), mergeReport, mergeType); + + checkIsTargetInSources(verifiedSources, params.getTarget(), mergeReport, mergeType); + + return verifyTarget(mergeReport, verifiedSources, params, mergeType); + } + + @Override + public Set verifySources( + Set paramSources, MergeReport mergeReport, MergeType mergeType) { + Set verifiedSources = new HashSet<>(); Optional.ofNullable(paramSources) .filter(CollectionUtils::isNotEmpty) .ifPresentOrElse( - ids -> getSourcesAndVerify(ids, mergeReport, verifiedSources, clazz), + ids -> getSourcesAndVerify(ids, mergeReport, verifiedSources, mergeType), () -> mergeReport.addErrorMessage( - new ErrorMessage(missingSourceErrorCode(clazz.getSimpleName())))); + new ErrorMessage(ErrorCode.E1530, mergeType.getName()))); + return verifiedSources; } @Override - public void checkIsTargetInSources( - Set sources, UID target, MergeReport mergeReport, Class clazz) { + public void checkIsTargetInSources( + Set sources, UID target, MergeReport mergeReport, MergeType mergeType) { if (sources.contains(target)) mergeReport.addErrorMessage( - new ErrorMessage(targetNotSourceErrorCode(clazz.getSimpleName()))); + new ErrorMessage(ErrorCode.E1532, mergeType.getName(), mergeType.getName())); } @Override - public MergeRequest verifyTarget( - MergeReport mergeReport, Set sources, MergeParams params, Class clazz) { - return getAndVerify(params.getTarget(), mergeReport, MergeObjectType.TARGET.name(), clazz) + public MergeRequest verifyTarget( + MergeReport mergeReport, Set sources, MergeParams params, MergeType mergeType) { + return getAndVerify(params.getTarget(), mergeReport, MergeObjectType.TARGET, mergeType) .map( - t -> + uid -> MergeRequest.builder() .sources(sources) - .target(t) + .target(uid) .deleteSources(params.isDeleteSources()) .dataMergeStrategy(params.getDataMergeStrategy()) .build()) @@ -104,19 +118,18 @@ public MergeRequest verifyTarget( * @param uid to verify * @param mergeReport to update * @param mergeObjectType indicating whether this is a source or target - * @param clazz {@link IdentifiableObject} type + * @param mergeType {@link MergeType} * @return optional UID */ - private Optional getAndVerify( - UID uid, MergeReport mergeReport, String mergeObjectType, Class clazz) { + private Optional getAndVerify( + UID uid, MergeReport mergeReport, MergeObjectType mergeObjectType, MergeType mergeType) { if (uid != null) { - return Optional.ofNullable(manager.get(clazz, uid.getValue())) + return Optional.ofNullable(manager.get(mergeType.getClazz(), uid.getValue())) .map(i -> UID.of(i.getUid())) - .or(reportError(uid, mergeReport, mergeObjectType, clazz)); + .or(reportError(uid, mergeReport, mergeObjectType, mergeType)); } else { - mergeReport.addErrorMessage( - new ErrorMessage(noTargetErrorCode(clazz.getSimpleName()), mergeObjectType, uid)); + mergeReport.addErrorMessage(new ErrorMessage(ErrorCode.E1531, mergeType.getName())); return Optional.empty(); } } @@ -129,71 +142,25 @@ private Optional getAndVerify( * @param uids to verify * @param report to update with any error * @param verifiedSources to update with verified uids - * @param clazz {@link IdentifiableObject} type + * @param mergeType {@link MergeType} */ - private void getSourcesAndVerify( - Set uids, MergeReport report, Set verifiedSources, Class clazz) { + private void getSourcesAndVerify( + Set uids, MergeReport report, Set verifiedSources, MergeType mergeType) { uids.forEach( uid -> - getAndVerify(uid, report, MergeObjectType.SOURCE.name(), clazz) + getAndVerify(uid, report, MergeObjectType.SOURCE, mergeType) .ifPresent(verifiedSources::add)); } - private Supplier> reportError( - UID uid, MergeReport mergeReport, String mergeObjectType, Class clazz) { + private Supplier> reportError( + UID uid, MergeReport mergeReport, MergeObjectType mergeObjectType, MergeType mergeType) { return () -> { mergeReport.addErrorMessage( - new ErrorMessage(doesNotExistErrorCode(clazz.getSimpleName()), mergeObjectType, uid)); + new ErrorMessage(ErrorCode.E1533, mergeObjectType.toString(), mergeType.getName(), uid)); return Optional.empty(); }; } - /** - * Methods to get the appropriate error code based on the Object type - * - * @param clazz class name - * @return error code - */ - private ErrorCode missingSourceErrorCode(String clazz) { - return switch (clazz) { - case INDICATOR_TYPE -> ErrorCode.E1530; - case INDICATOR -> ErrorCode.E1540; - case DATA_ELEMENT -> ErrorCode.E1550; - case CATEGORY_OPTION -> ErrorCode.E1650; - default -> throw new IllegalStateException(MERGE_ERROR + clazz); - }; - } - - private ErrorCode noTargetErrorCode(String clazz) { - return switch (clazz) { - case INDICATOR_TYPE -> ErrorCode.E1531; - case INDICATOR -> ErrorCode.E1541; - case DATA_ELEMENT -> ErrorCode.E1551; - case CATEGORY_OPTION -> ErrorCode.E1651; - default -> throw new IllegalStateException(MERGE_ERROR + clazz); - }; - } - - private ErrorCode targetNotSourceErrorCode(String clazz) { - return switch (clazz) { - case INDICATOR_TYPE -> ErrorCode.E1532; - case INDICATOR -> ErrorCode.E1542; - case DATA_ELEMENT -> ErrorCode.E1552; - case CATEGORY_OPTION -> ErrorCode.E1652; - default -> throw new IllegalStateException(MERGE_ERROR + clazz); - }; - } - - private ErrorCode doesNotExistErrorCode(String clazz) { - return switch (clazz) { - case INDICATOR_TYPE -> ErrorCode.E1533; - case INDICATOR -> ErrorCode.E1543; - case DATA_ELEMENT -> ErrorCode.E1553; - case CATEGORY_OPTION -> ErrorCode.E1653; - default -> throw new IllegalStateException(MERGE_ERROR + clazz); - }; - } - enum MergeObjectType { SOURCE, TARGET diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeHandler.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/option/CategoryOptionMergeHandler.java similarity index 99% rename from dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeHandler.java rename to dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/option/CategoryOptionMergeHandler.java index dbb3ff48da00..f62280f70d18 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeHandler.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/option/CategoryOptionMergeHandler.java @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.merge.category; +package org.hisp.dhis.merge.category.option; import java.util.List; import lombok.RequiredArgsConstructor; diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeService.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/option/CategoryOptionMergeService.java similarity index 88% rename from dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeService.java rename to dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/option/CategoryOptionMergeService.java index 78c8ce9612b3..3e2ccf59c655 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeService.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/option/CategoryOptionMergeService.java @@ -25,12 +25,10 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.merge.category; +package org.hisp.dhis.merge.category.option; import jakarta.persistence.EntityManager; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; @@ -43,9 +41,9 @@ import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeRequest; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.merge.MergeType; import org.hisp.dhis.merge.MergeValidator; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** * Main class for a {@link CategoryOption} merge. @@ -65,21 +63,10 @@ public class CategoryOptionMergeService implements MergeService { @Override public MergeRequest validate(@Nonnull MergeParams params, @Nonnull MergeReport mergeReport) { - log.info("Validating CategoryOption merge request"); - - // sources - Set sources = new HashSet<>(); - validator.verifySources(params.getSources(), sources, mergeReport, CategoryOption.class); - - // target - validator.checkIsTargetInSources( - sources, params.getTarget(), mergeReport, CategoryOption.class); - - return validator.verifyTarget(mergeReport, sources, params, CategoryOption.class); + return validator.validateUIDs(params, mergeReport, MergeType.CATEGORY_OPTION); } @Override - @Transactional public MergeReport merge(@Nonnull MergeRequest request, @Nonnull MergeReport mergeReport) { log.info("Performing CategoryOption merge"); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeProcessor.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeProcessor.java deleted file mode 100644 index 57ca57b21ccf..000000000000 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeProcessor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2004-2024, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.merge.dataelement; - -import lombok.RequiredArgsConstructor; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeService; -import org.springframework.stereotype.Component; - -/** - * Implementation of {@link MergeProcessor} that currently only uses its default method. - * - * @author david mackessy - */ -@Component -@RequiredArgsConstructor -public class DataElementMergeProcessor implements MergeProcessor { - - /** - * Spring injects the correct service based on the variable name (when there are multiple - * implementations to choose from). So The {@link DataElementMergeService} gets injected here. - */ - private final MergeService dataElementMergeService; - - @Override - public MergeService getMergeService() { - return dataElementMergeService; - } -} diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeService.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeService.java index 38dac3ea0443..1e6b29f801d9 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeService.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeService.java @@ -29,9 +29,7 @@ import com.google.common.collect.ImmutableList; import jakarta.persistence.EntityManager; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; @@ -47,13 +45,13 @@ import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeRequest; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.merge.MergeType; import org.hisp.dhis.merge.MergeValidator; import org.hisp.dhis.merge.dataelement.handler.AnalyticalDataElementMergeHandler; import org.hisp.dhis.merge.dataelement.handler.DataDataElementMergeHandler; import org.hisp.dhis.merge.dataelement.handler.MetadataDataElementMergeHandler; import org.hisp.dhis.merge.dataelement.handler.TrackerDataElementMergeHandler; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** * Main class for a {@link org.hisp.dhis.dataelement.DataElement} merge. @@ -81,23 +79,15 @@ public class DataElementMergeService implements MergeService { @Override public MergeRequest validate(@Nonnull MergeParams params, @Nonnull MergeReport mergeReport) { - log.info("Validating DataElement merge request"); + MergeRequest request = validator.validateUIDs(params, mergeReport, MergeType.DATA_ELEMENT); + if (mergeReport.hasErrorMessages()) return request; + + // data element-specific validation if (params.getDataMergeStrategy() == null) { - mergeReport.addErrorMessage(new ErrorMessage(ErrorCode.E1556)); - return null; + mergeReport.addErrorMessage(new ErrorMessage(ErrorCode.E1534)); + return request; } - // sources - Set sources = new HashSet<>(); - validator.verifySources(params.getSources(), sources, mergeReport, DataElement.class); - - // target - validator.checkIsTargetInSources(sources, params.getTarget(), mergeReport, DataElement.class); - MergeRequest request = validator.verifyTarget(mergeReport, sources, params, DataElement.class); - - if (mergeReport.hasErrorMessages()) return request; - - // get DEs for further type-specific validation DataElement deTarget = dataElementService.getDataElement(request.getTarget().getValue()); List deSources = dataElementService.getDataElementsByUid( @@ -113,7 +103,6 @@ public MergeRequest validate(@Nonnull MergeParams params, @Nonnull MergeReport m } @Override - @Transactional public MergeReport merge(@Nonnull MergeRequest request, @Nonnull MergeReport mergeReport) { log.info("Performing DataElement merge"); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidator.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidator.java index e8b7ca064852..d48f0e56ab62 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidator.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidator.java @@ -27,8 +27,8 @@ */ package org.hisp.dhis.merge.dataelement; -import static org.hisp.dhis.feedback.ErrorCode.E1554; -import static org.hisp.dhis.feedback.ErrorCode.E1555; +import static org.hisp.dhis.feedback.ErrorCode.E1550; +import static org.hisp.dhis.feedback.ErrorCode.E1551; import java.util.List; import javax.annotation.Nonnull; @@ -56,7 +56,7 @@ public MergeReport validateValueType( if (!mismatches.isEmpty()) { report.addErrorMessage( new ErrorMessage( - E1554, + E1550, target.getValueType(), mismatches.stream().map(DataElement::getValueType).distinct().toList())); } @@ -75,7 +75,7 @@ public MergeReport validateDomainType( if (!mismatches.isEmpty()) { report.addErrorMessage( new ErrorMessage( - E1555, + E1551, target.getDomainType(), mismatches.stream().map(DataElement::getDomainType).distinct().toList())); } diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/handler/TrackerDataElementMergeHandler.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/handler/TrackerDataElementMergeHandler.java index 07c40b881101..18561899de4d 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/handler/TrackerDataElementMergeHandler.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/dataelement/handler/TrackerDataElementMergeHandler.java @@ -55,6 +55,7 @@ import org.hisp.dhis.programrule.ProgramRuleActionStore; import org.hisp.dhis.programrule.ProgramRuleVariable; import org.hisp.dhis.programrule.ProgramRuleVariableStore; +import org.hisp.dhis.tracker.export.event.EventChangeLog; import org.hisp.dhis.tracker.export.event.EventChangeLogService; import org.hisp.dhis.tracker.export.event.TrackedEntityDataValueChangeLog; import org.springframework.stereotype.Component; @@ -76,7 +77,7 @@ public class TrackerDataElementMergeHandler { private final ProgramRuleActionStore programRuleActionStore; private final ProgramIndicatorStore programIndicatorStore; private final EventStore eventStore; - private final EventChangeLogService teDataValueChangeLogService; + private final EventChangeLogService eventChangeLogService; /** * Method retrieving {@link ProgramIndicator}s which have a source {@link DataElement} reference @@ -274,9 +275,9 @@ private void setLastUpdatedAsTargetAndRemoveRemaining( } /** - * Method handling {@link TrackedEntityDataValueChangeLog}s. All {@link - * TrackedEntityDataValueChangeLog}s will either be deleted or left as is, based on whether the - * source {@link DataElement}s are being deleted or not. + * Method handling {@link TrackedEntityDataValueChangeLog}s and {@link EventChangeLog}s. Both of + * them will either be deleted or left as is, based on whether the source {@link DataElement}s are + * being deleted or not. * * @param sources source {@link DataElement}s used to retrieve {@link DataValueAudit}s * @param mergeRequest merge request @@ -286,7 +287,8 @@ public void handleTrackedEntityDataValueChangelog( if (mergeRequest.isDeleteSources()) { log.info( "Deleting source tracked entity data value change log records as source DataElements are being deleted"); - sources.forEach(teDataValueChangeLogService::deleteTrackedEntityDataValueChangeLog); + sources.forEach(eventChangeLogService::deleteTrackedEntityDataValueChangeLog); + sources.forEach(eventChangeLogService::deleteEventChangeLog); } else { log.info( "Leaving source tracked entity data value change log records as is, source DataElements are not being deleted"); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeService.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeService.java index d49ea52abfd6..c46b4eb840e2 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeService.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeService.java @@ -28,12 +28,11 @@ package org.hisp.dhis.merge.indicator; import com.google.common.collect.ImmutableList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.hisp.dhis.common.UID; import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.indicator.Indicator; @@ -43,16 +42,17 @@ import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeRequest; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.merge.MergeType; import org.hisp.dhis.merge.MergeValidator; import org.hisp.dhis.merge.indicator.handler.IndicatorMergeHandler; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** * Main class for indicator merge. * * @author david mackessy */ +@Slf4j @Service @RequiredArgsConstructor public class IndicatorMergeService implements MergeService { @@ -66,18 +66,10 @@ public class IndicatorMergeService implements MergeService { @Override public MergeRequest validate(@Nonnull MergeParams params, @Nonnull MergeReport mergeReport) { - // sources - Set sources = new HashSet<>(); - validator.verifySources(params.getSources(), sources, mergeReport, Indicator.class); - - // target - validator.checkIsTargetInSources(sources, params.getTarget(), mergeReport, Indicator.class); - - return validator.verifyTarget(mergeReport, sources, params, Indicator.class); + return validator.validateUIDs(params, mergeReport, MergeType.INDICATOR); } @Override - @Transactional public MergeReport merge(@Nonnull MergeRequest request, @Nonnull MergeReport mergeReport) { List sources = indicatorService.getIndicatorsByUid(UID.toValueList(request.getSources())); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeProcessor.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeProcessor.java deleted file mode 100644 index 3a540e3b0031..000000000000 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeProcessor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2004-2023, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.merge.indicator; - -import lombok.RequiredArgsConstructor; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeService; -import org.springframework.stereotype.Component; - -/** - * Implementation of {@link MergeProcessor} that currently only uses its default method. - * - * @author david mackessy - */ -@Component -@RequiredArgsConstructor -public class IndicatorTypeMergeProcessor implements MergeProcessor { - - /** - * Spring injects the correct service based on the variable name (when there are multiple - * implementations to choose from). So The {@link IndicatorTypeMergeService} gets injected here. - */ - private final MergeService indicatorTypeMergeService; - - @Override - public MergeService getMergeService() { - return indicatorTypeMergeService; - } -} diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeService.java b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeService.java index 52e850994fb6..af1e94c5c0be 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeService.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeService.java @@ -27,11 +27,10 @@ */ package org.hisp.dhis.merge.indicator; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.annotation.Nonnull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.UID; import org.hisp.dhis.feedback.MergeReport; @@ -41,15 +40,16 @@ import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeRequest; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.merge.MergeType; import org.hisp.dhis.merge.MergeValidator; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** * Main class for indicator type merge. * * @author david mackessy */ +@Slf4j @Service @RequiredArgsConstructor public class IndicatorTypeMergeService implements MergeService { @@ -60,18 +60,10 @@ public class IndicatorTypeMergeService implements MergeService { @Override public MergeRequest validate(@Nonnull MergeParams params, @Nonnull MergeReport mergeReport) { - // sources - Set sources = new HashSet<>(); - validator.verifySources(params.getSources(), sources, mergeReport, IndicatorType.class); - - // target - validator.checkIsTargetInSources(sources, params.getTarget(), mergeReport, IndicatorType.class); - - return validator.verifyTarget(mergeReport, sources, params, IndicatorType.class); + return validator.validateUIDs(params, mergeReport, MergeType.INDICATOR_TYPE); } @Override - @Transactional public MergeReport merge(@Nonnull MergeRequest request, @Nonnull MergeReport mergeReport) { List sources = indicatorService.getIndicatorTypesByUid(UID.toValueList(request.getSources())); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidatorTest.java b/dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidatorTest.java index 50d06ab94408..7610c9c7280d 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidatorTest.java +++ b/dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeValidatorTest.java @@ -38,7 +38,6 @@ import org.hisp.dhis.dataelement.DataElementDomain; import org.hisp.dhis.feedback.ErrorCode; import org.hisp.dhis.feedback.MergeReport; -import org.hisp.dhis.merge.MergeType; import org.hisp.dhis.test.TestBase; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -58,8 +57,7 @@ void whenAllValueTypesMatchThenNoError() { // when DataElementMergeValidator validator = new DataElementMergeValidator(); MergeReport report = - validator.validateValueType( - target, List.of(source1, source2, source3), new MergeReport(MergeType.DATA_ELEMENT)); + validator.validateValueType(target, List.of(source1, source2, source3), new MergeReport()); // then assertFalse(report.hasErrorMessages()); @@ -78,12 +76,11 @@ void when1ValueTypeDoesNotMatchThenError() { // when DataElementMergeValidator validator = new DataElementMergeValidator(); MergeReport report = - validator.validateValueType( - target, List.of(source1, source2, source3), new MergeReport(MergeType.DATA_ELEMENT)); + validator.validateValueType(target, List.of(source1, source2, source3), new MergeReport()); // then assertTrue(report.hasErrorMessages()); - assertEquals(ErrorCode.E1554, report.getMergeErrors().get(0).getErrorCode()); + assertEquals(ErrorCode.E1550, report.getMergeErrors().get(0).getErrorCode()); assertEquals( "All source ValueTypes must match target ValueType: `TEXT`. Other ValueTypes found: `[NUMBER]`", report.getMergeErrors().get(0).getMessage()); @@ -102,12 +99,11 @@ void whenMultipleValueTypeDoesNotMatchThenError() { // when DataElementMergeValidator validator = new DataElementMergeValidator(); MergeReport report = - validator.validateValueType( - target, List.of(source1, source2, source3), new MergeReport(MergeType.DATA_ELEMENT)); + validator.validateValueType(target, List.of(source1, source2, source3), new MergeReport()); // then assertTrue(report.hasErrorMessages()); - assertEquals(ErrorCode.E1554, report.getMergeErrors().get(0).getErrorCode()); + assertEquals(ErrorCode.E1550, report.getMergeErrors().get(0).getErrorCode()); assertEquals( "All source ValueTypes must match target ValueType: `TEXT`. Other ValueTypes found: `[NUMBER, DATE]`", report.getMergeErrors().get(0).getMessage()); @@ -130,8 +126,7 @@ void whenAllDomainTypesMatchThenNoError() { // when DataElementMergeValidator validator = new DataElementMergeValidator(); MergeReport report = - validator.validateDomainType( - target, List.of(source1, source2, source3), new MergeReport(MergeType.DATA_ELEMENT)); + validator.validateDomainType(target, List.of(source1, source2, source3), new MergeReport()); // then assertFalse(report.hasErrorMessages()); @@ -154,12 +149,11 @@ void whenDomainTypeDoNotMatchThenError() { // when DataElementMergeValidator validator = new DataElementMergeValidator(); MergeReport report = - validator.validateDomainType( - target, List.of(source1, source2, source3), new MergeReport(MergeType.DATA_ELEMENT)); + validator.validateDomainType(target, List.of(source1, source2, source3), new MergeReport()); // then assertTrue(report.hasErrorMessages()); - assertEquals(ErrorCode.E1555, report.getMergeErrors().get(0).getErrorCode()); + assertEquals(ErrorCode.E1551, report.getMergeErrors().get(0).getErrorCode()); assertEquals( "All source DataElementDomains must match target DataElementDomain: `AGGREGATE`. Other DataElementDomains found: `[TRACKER]`", report.getMergeErrors().get(0).getMessage()); diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableUpdateParams.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableUpdateParams.java index a971733dee97..8790cc8e6740 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableUpdateParams.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableUpdateParams.java @@ -166,6 +166,15 @@ public Date getFromDate() { return earliest; } + /** + * Indicates whether a from date exists. + * + * @return true if a from date exists. + */ + public boolean hasFromDate() { + return getFromDate() != null; + } + public AnalyticsTableUpdateParams withLatestPartition() { return this.toBuilder().lastYears(AnalyticsTablePartition.LATEST_PARTITION).build(); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/MeasureFilter.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/MeasureFilter.java index fa0a5cf738b5..f23978eda067 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/MeasureFilter.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/MeasureFilter.java @@ -47,19 +47,12 @@ public enum MeasureFilter { * @return true if the constraint/filter is valid when x is compared with y. */ public boolean measureIsValid(Double x, Double y) { - switch (this) { - case EQ: - return Double.compare(x, y) == 0; - case GT: - return Double.compare(x, y) > 0; - case GE: - return Double.compare(x, y) >= 0; - case LT: - return Double.compare(x, y) < 0; - case LE: - return Double.compare(x, y) <= 0; - default: - return false; - } + return switch (this) { + case EQ -> Double.compare(x, y) == 0; + case GT -> Double.compare(x, y) > 0; + case GE -> Double.compare(x, y) >= 0; + case LT -> Double.compare(x, y) < 0; + case LE -> Double.compare(x, y) <= 0; + }; } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/ValueTypeMapping.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/ValueTypeMapping.java index bac5c446b1f2..3710ae0736cf 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/ValueTypeMapping.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/ValueTypeMapping.java @@ -71,7 +71,7 @@ public enum ValueTypeMapping { @Getter private final UnaryOperator argumentTransformer; @Getter private final String postgresCast; - ValueTypeMapping(Function converter, Class... classes) { + ValueTypeMapping(Function converter, Class... classes) { this.converter = converter; this.valueTypes = fromClasses(classes); this.selectTransformer = UnaryOperator.identity(); @@ -86,7 +86,7 @@ public enum ValueTypeMapping { * @param classes the classes to be converted * @return the respective {@link ValueType} array */ - private ValueType[] fromClasses(Class... classes) { + private ValueType[] fromClasses(Class... classes) { return stream(ValueType.values()) .filter(valueType -> isAssignableFrom(classes, valueType)) .toArray(ValueType[]::new); @@ -99,14 +99,14 @@ private ValueType[] fromClasses(Class... classes) { * @param valueType the {@link ValueType} to be checked * @return true if the {@link ValueType} is assignable from the given classes, false otherwise */ - private static boolean isAssignableFrom(Class[] classes, ValueType valueType) { + private static boolean isAssignableFrom(Class[] classes, ValueType valueType) { return stream(classes).anyMatch(valueType.getJavaClass()::isAssignableFrom); } ValueTypeMapping( Function converter, UnaryOperator selectTransformer, - Class... classes) { + Class... classes) { this.converter = converter; this.valueTypes = fromClasses(classes); this.selectTransformer = s -> Objects.isNull(s) ? null : selectTransformer.apply(s); diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java index 3e3829dcc77f..324277501057 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java @@ -59,6 +59,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -95,7 +96,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import org.springframework.util.Assert; @@ -191,7 +191,7 @@ public Future> getAggregatedDataValues( if (params.analyzeOnly()) { withExceptionHandling( () -> executionPlanStore.addExecutionPlan(immutableParams.getExplainOrderId(), sql)); - return new AsyncResult<>(Maps.newHashMap()); + return CompletableFuture.completedFuture(Maps.newHashMap()); } Map map; @@ -205,12 +205,12 @@ public Future> getAggregatedDataValues( throw ex; } log.warn(ERR_MSG_SILENT_FALLBACK, ex); - return new AsyncResult<>(Maps.newHashMap()); + return CompletableFuture.completedFuture(Maps.newHashMap()); } replaceDataPeriodsWithAggregationPeriods(map, params, dataPeriodAggregationPeriodMap); - return new AsyncResult<>(map); + return CompletableFuture.completedFuture(map); } catch (DataAccessResourceFailureException ex) { throw new QueryRuntimeException(ErrorCode.E7131); } catch (RuntimeException ex) { @@ -955,9 +955,7 @@ private Map getKeyValueMap(DataQueryParams params, String sql, i for (DimensionalObject dim : params.getDimensions()) { String value = dim.isFixed() ? dim.getDimensionName() : rowSet.getString(dim.getDimensionName()); - String queryModsId = params.getQueryModsId(dim); - key.append(value).append(queryModsId).append(DIMENSION_SEP); } @@ -965,12 +963,10 @@ private Map getKeyValueMap(DataQueryParams params, String sql, i if (params.isDataType(TEXT)) { String value = rowSet.getString(VALUE_ID); - map.put(key.toString(), value); } else // NUMERIC { Double value = rowSet.getDouble(VALUE_ID); - map.put(key.toString(), value); } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java index 42f345e3b7b9..b08af886985a 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java @@ -94,7 +94,6 @@ import org.hisp.dhis.setting.UserSettings; import org.hisp.dhis.trackedentity.TrackedEntityAttribute; import org.hisp.dhis.trackedentity.TrackedEntityAttributeService; -import org.hisp.dhis.user.UserSettingsService; import org.springframework.stereotype.Service; import org.springframework.util.Assert; @@ -119,8 +118,6 @@ public class DefaultEventDataQueryService implements EventDataQueryService { private final DataQueryService dataQueryService; - private final UserSettingsService userSettingsService; - @Override public EventQueryParams getFromRequest(EventDataQueryRequest request) { return getFromRequest(request, false); diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/programindicator/RelationshipTypeJoinGenerator.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/programindicator/RelationshipTypeJoinGenerator.java index 5fd86f689d79..6c12a6b35274 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/programindicator/RelationshipTypeJoinGenerator.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/programindicator/RelationshipTypeJoinGenerator.java @@ -80,31 +80,27 @@ public static String generate( private static String getToJoin(RelationshipEntity relationshipEntity) { String sql = "LEFT JOIN "; - switch (relationshipEntity) { - case TRACKED_ENTITY_INSTANCE: - return sql + "trackedentity te on te.trackedentityid = ri2.trackedentityid"; - case PROGRAM_STAGE_INSTANCE: - return sql + "event ev on ev.eventid = ri2.eventid"; - case PROGRAM_INSTANCE: - return sql + "enrollment en on en.enrollmentid = ri2.enrollmentid"; - default: - throw new IllegalQueryException( - new ErrorMessage(ErrorCode.E7227, relationshipEntity.name())); - } + return switch (relationshipEntity) { + case TRACKED_ENTITY_INSTANCE -> + sql + "trackedentity te on te.trackedentityid = ri2.trackedentityid"; + case PROGRAM_STAGE_INSTANCE -> sql + "event ev on ev.eventid = ri2.eventid"; + case PROGRAM_INSTANCE -> sql + "enrollment en on en.enrollmentid = ri2.enrollmentid"; + default -> + throw new IllegalQueryException( + new ErrorMessage(ErrorCode.E7227, relationshipEntity.name())); + }; } private static String getFromRelationshipEntity( String alias, RelationshipEntity relationshipEntity, AnalyticsType programIndicatorType) { - switch (relationshipEntity) { - case TRACKED_ENTITY_INSTANCE: - return getTei(alias); - case PROGRAM_STAGE_INSTANCE: - case PROGRAM_INSTANCE: - return (programIndicatorType.equals(AnalyticsType.EVENT) - ? getEvent(alias) - : getEnrollment(alias)); - } - throw new IllegalQueryException(new ErrorMessage(ErrorCode.E7227, relationshipEntity.name())); + return switch (relationshipEntity) { + case TRACKED_ENTITY_INSTANCE -> getTei(alias); + case PROGRAM_STAGE_INSTANCE, PROGRAM_INSTANCE -> + programIndicatorType.equals(AnalyticsType.EVENT) ? getEvent(alias) : getEnrollment(alias); + default -> + throw new IllegalQueryException( + new ErrorMessage(ErrorCode.E7227, relationshipEntity.name())); + }; } private static String getTei(String alias) { diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java index 5a5d59f0fe9b..c88fcd248943 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java @@ -32,7 +32,9 @@ import static org.hisp.dhis.analytics.table.util.PartitionUtils.getStartDate; import static org.hisp.dhis.commons.util.TextUtils.format; import static org.hisp.dhis.db.model.DataType.CHARACTER_11; +import static org.hisp.dhis.db.model.DataType.INTEGER; import static org.hisp.dhis.db.model.DataType.TEXT; +import static org.hisp.dhis.db.model.constraint.Nullable.NOT_NULL; import static org.hisp.dhis.util.DateUtils.toLongDate; import java.util.Collection; @@ -732,6 +734,24 @@ protected String replaceQualify(String template, Map variables) return TextUtils.replace(template, map); } + protected AnalyticsTableColumn getPartitionColumn() { + return AnalyticsTableColumn.builder() + .name("year") + .dataType(INTEGER) + .nullable(NOT_NULL) + // The expression should use sqlBuilder, but the concept of functions (like YEAR) + // is part of the previous PR (https://github.com/dhis2/dhis2-core/pull/19131/files) + .selectExpression( + """ + CASE + WHEN ev.status = 'SCHEDULE' THEN YEAR(ev.scheduleddate) + ELSE YEAR(ev.occurreddate) + END + """) + .skipIndex(Skip.SKIP) + .build(); + } + // ------------------------------------------------------------------------- // Private supportive methods // ------------------------------------------------------------------------- diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java index 55347a9264bd..f2d9807084c7 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java @@ -108,9 +108,7 @@ public void create(AnalyticsTableUpdateParams params, JobProgress progress) { List tables = tableManager.getAnalyticsTables(params); if (tables.isEmpty()) { - clock.logTime( - "Table update aborted, no table or partitions to be updated: '{}'", - tableType.getTableName()); + clock.logTime("Table update aborted, nothing to update: '{}'", tableType.getTableName()); progress.startingStage("Table updates " + tableType); progress.completedStage("Table updated aborted, no table or partitions to be updated"); return; @@ -369,11 +367,17 @@ List getTablePartitions(List tables) { int getParallelJobs() { SystemSettings settings = settingsProvider.getCurrentSettings(); int parallelJobs = settings.getParallelJobsInAnalyticsTableExport(); - if (parallelJobs > 0) return parallelJobs; + if (parallelJobs > 0) { + return parallelJobs; + } int databaseCpus = settings.getDatabaseServerCpus(); - if (databaseCpus > 0) return databaseCpus; + if (databaseCpus > 0) { + return databaseCpus; + } int serverCpus = SystemUtils.getCpuCores(); - if (serverCpus > 2) return serverCpus - 1; + if (serverCpus > 2) { + return serverCpus - 1; + } return serverCpus; } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java index 0149cc12dbd1..b764877b0a0d 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java @@ -245,13 +245,13 @@ public void removeUpdatedData(List tables) { """ delete from ${tableName} ax \ where ax.id in ( \ - select concat(de.uid,'-',ps.iso,'-',ou.uid,'-',co.uid,'-',ao.uid) as id \ + select concat(des.dataelementuid,'-',ps.iso,'-',ous.organisationunituid,'-',dcs.categoryoptioncombouid,'-',acs.categoryoptioncombouid) as id \ from ${datavalue} dv \ - inner join ${dataelement} de on dv.dataelementid=de.dataelementid \ + inner join analytics_rs_dataelementstructure des on dv.dataelementid=des.dataelementid \ inner join analytics_rs_periodstructure ps on dv.periodid=ps.periodid \ - inner join ${organisationunit} ou on dv.sourceid=ou.organisationunitid \ - inner join ${categoryoptioncombo} co on dv.categoryoptioncomboid=co.categoryoptioncomboid \ - inner join ${categoryoptioncombo} ao on dv.attributeoptioncomboid=ao.categoryoptioncomboid \ + inner join analytics_rs_orgunitstructure ous on dv.sourceid=ous.organisationunitid \ + inner join analytics_rs_categorystructure dcs on dv.categoryoptioncomboid=dcs.categoryoptioncomboid \ + inner join analytics_rs_categorystructure acs on dv.attributeoptioncomboid=acs.categoryoptioncomboid \ where dv.lastupdated >= '${startDate}'and dv.lastupdated < '${endDate}');""", Map.of( "tableName", qualify(getAnalyticsTableType().getTableName()), @@ -666,17 +666,17 @@ private List getDataYears(AnalyticsTableUpdateParams params) { new StringBuilder( replaceQualify( """ - select distinct(extract(year from pe.startdate)) \ + select distinct(extract(year from pes.startdate)) \ from ${datavalue} dv \ - inner join ${period} pe on dv.periodid=pe.periodid \ - where pe.startdate is not null \ + inner join analytics_rs_periodstructure pes on dv.periodid=pes.periodid \ + where pes.startdate is not null \ and dv.lastupdated < '${startTime}'\s""", Map.of("startTime", toLongDate(params.getStartTime())))); - if (params.getFromDate() != null) { + if (params.hasFromDate()) { sql.append( replace( - "and pe.startdate >= '${fromDate}'", + "and pes.startdate >= '${fromDate}'", Map.of("fromDate", DateUtils.toMediumDate(params.getFromDate())))); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java index 368f567c633a..6751c6246f6b 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java @@ -298,17 +298,17 @@ private List getDataYears(AnalyticsTableUpdateParams params) { String sql = replaceQualify( """ - select distinct(extract(year from pe.startdate)) \ + select distinct(extract(year from ps.startdate)) \ from ${completedatasetregistration} cdr \ - inner join period pe on cdr.periodid=pe.periodid \ - where pe.startdate is not null \ + inner join analytics_rs_periodstructure ps on cdr.periodid=ps.periodid \ + where ps.startdate is not null \ and cdr.date < '${startTime}'""", Map.of("startTime", toLongDate(params.getStartTime()))); if (params.getFromDate() != null) { sql += replace( - "and pe.startdate >= '${fromDate}'", + "and ps.startdate >= '${fromDate}'", Map.of("fromDate", DateUtils.toLongDate(params.getFromDate()))); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java index f6361f8aba1c..fc303bd6f90c 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java @@ -376,6 +376,9 @@ private List getColumns( .toList()); columns.addAll(getOrganisationUnitGroupSetColumns()); + if (sqlBuilder.supportsDeclarativePartitioning()) { + columns.add(getPartitionColumn()); + } return columns; } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEnrollmentsAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEnrollmentsAnalyticsTableManager.java index 694c8e481bad..89502457e852 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEnrollmentsAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEnrollmentsAnalyticsTableManager.java @@ -246,7 +246,9 @@ private List getColumns() { List columns = new ArrayList<>(); columns.addAll(FIXED_COLS); columns.add(getOrganisationUnitNameHierarchyColumn()); - + if (sqlBuilder.supportsDeclarativePartitioning()) { + columns.add(getPartitionColumn()); + } return columns; } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEventsAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEventsAnalyticsTableManager.java index 549cd6d8fa4c..096e1f7e0a5e 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEventsAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityEventsAnalyticsTableManager.java @@ -370,6 +370,11 @@ private List getDataYears(AnalyticsTableUpdateParams params, TrackedEnt private List getColumns() { List columns = new ArrayList<>(); columns.addAll(FIXED_COLS); + + if (sqlBuilder.supportsDeclarativePartitioning()) { + columns.add(getPartitionColumn()); + } + columns.add(getOrganisationUnitNameHierarchyColumn()); return columns; diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcValidationResultTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcValidationResultTableManager.java index eeb0cf8f36b1..5c2f21565f86 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcValidationResultTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcValidationResultTableManager.java @@ -85,12 +85,12 @@ public class JdbcValidationResultTableManager extends AbstractJdbcTableManager { AnalyticsTableColumn.builder() .name("pestartdate") .dataType(TIMESTAMP) - .selectExpression("pe.startdate") + .selectExpression("ps.startdate") .build(), AnalyticsTableColumn.builder() .name("peenddate") .dataType(TIMESTAMP) - .selectExpression("pe.enddate") + .selectExpression("ps.enddate") .build(), AnalyticsTableColumn.builder() .name("year") @@ -189,7 +189,6 @@ public void populateTable(AnalyticsTableUpdateParams params, AnalyticsTableParti replaceQualify( """ from ${validationresult} vrs - inner join ${period} pe on vrs.periodid=pe.periodid inner join analytics_rs_periodstructure ps on vrs.periodid=ps.periodid inner join ${validationrule} vr on vr.validationruleid=vrs.validationruleid inner join analytics_rs_organisationunitgroupsetstructure ougs on vrs.organisationunitid=ougs.organisationunitid @@ -211,15 +210,15 @@ private List getDataYears(AnalyticsTableUpdateParams params) { params.getFromDate() == null ? "" : replace( - "and pe.startdate >= '${fromDate}'", + "and ps.startdate >= '${fromDate}'", Map.of("fromDate", DateUtils.toMediumDate(params.getFromDate()))); String sql = replaceQualify( """ - select distinct(extract(year from pe.startdate)) + select distinct(extract(year from ps.startdate)) from ${validationresult} vrs - inner join ${period} pe on vrs.periodid=pe.periodid - where pe.startdate is not null + inner join analytics_rs_periodstructure ps on vrs.periodid=ps.periodid + where ps.startdate is not null and vrs.created < '${startTime}' ${fromDateClause}""", Map.of( diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/init/AnalyticsDatabaseInit.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/init/AnalyticsDatabaseInit.java index 36e7ca4401de..b6d415081878 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/init/AnalyticsDatabaseInit.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/init/AnalyticsDatabaseInit.java @@ -76,13 +76,18 @@ public void init() { Database database = settings.getAnalyticsDatabase(); switch (database) { - case DORIS -> initDoris(); case POSTGRESQL -> initPostgreSql(); + case DORIS -> initDoris(); } log.info("Initialized analytics database: '{}'", database); } + /** Work for initializing a PostgreSQL analytics database. */ + private void initPostgreSql() { + // No work at this point + } + /** * Work for initializing a Doris analytics database. Creates a JDBC catalog which is used to * connect to and read from the PostgreSQL transaction database as an external data source. Read @@ -96,9 +101,4 @@ private void initDoris() { jdbcTemplate.execute(sqlBuilder.dropCatalogIfExists()); jdbcTemplate.execute(sqlBuilder.createCatalog(connectionUrl, username, password)); } - - /** Work for initializing a PostgreSQL analytics database. */ - private void initPostgreSql() { - // No work at this point - } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java index 8fdedbcd8937..749e1173e694 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java @@ -102,7 +102,7 @@ public boolean isAnalyticsDatabaseConfigured() { } /** - * Returns the configured analytics {@link Database}. Default is {@link Database#POSTGRESQL}. + * Returns the configured analytics {@link Database}. The default is {@link Database#POSTGRESQL}. * * @return the analytics {@link Database}. */ diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/model/Database.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/model/Database.java index c20f9ac54767..0924ac43ff0d 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/model/Database.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/model/Database.java @@ -27,12 +27,26 @@ */ package org.hisp.dhis.db.model; +import org.hisp.dhis.analytics.table.init.AnalyticsDatabaseInit; +import org.hisp.dhis.db.sql.SqlBuilder; +import org.hisp.dhis.db.sql.SqlBuilderProvider; + /** * Enumeration of database platforms. * + *

To add support for a new analytics database engine, add the following: + * + *

    + *
  • Value to this {@link Database}. + *
  • Implementation class of {@link SqlBuilder}. + *
  • Register {@link SqlBuilder} implementation in {@link SqlBuilderProvider}. + *
  • Method to {@link AnalyticsDatabaseInit} (optional). + *
  • JDBC driver in pom.xml. + *
+ * * @author Lars Helge Overland */ public enum Database { POSTGRESQL, - DORIS; + DORIS } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/AbstractSqlBuilder.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/AbstractSqlBuilder.java index d40f83c2214a..2f18c53c0dad 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/AbstractSqlBuilder.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/AbstractSqlBuilder.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; import org.apache.commons.text.StringSubstitutor; import org.hisp.dhis.db.model.DataType; @@ -58,11 +59,21 @@ public abstract class AbstractSqlBuilder implements SqlBuilder { // Utilities + @Override + public String quote(String alias, String relation) { + return alias + DOT + quote(relation); + } + @Override public String quoteAx(String relation) { return ALIAS_AX + DOT + quote(relation); } + @Override + public String singleQuote(String value) { + return SINGLE_QUOTE + escape(value) + SINGLE_QUOTE; + } + @Override public String singleQuotedCommaDelimited(Collection items) { return isEmpty(items) @@ -70,6 +81,23 @@ public String singleQuotedCommaDelimited(Collection items) { : items.stream().map(this::singleQuote).collect(Collectors.joining(COMMA)); } + // Index types + + @Override + public String indexTypeBtree() { + return notSupported(); + } + + @Override + public String indexTypeGist() { + return notSupported(); + } + + @Override + public String indexTypeGin() { + return notSupported(); + } + // Statements @Override @@ -103,6 +131,28 @@ public String countRows(Table table) { return String.format("select count(*) as row_count from %s;", quote(table.getName())); } + // Table + + @Override + public String analyzeTable(String name) { + return notSupported(); + } + + @Override + public String vacuumTable(Table table) { + return notSupported(); + } + + @Override + public String setParentTable(Table table, String parentName) { + return notSupported(); + } + + @Override + public String removeParentTable(Table table, String parentName) { + return notSupported(); + } + // Mapping /** @@ -206,4 +256,17 @@ protected String toIndexColumn(Index index, String column) { protected String notSupported() { throw new UnsupportedOperationException(); } + + /** + * Converts the given collection to a comma-separated string, using the given mapping function to + * convert each item in the collection to a string. + * + * @param + * @param collection the {@link Collection}. + * @param mapper the string mapping {@link Function}. + * @return a comma-separated string. + */ + protected String toCommaSeparated(Collection collection, Function mapper) { + return collection.stream().map(mapper).collect(Collectors.joining(",")); + } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java index cf13ca1a9949..9a9fc8506718 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java @@ -27,8 +27,6 @@ */ package org.hisp.dhis.db.sql; -import static org.hisp.dhis.commons.util.TextUtils.removeLastComma; - import java.util.Map; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.Validate; @@ -131,23 +129,6 @@ public String dataTypeJson() { return "json"; } - // Index types - - @Override - public String indexTypeBtree() { - return notSupported(); - } - - @Override - public String indexTypeGist() { - return notSupported(); - } - - @Override - public String indexTypeGin() { - return notSupported(); - } - // Index functions @Override @@ -195,16 +176,6 @@ public String quote(String relation) { return QUOTE + escapedRelation + QUOTE; } - @Override - public String quote(String alias, String relation) { - return alias + DOT + quote(relation); - } - - @Override - public String singleQuote(String value) { - return SINGLE_QUOTE + escape(value) + SINGLE_QUOTE; - } - @Override public String escape(String value) { return value @@ -234,29 +205,18 @@ public String createTable(Table table) { // Columns if (table.hasColumns()) { - sql.append("("); - - for (Column column : table.getColumns()) { - String dataType = getDataTypeName(column.getDataType()); - String nullable = column.getNullable() == Nullable.NOT_NULL ? " not null" : " null"; - - sql.append(quote(column.getName()) + " ").append(dataType).append(nullable).append(", "); - } + String columns = toCommaSeparated(table.getColumns(), this::toColumnString); - removeLastComma(sql).append(") engine = olap "); + sql.append("(").append(columns).append(") engine = olap "); } // Primary key if (table.hasPrimaryKey()) { // If primary key exists, use it as keys with the unique model - sql.append("unique key ("); + String keys = toCommaSeparated(table.getPrimaryKey(), this::quote); - for (String columnName : table.getPrimaryKey()) { - sql.append(quote(columnName) + ", "); - } - - removeLastComma(sql).append(") "); + sql.append("unique key (").append(keys).append(") "); } else if (table.hasColumns()) { // If columns exist, use first as key with the duplicate model String key = quote(table.getFirstColumn().getName()); @@ -267,17 +227,9 @@ public String createTable(Table table) { // Partitions if (table.hasPartitions()) { - sql.append("partition by range(year) ("); // Make configurable - - for (TablePartition partition : table.getPartitions()) { - sql.append("partition ") - .append(quote(partition.getName())) - .append(" values less than(\"") - .append(partition.getValue()) // Set last partition to max value - .append("\"),"); - } + String partitions = toCommaSeparated(table.getPartitions(), this::toPartitionString); - removeLastComma(sql).append(") "); + sql.append("partition by range(year) (").append(partitions).append(") "); // Make configurable } // Distribution @@ -298,6 +250,29 @@ public String createTable(Table table) { return sql.append(";").toString(); } + /** + * Returns a column definition string. + * + * @param column the {@link Column}. + * @return a column clause. + */ + private String toColumnString(Column column) { + String dataType = getDataTypeName(column.getDataType()); + String nullable = column.getNullable() == Nullable.NOT_NULL ? " not null" : " null"; + return quote(column.getName()) + " " + dataType + nullable; + } + + /** + * Returns a partition definition string. + * + * @param partition the {@link TablePartition}. + * @return a partition definition string. + */ + private String toPartitionString(TablePartition partition) { + String condition = "values less than(\"" + partition.getValue() + "\")"; + return "partition " + quote(partition.getName()) + " " + condition; + } + /** * Returns the distribution key. Uses the first primary key column name if any exists, or the * first column name if any exists, otherwise null. @@ -314,16 +289,6 @@ private String getDistKey(Table table) { return null; } - @Override - public String analyzeTable(String name) { - return notSupported(); - } - - @Override - public String vacuumTable(Table table) { - return notSupported(); - } - @Override public String renameTable(Table table, String newName) { return String.format("alter table %s rename %s;", quote(table.getName()), quote(newName)); @@ -344,22 +309,13 @@ public String dropTableIfExistsCascade(String name) { return dropTableIfExists(name); } - @Override - public String setParentTable(Table table, String parentName) { - return notSupported(); - } - - @Override - public String removeParentTable(Table table, String parentName) { - return notSupported(); - } - @Override public String tableExists(String name) { return String.format( """ select t.table_name from information_schema.tables t \ - where t.table_schema = 'public' and t.table_name = %s;""", + where t.table_schema = 'public' \ + and t.table_name = %s;""", singleQuote(name)); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java index 4dcfe13a4001..68df08428a23 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java @@ -29,7 +29,6 @@ import static org.hisp.dhis.commons.util.TextUtils.removeLastComma; -import java.util.stream.Collectors; import org.hisp.dhis.db.model.Collation; import org.hisp.dhis.db.model.Column; import org.hisp.dhis.db.model.Index; @@ -198,16 +197,6 @@ public String quote(String relation) { return QUOTE + escapedRelation + QUOTE; } - @Override - public String quote(String alias, String relation) { - return alias + DOT + quote(relation); - } - - @Override - public String singleQuote(String value) { - return SINGLE_QUOTE + escape(value) + SINGLE_QUOTE; - } - @Override public String escape(String value) { return value @@ -340,12 +329,7 @@ public String createIndex(Index index) { String unique = index.getUnique() == Unique.UNIQUE ? "unique " : ""; String tableName = index.getTableName(); String typeName = getIndexTypeName(index.getIndexType()); - - String columns = - index.getColumns().stream() - .map(col -> toIndexColumn(index, col)) - .collect(Collectors.joining(COMMA)); - + String columns = toCommaSeparated(index.getColumns(), col -> toIndexColumn(index, col)); String sortOrder = index.getSortOrder(); return sortOrder == null diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java index 1d8f7b889de4..ae40a411976f 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java @@ -68,7 +68,7 @@ public class JdbcResourceTableStore implements ResourceTableStore { private final SqlBuilder sqlBuilder = new PostgreSqlBuilder(); - private final SqlBuilder analyticsSqlBuilder; + private final SqlBuilder analyticsDatabaseSqlBuilder; @Override public void generateResourceTable(ResourceTable resourceTable) { @@ -105,11 +105,11 @@ public void replicateAnalyticsResourceTable(ResourceTable resourceTable) { final Table table = resourceTable.getMainTable(); final String tableName = table.getName(); - dropAnalyticsTable(table); + dropAnalyticsDatabaseTable(table); - createAnalyticsTable(table); + createAnalyticsDatabaseTable(table); - replicateAnalyticsTable(table); + replicateAnalyticsDatabaseTable(table); log.info("Analytics resource table replication done: '{}' '{}'", tableName, clock.time()); } @@ -119,8 +119,8 @@ public void replicateAnalyticsResourceTable(ResourceTable resourceTable) { * * @param table the {@link Table}. */ - private void dropAnalyticsTable(Table table) { - String sql = analyticsSqlBuilder.dropTableIfExists(table); + private void dropAnalyticsDatabaseTable(Table table) { + String sql = analyticsDatabaseSqlBuilder.dropTableIfExists(table); log.info("Drop table SQL: '{}'", sql); analyticsJdbcTemplate.execute(sql); } @@ -130,8 +130,8 @@ private void dropAnalyticsTable(Table table) { * * @param table the {@link Table}. */ - private void createAnalyticsTable(Table table) { - String sql = analyticsSqlBuilder.createTable(table); + private void createAnalyticsDatabaseTable(Table table) { + String sql = analyticsDatabaseSqlBuilder.createTable(table); log.info("Create table SQL: '{}'", sql); analyticsJdbcTemplate.execute(sql); } @@ -141,12 +141,12 @@ private void createAnalyticsTable(Table table) { * * @param table the {@link Table}. */ - private void replicateAnalyticsTable(Table table) { + private void replicateAnalyticsDatabaseTable(Table table) { String sql = format( "insert into %s select * from %s", - analyticsSqlBuilder.quote(table.getName()), - analyticsSqlBuilder.qualifyTable(table.getName())); + analyticsDatabaseSqlBuilder.quote(table.getName()), + analyticsDatabaseSqlBuilder.qualifyTable(table.getName())); log.info("Replicate table SQL: '{}'", sql); analyticsJdbcTemplate.execute(sql); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/DorisSqlBuilderTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/DorisSqlBuilderTest.java index 222d78d4da16..2128577adcb3 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/DorisSqlBuilderTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/DorisSqlBuilderTest.java @@ -188,9 +188,9 @@ void testCreateTableA() { String expected = """ - create table `immunization` (`id` bigint not null, \ - `data` char(11) not null, `period` varchar(50) not null, \ - `created` datetime null, `user` json null, `value` double null) \ + create table `immunization` (`id` bigint not null,\ + `data` char(11) not null,`period` varchar(50) not null,\ + `created` datetime null,`user` json null,`value` double null) \ engine = olap \ unique key (`id`) \ distributed by hash(`id`) buckets 10 \ @@ -205,8 +205,8 @@ void testCreateTableB() { String expected = """ - create table `vaccination` (`id` int not null, \ - `facility_type` varchar(255) null, `bcg_doses` double null) \ + create table `vaccination` (`id` int not null,\ + `facility_type` varchar(255) null,`bcg_doses` double null) \ engine = olap \ duplicate key (`id`) \ distributed by hash(`id`) buckets 10 \ @@ -223,8 +223,8 @@ void testCreateTableC() { String expected = """ - create table `nutrition` (`id` bigint not null, \ - `vitamin_a` bigint null, `vitamin_d` bigint null) \ + create table `nutrition` (`id` bigint not null,\ + `vitamin_a` bigint null,`vitamin_d` bigint null) \ engine = olap \ unique key (`id`) \ distributed by hash(`id`) buckets 10 \ diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/PostgreSqlBuilderTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/PostgreSqlBuilderTest.java index 5ea87388b2e7..b5270ec3aecb 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/PostgreSqlBuilderTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/db/sql/PostgreSqlBuilderTest.java @@ -373,7 +373,7 @@ void testCreateIndexB() { List indexes = getIndexesA(); String expected = - "create index \"in_immunization_period_created\" on \"immunization\" using btree(\"period\", \"created\");"; + "create index \"in_immunization_period_created\" on \"immunization\" using btree(\"period\",\"created\");"; assertEquals(expected, sqlBuilder.createIndex(indexes.get(1))); } @@ -393,7 +393,7 @@ void testCreateIndexD() { List indexes = getIndexesA(); String expected = - "create index \"in_immunization_data_period\" on \"immunization\" using btree(lower(\"data\"), lower(\"period\"));"; + "create index \"in_immunization_data_period\" on \"immunization\" using btree(lower(\"data\"),lower(\"period\"));"; assertEquals(expected, sqlBuilder.createIndex(indexes.get(3))); } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java index faa2e71b6a0d..18d4a16a829a 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java @@ -766,7 +766,13 @@ private void collectScanTargets(Map, List> targets) { } objects.forEach( - o -> list.addAll(ReflectionUtils.invokeMethod(o, property.getGetterMethod()))); + o -> { + Collection propertyValue = + ReflectionUtils.invokeMethod(o, property.getGetterMethod()); + if (!org.apache.commons.collections4.CollectionUtils.isEmpty(propertyValue)) { + list.addAll(propertyValue); + } + }); targets.put(property.getItemKlass(), list); } else { List list = new ArrayList<>(); @@ -776,7 +782,12 @@ private void collectScanTargets(Map, List> targets) { } objects.forEach( - o -> list.add(ReflectionUtils.invokeMethod(o, property.getGetterMethod()))); + o -> { + Object item = ReflectionUtils.invokeMethod(o, property.getGetterMethod()); + if (item != null) { + list.add(item); + } + }); targets.put(property.getKlass(), list); } } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/TrackedEntityDataValueChangeLogDeletionHandler.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/TrackedEntityDataValueChangeLogDeletionHandler.java index 1298e7db82d6..11a6b0e7d97e 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/TrackedEntityDataValueChangeLogDeletionHandler.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/TrackedEntityDataValueChangeLogDeletionHandler.java @@ -49,5 +49,7 @@ private void deleteDataElement(DataElement dataElement) { delete( "delete from trackedentitydatavalueaudit where dataelementid = :id", Map.of("id", dataElement.getId())); + delete( + "delete from eventchangelog where dataelementid = :id", Map.of("id", dataElement.getId())); } } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/oidc/DhisOidcUser.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/oidc/DhisOidcUser.java index 90818d9ba386..4b77673bf69e 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/oidc/DhisOidcUser.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/oidc/DhisOidcUser.java @@ -214,6 +214,11 @@ public boolean isTwoFactorEnabled() { return user.isTwoFactorEnabled(); } + @Override + public boolean isEmailVerified() { + return user.isEmailVerified(); + } + @Override public boolean hasAnyRestrictions(Collection restrictions) { return user.hasAnyRestrictions(restrictions); diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/spring2fa/TwoFactorAuthenticationProvider.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/spring2fa/TwoFactorAuthenticationProvider.java index e90db9f15e18..82b438917d0b 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/spring2fa/TwoFactorAuthenticationProvider.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/spring2fa/TwoFactorAuthenticationProvider.java @@ -51,6 +51,7 @@ /** * @author Henning Håkonsen + * @author Morten Svanæs */ @Slf4j @Component diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/eventchangelog.hiberante/EventChangeLog.hbm.xml b/dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/eventchangelog.hiberante/EventChangeLog.hbm.xml new file mode 100644 index 000000000000..4c8e53e7434f --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/eventchangelog.hiberante/EventChangeLog.hbm.xml @@ -0,0 +1,43 @@ + + + + + + + + + eventchangelog_sequence + + + + + + + + + + + + + + + + org.hisp.dhis.changelog.ChangeLogType + true + 12 + + + + + + + + + + + + diff --git a/dhis-2/dhis-services/dhis-service-setting/src/test/java/org/hisp/dhis/setting/SystemSettingsTest.java b/dhis-2/dhis-services/dhis-service-setting/src/test/java/org/hisp/dhis/setting/SystemSettingsTest.java index 7cbdaa8997d0..0af982a766c3 100644 --- a/dhis-2/dhis-services/dhis-service-setting/src/test/java/org/hisp/dhis/setting/SystemSettingsTest.java +++ b/dhis-2/dhis-services/dhis-service-setting/src/test/java/org/hisp/dhis/setting/SystemSettingsTest.java @@ -90,7 +90,7 @@ void testIsTranslatable() { @Test void testKeysWithDefaults() { Set keys = SystemSettings.keysWithDefaults(); - assertEquals(136, keys.size()); + assertEquals(137, keys.size()); // just check some at random assertTrue(keys.contains("syncSkipSyncForDataChangedBefore")); assertTrue(keys.contains("keyTrackerDashboardLayout")); diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/DefaultEventChangeLogService.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/DefaultEventChangeLogService.java index 8be300780c0c..755eddc9545c 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/DefaultEventChangeLogService.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/DefaultEventChangeLogService.java @@ -29,9 +29,11 @@ import static org.hisp.dhis.user.CurrentUserUtil.getCurrentUserDetails; +import java.util.Date; import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; +import org.hisp.dhis.changelog.ChangeLogType; import org.hisp.dhis.common.UID; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.feedback.ForbiddenException; @@ -48,7 +50,7 @@ public class DefaultEventChangeLogService implements EventChangeLogService { private final EventService eventService; - private final JdbcEventChangeLogStore jdbcEventChangeLogStore; + private final HibernateEventChangeLogStore hibernateEventChangeLogStore; private final HibernateTrackedEntityDataValueChangeLogStore trackedEntityDataValueChangeLogStore; private final TrackerAccessManager trackerAccessManager; @@ -60,7 +62,20 @@ public Page getEventChangeLog( // check existence and access eventService.getEvent(event); - return jdbcEventChangeLogStore.getEventChangeLog(event, operationParams.getOrder(), pageParams); + return hibernateEventChangeLogStore.getEventChangeLogs( + event, operationParams.getOrder(), pageParams); + } + + @Transactional + @Override + public void deleteEventChangeLog(Event event) { + hibernateEventChangeLogStore.deleteEventChangeLog(event); + } + + @Transactional + @Override + public void deleteEventChangeLog(DataElement dataElement) { + hibernateEventChangeLogStore.deleteEventChangeLog(dataElement); } @Override @@ -89,6 +104,30 @@ public void addTrackedEntityDataValueChangeLog( trackedEntityDataValueChangeLog); } + @Override + @Transactional + public void addDataValueChangeLog( + Event event, + DataElement dataElement, + String currentValue, + String previousValue, + ChangeLogType changeLogType, + String userName) { + + EventChangeLog eventChangeLog = + new EventChangeLog( + event, + dataElement, + null, + previousValue, + currentValue, + changeLogType, + new Date(), + userName); + + hibernateEventChangeLogStore.addEventChangeLog(eventChangeLog); + } + @Override @Transactional(readOnly = true) public int countTrackedEntityDataValueChangeLogs( @@ -111,6 +150,6 @@ public void deleteTrackedEntityDataValueChangeLog(DataElement dataElement) { @Override @Transactional(readOnly = true) public Set getOrderableFields() { - return jdbcEventChangeLogStore.getOrderableFields(); + return hibernateEventChangeLogStore.getOrderableFields(); } } diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLog.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLog.java index dfbc383b5b8c..676d43e0cee6 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLog.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLog.java @@ -27,24 +27,79 @@ */ package org.hisp.dhis.tracker.export.event; -import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hisp.dhis.changelog.ChangeLogType; +import org.hisp.dhis.dataelement.DataElement; +import org.hisp.dhis.program.Event; import org.hisp.dhis.program.UserInfoSnapshot; -public record EventChangeLog( - /** - * Ideally we would reuse {@code org.hisp.dhis.webapi.controller.tracker.view.User} so our - * responses stay consistent. - */ - @JsonProperty UserInfoSnapshot createdBy, - @JsonProperty Date createdAt, - @JsonProperty String type, - @JsonProperty Change change) { - - public record Change(@JsonProperty DataValueChange dataValue) {} - - public record DataValueChange( - @JsonProperty String dataElement, - @JsonProperty String previousValue, - @JsonProperty String currentValue) {} +@NoArgsConstructor +@Getter +@Setter +public class EventChangeLog { + private long id; + + private Event event; + + private DataElement dataElement; + + private String eventProperty; + + private String previousValue; + + private String currentValue; + + private ChangeLogType changeLogType; + + private Date created; + + private String createdByUsername; + + private UserInfoSnapshot createdBy; + + public EventChangeLog( + Event event, + DataElement dataElement, + String eventProperty, + String previousValue, + String currentValue, + ChangeLogType changeLogType, + Date created, + String createdByUsername) { + this(event, dataElement, eventProperty, previousValue, currentValue, changeLogType, created); + this.createdByUsername = createdByUsername; + } + + public EventChangeLog( + Event event, + DataElement dataElement, + String eventProperty, + String previousValue, + String currentValue, + ChangeLogType changeLogType, + Date created, + UserInfoSnapshot createdBy) { + this(event, dataElement, eventProperty, previousValue, currentValue, changeLogType, created); + this.createdBy = createdBy; + } + + private EventChangeLog( + Event event, + DataElement dataElement, + String eventProperty, + String previousValue, + String currentValue, + ChangeLogType changeLogType, + Date created) { + this.event = event; + this.dataElement = dataElement; + this.eventProperty = eventProperty; + this.previousValue = previousValue; + this.currentValue = currentValue; + this.changeLogType = changeLogType; + this.created = created; + } } diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLogService.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLogService.java index c09a29cbc4ac..c13295aa6e55 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLogService.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventChangeLogService.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Set; +import org.hisp.dhis.changelog.ChangeLogType; import org.hisp.dhis.common.UID; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.feedback.ForbiddenException; @@ -55,16 +56,29 @@ Page getEventChangeLog( List getTrackedEntityDataValueChangeLogs( TrackedEntityDataValueChangeLogQueryParams params); + @Deprecated(since = "2.42") void addTrackedEntityDataValueChangeLog( TrackedEntityDataValueChangeLog trackedEntityDataValueChangeLog); + void addDataValueChangeLog( + Event event, + DataElement dataElement, + String currentValue, + String previousValue, + ChangeLogType changeLogType, + String userName); + @Deprecated(since = "2.42") int countTrackedEntityDataValueChangeLogs(TrackedEntityDataValueChangeLogQueryParams params); void deleteTrackedEntityDataValueChangeLog(Event event); + void deleteEventChangeLog(Event event); + void deleteTrackedEntityDataValueChangeLog(DataElement dataElement); + void deleteEventChangeLog(DataElement dataElement); + /** * Fields the {@link #getEventChangeLog(UID, EventChangeLogOperationParams, PageParams)} can order * event change logs by. Ordering by fields other than these is considered a programmer error. diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateEventChangeLogStore.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateEventChangeLogStore.java new file mode 100644 index 000000000000..8702753bc1ce --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateEventChangeLogStore.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2004-2024, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.tracker.export.event; + +import static java.util.Map.entry; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.hibernate.Session; +import org.hisp.dhis.changelog.ChangeLogType; +import org.hisp.dhis.common.SortDirection; +import org.hisp.dhis.common.UID; +import org.hisp.dhis.dataelement.DataElement; +import org.hisp.dhis.program.Event; +import org.hisp.dhis.program.UserInfoSnapshot; +import org.hisp.dhis.tracker.export.Order; +import org.hisp.dhis.tracker.export.Page; +import org.hisp.dhis.tracker.export.PageParams; +import org.springframework.stereotype.Repository; + +@Repository("org.hisp.dhis.tracker.export.event.HibernateEventChangeLogStore") +public class HibernateEventChangeLogStore { + private static final String COLUMN_CHANGELOG_CREATED = "ecl.created"; + private static final String DEFAULT_ORDER = + COLUMN_CHANGELOG_CREATED + " " + SortDirection.DESC.getValue(); + + /** + * Event change logs can be ordered by given fields which correspond to fields on {@link + * EventChangeLog}. Maps fields to DB columns. The order implementation for change logs is + * different from other tracker exporters {@link EventChangeLog} is the view which is already + * returned from the service/store. Tracker exporter services return a representation we have to + * map to a view model. This mapping is not necessary for change logs. + */ + private static final Map ORDERABLE_FIELDS = + Map.ofEntries(entry("createdAt", COLUMN_CHANGELOG_CREATED)); + + private final EntityManager entityManager; + + public HibernateEventChangeLogStore(EntityManager entityManager) { + this.entityManager = entityManager; + } + + public void addEventChangeLog(EventChangeLog eventChangeLog) { + entityManager.unwrap(Session.class).save(eventChangeLog); + } + + public Page getEventChangeLogs( + UID event, List order, PageParams pageParams) { + + String hql = + String.format( + """ + select ecl.event, + ecl.dataElement, + ecl.eventProperty, + ecl.previousValue, + ecl.currentValue, + ecl.changeLogType, + ecl.created, + ecl.createdByUsername, + u.firstName, + u.surname, + u.uid + from EventChangeLog ecl + join ecl.event e + left join ecl.createdBy u + where e.uid = :eventUid + order by %s + """ + .formatted(sortExpressions(order))); + + Query query = entityManager.createQuery(hql); + query.setParameter("eventUid", event.getValue()); + query.setFirstResult((pageParams.getPage() - 1) * pageParams.getPageSize()); + query.setMaxResults(pageParams.getPageSize() + 1); + + List results = query.getResultList(); + List eventChangeLogs = + results.stream() + .map( + row -> { + Event e = (Event) row[0]; + DataElement dataElement = (DataElement) row[1]; + String eventProperty = (String) row[2]; + String previousValue = (String) row[3]; + String currentValue = (String) row[4]; + ChangeLogType changeLogType = (ChangeLogType) row[5]; + Date created = (Date) row[6]; + + UserInfoSnapshot createdBy = + new UserInfoSnapshot((String) row[7], (String) row[8], (String) row[9]); + createdBy.setUid((String) row[10]); + + return new EventChangeLog( + e, + dataElement, + eventProperty, + previousValue, + currentValue, + changeLogType, + created, + createdBy); + }) + .toList(); + + Integer prevPage = pageParams.getPage() > 1 ? pageParams.getPage() - 1 : null; + if (eventChangeLogs.size() > pageParams.getPageSize()) { + return Page.withPrevAndNext( + eventChangeLogs.subList(0, pageParams.getPageSize()), + pageParams.getPage(), + pageParams.getPageSize(), + prevPage, + pageParams.getPage() + 1); + } + + return Page.withPrevAndNext( + eventChangeLogs, pageParams.getPage(), pageParams.getPageSize(), prevPage, null); + } + + public void deleteEventChangeLog(DataElement dataElement) { + String hql = "delete from EventChangeLog where dataElement = :dataElement"; + + entityManager.createQuery(hql).setParameter("dataElement", dataElement).executeUpdate(); + } + + public void deleteEventChangeLog(Event event) { + String hql = "delete from EventChangeLog where event = :event"; + + entityManager.createQuery(hql).setParameter("event", event).executeUpdate(); + } + + private static String sortExpressions(List order) { + if (order.isEmpty()) { + return DEFAULT_ORDER; + } + + return ORDERABLE_FIELDS.get(order.get(0).getField()) + + " " + + order.get(0).getDirection().getValue(); + } + + public Set getOrderableFields() { + return ORDERABLE_FIELDS.keySet(); + } +} diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateTrackedEntityDataValueChangeLogStore.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateTrackedEntityDataValueChangeLogStore.java index 4509a8fe541a..dfa9cc4bb1f0 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateTrackedEntityDataValueChangeLogStore.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/HibernateTrackedEntityDataValueChangeLogStore.java @@ -59,7 +59,7 @@ class HibernateTrackedEntityDataValueChangeLogStore { // Dependencies // ------------------------------------------------------------------------- - private EntityManager entityManager; + private final EntityManager entityManager; public HibernateTrackedEntityDataValueChangeLogStore(EntityManager entityManager) { this.entityManager = entityManager; diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventChangeLogStore.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventChangeLogStore.java deleted file mode 100644 index 2cb074a0a17d..000000000000 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventChangeLogStore.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2004-2024, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.tracker.export.event; - -import static java.util.Map.entry; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import lombok.RequiredArgsConstructor; -import org.hisp.dhis.common.SortDirection; -import org.hisp.dhis.common.UID; -import org.hisp.dhis.program.UserInfoSnapshot; -import org.hisp.dhis.tracker.export.Order; -import org.hisp.dhis.tracker.export.Page; -import org.hisp.dhis.tracker.export.PageParams; -import org.hisp.dhis.tracker.export.event.EventChangeLog.Change; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.stereotype.Repository; - -@Repository("org.hisp.dhis.tracker.export.event.JdbcEventChangeLogStore") -@RequiredArgsConstructor -class JdbcEventChangeLogStore { - private static final String COLUMN_CHANGELOG_CREATED = "created"; - private static final String DEFAULT_ORDER = - COLUMN_CHANGELOG_CREATED + " " + SortDirection.DESC.getValue(); - - /** - * Event change logs can be ordered by given fields which correspond to fields on {@link - * org.hisp.dhis.tracker.export.event.EventChangeLog}. Maps fields to DB columns. The order - * implementation for change logs is different from other tracker exporters {@link - * org.hisp.dhis.tracker.export.event.EventChangeLog} is the view which is already returned from - * the service/store. Tracker exporter services return a representation we have to map to a view - * model. This mapping is not necessary for change logs. - */ - private static final Map ORDERABLE_FIELDS = - Map.ofEntries(entry("createdAt", COLUMN_CHANGELOG_CREATED)); - - private final NamedParameterJdbcTemplate jdbcTemplate; - - private static final RowMapper customEventChangeLogRowMapper = - (rs, rowNum) -> { - UserInfoSnapshot createdBy = new UserInfoSnapshot(); - createdBy.setUsername(rs.getString("username")); - createdBy.setFirstName(rs.getString("firstname")); - createdBy.setSurname(rs.getString("surname")); - createdBy.setUid(rs.getString("useruid")); - - return new EventChangeLog( - createdBy, - rs.getTimestamp(COLUMN_CHANGELOG_CREATED), - rs.getString("type"), - new Change( - new EventChangeLog.DataValueChange( - rs.getString("dataelementuid"), - rs.getString("previousvalue"), - rs.getString("currentvalue")))); - }; - - public Page getEventChangeLog( - UID event, List order, PageParams pageParams) { - // language=SQL - String sql = - """ - select - cl.type, - case - when cl.type = 'CREATE' then cl.previouschangelogvalue - when cl.type = 'UPDATE' and cl.currentchangelogvalue is null then cl.currentvalue - when cl.type = 'UPDATE' and cl.currentchangelogvalue is not null then cl.currentchangelogvalue - end as currentvalue, - case - when cl.type = 'DELETE' then cl.previouschangelogvalue - when cl.type = 'UPDATE' then cl.previouschangelogvalue - end as previousvalue, cl.dataelementuid, cl.created, cl.username, cl.firstname, cl.surname, cl.useruid - from - (select t.created, d.uid as dataelementuid, t.audittype as type, u.firstname, u.surname, u.username, u.uid as useruid, - LAG (t.value) OVER (PARTITION BY t.eventid, t.dataelementid ORDER BY t.created DESC) AS currentchangelogvalue, - t.value as previouschangelogvalue, - e.eventdatavalues -> d.uid ->> 'value' as currentvalue - from trackedentitydatavalueaudit t - join event e using (eventid) - join dataelement d using (dataelementid) - join userinfo u on u.username = t.modifiedby - where t.audittype in ('CREATE', 'UPDATE', 'DELETE') - and e.uid = :uid - order by %s - limit :limit offset :offset) cl - """ - .formatted(sortExpressions(order)); - - final MapSqlParameterSource parameters = new MapSqlParameterSource(); - parameters - .addValue("uid", event.getValue()) - .addValue("limit", pageParams.getPageSize() + 1) - .addValue("offset", (pageParams.getPage() - 1) * pageParams.getPageSize()); - - List changeLogs = - jdbcTemplate.query(sql, parameters, customEventChangeLogRowMapper); - - Integer prevPage = pageParams.getPage() > 1 ? pageParams.getPage() - 1 : null; - if (changeLogs.size() > pageParams.getPageSize()) { - return Page.withPrevAndNext( - changeLogs.subList(0, pageParams.getPageSize()), - pageParams.getPage(), - pageParams.getPageSize(), - prevPage, - pageParams.getPage() + 1); - } - - return Page.withPrevAndNext( - changeLogs, pageParams.getPage(), pageParams.getPageSize(), prevPage, null); - } - - private static String sortExpressions(List order) { - if (order.isEmpty()) { - return DEFAULT_ORDER; - } - - return ORDERABLE_FIELDS.get(order.get(0).getField()) - + " " - + order.get(0).getDirection().getValue(); - } - - public Set getOrderableFields() { - return ORDERABLE_FIELDS.keySet(); - } -} diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/AbstractTrackerPersister.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/AbstractTrackerPersister.java index e29562658c1c..dc069a7d3715 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/AbstractTrackerPersister.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/AbstractTrackerPersister.java @@ -122,14 +122,13 @@ public TrackerTypeReport persist(EntityManager entityManager, TrackerBundle bund // persistOwnership(bundle, trackerDto, convertedDto); - updateDataValues( - entityManager, bundle.getPreheat(), trackerDto, convertedDto, bundle.getUser()); - // // Save or update the entity // if (isNew(bundle, trackerDto)) { entityManager.persist(convertedDto); + updateDataValues( + entityManager, bundle.getPreheat(), trackerDto, convertedDto, bundle.getUser()); typeReport.getStats().incCreated(); typeReport.addEntity(objectReport); updateAttributes( @@ -139,6 +138,8 @@ public TrackerTypeReport persist(EntityManager entityManager, TrackerBundle bund typeReport.getStats().incIgnored(); // Relationships are not updated. A warning was already added to the report } else { + updateDataValues( + entityManager, bundle.getPreheat(), trackerDto, convertedDto, bundle.getUser()); updateAttributes( entityManager, bundle.getPreheat(), trackerDto, convertedDto, bundle.getUser()); entityManager.merge(convertedDto); diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/DefaultTrackerObjectsDeletionService.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/DefaultTrackerObjectsDeletionService.java index 8d7e8ff6b465..2fd6e1e54ff1 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/DefaultTrackerObjectsDeletionService.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/DefaultTrackerObjectsDeletionService.java @@ -141,6 +141,7 @@ public TrackerTypeReport deleteEvents(List events) throws NotFoundException // This is needed until deprecated method // eventChangeLogService.getTrackedEntityDataValueChangeLogs is removed. eventChangeLogService.deleteTrackedEntityDataValueChangeLog(event); + eventChangeLogService.deleteEventChangeLog(event); List notificationInstances = programNotificationInstanceService.getProgramNotificationInstances( diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/EventPersister.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/EventPersister.java index 0a7b23a6adfd..2c8c05e6fe5c 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/EventPersister.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/persister/EventPersister.java @@ -27,6 +27,10 @@ */ package org.hisp.dhis.tracker.imports.bundle.persister; +import static org.hisp.dhis.changelog.ChangeLogType.CREATE; +import static org.hisp.dhis.changelog.ChangeLogType.DELETE; +import static org.hisp.dhis.changelog.ChangeLogType.UPDATE; + import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Collections; @@ -41,7 +45,6 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; -import org.hisp.dhis.changelog.ChangeLogType; import org.hisp.dhis.common.UID; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.event.EventStatus; @@ -51,7 +54,6 @@ import org.hisp.dhis.reservedvalue.ReservedValueService; import org.hisp.dhis.tracker.TrackerType; import org.hisp.dhis.tracker.export.event.EventChangeLogService; -import org.hisp.dhis.tracker.export.event.TrackedEntityDataValueChangeLog; import org.hisp.dhis.tracker.export.trackedentity.TrackedEntityChangeLogService; import org.hisp.dhis.tracker.imports.bundle.TrackerBundle; import org.hisp.dhis.tracker.imports.bundle.TrackerObjectsMapper; @@ -172,32 +174,22 @@ private void handleDataValues( EventDataValue dbDataValue = dataValueDBMap.get(dataElement.getUid()); if (isNewDataValue(dbDataValue, dataValue)) { - logDataValueChange( - user.getUsername(), - dataElement, - event, - dataValue.getValue(), - dataValue.isProvidedElsewhere(), - ChangeLogType.CREATE); + eventChangeLogService.addDataValueChangeLog( + event, dataElement, dataValue.getValue(), null, CREATE, user.getUsername()); saveDataValue(dataValue, event, dataElement, user, entityManager, preheat); } else if (isUpdate(dbDataValue, dataValue)) { - logDataValueChange( - user.getUsername(), - dataElement, + eventChangeLogService.addDataValueChangeLog( event, + dataElement, + dataValue.getValue(), dbDataValue.getValue(), - dbDataValue.getProvidedElsewhere(), - ChangeLogType.UPDATE); + UPDATE, + user.getUsername()); updateDataValue( dbDataValue, dataValue, event, dataElement, user, entityManager, preheat); } else if (isDeletion(dbDataValue, dataValue)) { - logDataValueChange( - user.getUsername(), - dataElement, - event, - dbDataValue.getValue(), - dbDataValue.getProvidedElsewhere(), - ChangeLogType.DELETE); + eventChangeLogService.addDataValueChangeLog( + event, dataElement, null, dbDataValue.getValue(), DELETE, user.getUsername()); deleteDataValue(dbDataValue, event, dataElement, entityManager, preheat); } }); @@ -262,26 +254,6 @@ private void deleteDataValue( event.getEventDataValues().remove(eventDataValue); } - private void logDataValueChange( - String userName, - DataElement de, - Event event, - String value, - boolean providedElsewhere, - ChangeLogType changeLogType) { - - TrackedEntityDataValueChangeLog changeLog = new TrackedEntityDataValueChangeLog(); - changeLog.setEvent(event); - changeLog.setValue(value); - changeLog.setAuditType(changeLogType); - changeLog.setDataElement(de); - changeLog.setModifiedBy(userName); - changeLog.setProvidedElsewhere(providedElsewhere); - changeLog.setCreated(new Date()); - - eventChangeLogService.addTrackedEntityDataValueChangeLog(changeLog); - } - @Override protected void persistOwnership( TrackerBundle bundle, org.hisp.dhis.tracker.imports.domain.Event trackerDto, Event entity) { diff --git a/dhis-2/dhis-support/dhis-support-db-migration/src/main/resources/org/hisp/dhis/db/migration/2.42/V2_42_25__Migrate_event_change_logs.sql b/dhis-2/dhis-support/dhis-support-db-migration/src/main/resources/org/hisp/dhis/db/migration/2.42/V2_42_25__Migrate_event_change_logs.sql new file mode 100644 index 000000000000..180f2349c658 --- /dev/null +++ b/dhis-2/dhis-support/dhis-support-db-migration/src/main/resources/org/hisp/dhis/db/migration/2.42/V2_42_25__Migrate_event_change_logs.sql @@ -0,0 +1,60 @@ +-- DHIS2-18117: migrate tracker event change logs from trackedentitydatavalueaudit to eventchangelogs + +-- Create new table and add constraints +create sequence if not exists eventchangelog_sequence; + +create table if not exists eventchangelog ( + eventchangelogid int8 not null default nextval('eventchangelog_sequence'), + eventid int8 not null, + dataelementid int8 null, + eventproperty varchar(100) null, + currentvalue varchar(50000) null, + previousvalue varchar(50000) null, + changelogtype varchar(100) not null, + created timestamp not null, + createdby varchar(255) not null, + constraint eventchangelog_pkey primary key (eventchangelogid), + constraint fk_eventchangelog_dataelementid foreign key (dataelementid) references dataelement(dataelementid), + constraint fk_eventchangelog_eventid foreign key (eventid) references event(eventid) +); + +create index if not exists eventchangelog_eventid_created_idx on eventchangelog (eventid, created desc); + +-- Migrate data from trackedentitydatavalueaudit to eventchangelog +insert into eventchangelog (eventchangelogid, eventid, dataelementid, currentvalue, previousvalue, created, createdby, changelogtype) +select + cl.trackedentitydatavalueauditid, + cl.eventid, + cl.dataelementid, + case + when cl.audittype = 'CREATE' then cl.previouschangelogvalue + when cl.audittype = 'UPDATE' and cl.currentchangelogvalue is null then cl.currentvalue + when cl.audittype = 'UPDATE' and cl.currentchangelogvalue is not null then cl.currentchangelogvalue + end, + case + when cl.audittype = 'DELETE' then cl.previouschangelogvalue + when cl.audittype = 'UPDATE' then cl.previouschangelogvalue + end, + coalesce(cl.created, '1970-01-01 00:00:00'), + coalesce (cl.modifiedby, '--'), + cl.audittype +from + (select t.trackedentitydatavalueauditid, t.eventid, t.dataelementid, t.created, t.audittype, t.modifiedby, + lag (t.value) over (partition by t.eventid, t.dataelementid order by t.created desc) as currentchangelogvalue, + t.value AS previouschangelogvalue, + e.eventdatavalues -> d.uid ->> 'value' as currentvalue + from trackedentitydatavalueaudit t + join event e using (eventid) + join dataelement d using (dataelementid) + order by t.trackedentitydatavalueauditid) cl +where cl.audittype in ('CREATE', 'UPDATE', 'DELETE') +and not exists ( + select 1 from eventchangelog ecl where ecl.eventchangelogid = cl.trackedentitydatavalueauditid +); + +-- Set sequence to highest value +select setval('eventchangelog_sequence', max(eventchangelogid)) from eventchangelog; + +-- Delete the migrated data from the old table, keeping the unmigrated data. +delete from trackedentitydatavalueaudit +where trackedentitydatavalueauditid in (select eventchangelogid from eventchangelog); \ No newline at end of file diff --git a/dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java b/dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java index caae8b168242..a67e6f9151d8 100644 --- a/dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java +++ b/dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java @@ -682,6 +682,11 @@ public enum ConfigurationKey { private final String defaultValue; + /** + * Confidential means that the system setting will be encrypted and not visible through the API. + * The system setting will be used internally in the backend, but cannot be used by web apps and + * clients. + */ private final boolean confidential; private final String[] aliases; diff --git a/dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/dbms/HibernateDbmsManager.java b/dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/dbms/HibernateDbmsManager.java index 20eaeecce803..9b7a0fbb3307 100644 --- a/dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/dbms/HibernateDbmsManager.java +++ b/dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/dbms/HibernateDbmsManager.java @@ -216,6 +216,7 @@ public void emptyDatabase() { emptyTable("programnotificationinstance"); emptyTable("trackedentitydatavalueaudit"); + emptyTable("eventchangelog"); emptyTable("trackedentityprogramowner"); emptyTable("event_notes"); diff --git a/dhis-2/dhis-support/dhis-support-system/pom.xml b/dhis-2/dhis-support/dhis-support-system/pom.xml index c73ab28a73b6..72d57d056c6b 100644 --- a/dhis-2/dhis-support/dhis-support-system/pom.xml +++ b/dhis-2/dhis-support/dhis-support-system/pom.xml @@ -144,10 +144,6 @@ org.apache.commons commons-math3 - - commons-codec - commons-codec - commons-validator commons-validator diff --git a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CodecUtils.java b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CodecUtils.java index 95d2872c99f8..a4ca85cae8c0 100644 --- a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CodecUtils.java +++ b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CodecUtils.java @@ -31,7 +31,6 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; -import org.apache.commons.codec.digest.DigestUtils; /** * Utility class for encoding and decoding operations. @@ -85,19 +84,4 @@ public static String utf8UrlEncode(String string) { throw new RuntimeException(e); } } - - /** - * Calculates the MD5 digest and returns the value as a 32 character hex string. Returns null if - * input is null. - * - * @param value the value to digest. - * @return MD5 digest as a hex string. - */ - public static String md5Hex(String value) { - return value != null ? DigestUtils.md5Hex(value) : null; - } - - public static String sha1Hex(String value) { - return value != null ? DigestUtils.sha1Hex(value) : null; - } } diff --git a/dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/CodecUtilsTest.java b/dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/CodecUtilsTest.java deleted file mode 100644 index a0fc9e7144f0..000000000000 --- a/dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/CodecUtilsTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.system.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import org.junit.jupiter.api.Test; - -class CodecUtilsTest { - @Test - void testMd5Hex() { - String value = "10-05-2022T12:55:45"; - - assertNull(CodecUtils.md5Hex(null)); - assertEquals(32, CodecUtils.md5Hex(value).length()); - assertEquals("c149820871470e3ab15eb24d42b3561a", CodecUtils.md5Hex(value)); - } - - @Test - void testSha1Hex() { - String value = "/api/me"; - - assertNull(CodecUtils.sha1Hex(null)); - assertEquals(40, CodecUtils.sha1Hex(value).length()); - assertEquals("4f8cc3f306852ecb642ba4375453be1a4b860e71", CodecUtils.sha1Hex(value)); - } -} diff --git a/dhis-2/dhis-test-e2e/pom.xml b/dhis-2/dhis-test-e2e/pom.xml index 2f8a3ea6193a..dc85d83e70ac 100644 --- a/dhis-2/dhis-test-e2e/pom.xml +++ b/dhis-2/dhis-test-e2e/pom.xml @@ -29,7 +29,7 @@ 2.9.0 1.5.3 4.2.2 - 1.18.34 + 1.18.36 2.29.0 4.26.0 2.0.16 diff --git a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/CategoryOptionMergeTest.java b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/CategoryOptionMergeTest.java index 83b050076c6a..db7ad8e0972b 100644 --- a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/CategoryOptionMergeTest.java +++ b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/CategoryOptionMergeTest.java @@ -113,9 +113,9 @@ void validDataElementMergeTest() { .validate() .statusCode(200) .body("httpStatus", equalTo("OK")) - .body("response.mergeReport.message", equalTo("CATEGORY_OPTION merge complete")) + .body("response.mergeReport.message", equalTo("CategoryOption merge complete")) .body("response.mergeReport.mergeErrors", empty()) - .body("response.mergeReport.mergeType", equalTo("CATEGORY_OPTION")) + .body("response.mergeReport.mergeType", equalTo("CategoryOption")) .body("response.mergeReport.sourcesDeleted", hasItems(sourceUid1, sourceUid2)); categoryOptionApiActions.get(sourceUid1).validateStatus(404); diff --git a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/DataElementMergeTest.java b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/DataElementMergeTest.java index 6e43ee1f3bad..c66af88bf65f 100644 --- a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/DataElementMergeTest.java +++ b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/DataElementMergeTest.java @@ -111,9 +111,9 @@ void validDataElementMergeTest() { .validate() .statusCode(200) .body("httpStatus", equalTo("OK")) - .body("response.mergeReport.message", equalTo("DATA_ELEMENT merge complete")) + .body("response.mergeReport.message", equalTo("DataElement merge complete")) .body("response.mergeReport.mergeErrors", empty()) - .body("response.mergeReport.mergeType", equalTo("DATA_ELEMENT")) + .body("response.mergeReport.mergeType", equalTo("DataElement")) .body("response.mergeReport.sourcesDeleted", hasItems(sourceUid1, sourceUid2)); // and all the following source data element references have been handled appropriately @@ -125,7 +125,7 @@ void validDataElementMergeTest() { @Test @Disabled( - "setup started failing on GitHub only 409 response, reason not know, e2e all passing locally") + "setup started failing on GitHub only 409 response, reason not known, e2e all passing locally") @DisplayName("DataElement merge fails when min max DE DB unique key constraint met") void dbConstraintMinMaxTest() { // given @@ -231,13 +231,13 @@ void invalidDataElementMergeValueType() { .validate() .statusCode(409) .body("httpStatus", equalTo("Conflict")) - .body("response.mergeReport.message", equalTo("DATA_ELEMENT merge has errors")) + .body("response.mergeReport.message", equalTo("DataElement merge has errors")) .body( "response.mergeReport.mergeErrors.message", allOf( hasItem( "All source ValueTypes must match target ValueType: `TEXT`. Other ValueTypes found: `[NUMBER]`"))) - .body("response.mergeReport.mergeErrors.errorCode", allOf(hasItem("E1554"))); + .body("response.mergeReport.mergeErrors.errorCode", allOf(hasItem("E1550"))); } @Test @@ -262,13 +262,13 @@ void invalidDataElementMergeDomainType() { .validate() .statusCode(409) .body("httpStatus", equalTo("Conflict")) - .body("response.mergeReport.message", equalTo("DATA_ELEMENT merge has errors")) + .body("response.mergeReport.message", equalTo("DataElement merge has errors")) .body( "response.mergeReport.mergeErrors.message", allOf( hasItem( "All source DataElementDomains must match target DataElementDomain: `AGGREGATE`. Other DataElementDomains found: `[TRACKER]`"))) - .body("response.mergeReport.mergeErrors.errorCode", allOf(hasItem("E1555"))); + .body("response.mergeReport.mergeErrors.errorCode", allOf(hasItem("E1551"))); } @Test diff --git a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorMergeTest.java b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorMergeTest.java index dd58732222f8..2d20c732dd32 100644 --- a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorMergeTest.java +++ b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorMergeTest.java @@ -130,9 +130,9 @@ void testValidIndicatorMerge() { .validate() .statusCode(200) .body("httpStatus", equalTo("OK")) - .body("response.mergeReport.message", equalTo("INDICATOR merge complete")) + .body("response.mergeReport.message", equalTo("Indicator merge complete")) .body("response.mergeReport.mergeErrors", empty()) - .body("response.mergeReport.mergeType", equalTo("INDICATOR")) + .body("response.mergeReport.mergeType", equalTo("Indicator")) .body("response.mergeReport.sourcesDeleted", hasItems(sourceUid1, sourceUid2)); // and all the following source indicator references have been handled appropriately diff --git a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorTypeMergeTest.java b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorTypeMergeTest.java index 6c09c4127313..53826638b3ac 100644 --- a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorTypeMergeTest.java +++ b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/merge/IndicatorTypeMergeTest.java @@ -150,9 +150,9 @@ void testValidMergeKeepSources() { .validate() .statusCode(200) .body("httpStatus", equalTo("OK")) - .body("response.mergeReport.message", equalTo("INDICATOR_TYPE merge complete")) + .body("response.mergeReport.message", equalTo("IndicatorType merge complete")) .body("response.mergeReport.mergeErrors", empty()) - .body("response.mergeReport.mergeType", equalTo("INDICATOR_TYPE")) + .body("response.mergeReport.mergeType", equalTo("IndicatorType")) .body("response.mergeReport.sourcesDeleted", empty()); // and sources & target exist @@ -230,9 +230,9 @@ void testValidMergeDeleteSources() { .validate() .statusCode(200) .body("httpStatus", equalTo("OK")) - .body("response.mergeReport.message", equalTo("INDICATOR_TYPE merge complete")) + .body("response.mergeReport.message", equalTo("IndicatorType merge complete")) .body("response.mergeReport.mergeErrors", empty()) - .body("response.mergeReport.mergeType", equalTo("INDICATOR_TYPE")) + .body("response.mergeReport.mergeType", equalTo("IndicatorType")) .body("response.mergeReport.sourcesDeleted", hasItems(indTypeUid1, indTypeUid2)); // and sources are deleted & target exists @@ -282,12 +282,12 @@ void testInvalidMergeNoSources() { .statusCode(409) .body("httpStatus", equalTo("Conflict")) .body("status", equalTo("WARNING")) - .body("response.mergeReport.message", equalTo("INDICATOR_TYPE merge has errors")) + .body("response.mergeReport.message", equalTo("IndicatorType merge has errors")) .body( "response.mergeReport.mergeErrors[0].message", - equalTo("At least one source indicator type must be specified")) + equalTo("At least one source IndicatorType must be specified")) .body("response.mergeReport.mergeErrors[0].errorCode", equalTo("E1530")) - .body("response.mergeReport.mergeType", equalTo("INDICATOR_TYPE")) + .body("response.mergeReport.mergeType", equalTo("IndicatorType")) .body("response.mergeReport.sourcesDeleted", empty()); } @@ -313,12 +313,12 @@ void testInvalidMergeNoTarget() { .statusCode(409) .body("httpStatus", equalTo("Conflict")) .body("status", equalTo("WARNING")) - .body("response.mergeReport.message", equalTo("INDICATOR_TYPE merge has errors")) + .body("response.mergeReport.message", equalTo("IndicatorType merge has errors")) .body( "response.mergeReport.mergeErrors[0].message", - equalTo("Target indicator type must be specified")) + equalTo("Target IndicatorType must be specified")) .body("response.mergeReport.mergeErrors[0].errorCode", equalTo("E1531")) - .body("response.mergeReport.mergeType", equalTo("INDICATOR_TYPE")) + .body("response.mergeReport.mergeType", equalTo("IndicatorType")) .body("response.mergeReport.sourcesDeleted", empty()); } @@ -354,12 +354,12 @@ void testInvalidMergeTargetInSources() { .statusCode(409) .body("httpStatus", equalTo("Conflict")) .body("status", equalTo("WARNING")) - .body("response.mergeReport.message", equalTo("INDICATOR_TYPE merge has errors")) + .body("response.mergeReport.message", equalTo("IndicatorType merge has errors")) .body( "response.mergeReport.mergeErrors[0].message", - equalTo("Target indicator type cannot be a source indicator type")) + equalTo("Target IndicatorType cannot be a source IndicatorType")) .body("response.mergeReport.mergeErrors[0].errorCode", equalTo("E1532")) - .body("response.mergeReport.mergeType", equalTo("INDICATOR_TYPE")) + .body("response.mergeReport.mergeType", equalTo("IndicatorType")) .body("response.mergeReport.sourcesDeleted", empty()); } @@ -378,9 +378,9 @@ void testInvalidMergeTargetAndSourcesDontExist() { .statusCode(409) .body("httpStatus", equalTo("Conflict")) .body("status", equalTo("WARNING")) - .body("response.mergeReport.message", equalTo("INDICATOR_TYPE merge has errors")) + .body("response.mergeReport.message", equalTo("IndicatorType merge has errors")) .body("response.mergeReport.mergeErrors.size()", equalTo(3)) - .body("response.mergeReport.mergeType", equalTo("INDICATOR_TYPE")) + .body("response.mergeReport.mergeType", equalTo("IndicatorType")) .body("response.mergeReport.sourcesDeleted", empty()); } diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsServiceTest.java index 60a4b5bbd74e..3ca3e385d074 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsServiceTest.java @@ -115,7 +115,6 @@ import org.hisp.dhis.program.ProgramTrackedEntityAttribute; import org.hisp.dhis.scheduling.JobProgress; import org.hisp.dhis.security.acl.AccessStringHelper; -import org.hisp.dhis.setting.SystemSettingsService; import org.hisp.dhis.test.integration.PostgresIntegrationTestBase; import org.hisp.dhis.trackedentity.TrackedEntity; import org.hisp.dhis.trackedentity.TrackedEntityAttribute; @@ -172,8 +171,6 @@ class EventAnalyticsServiceTest extends PostgresIntegrationTestBase { @Autowired private CategoryService categoryService; - @Autowired private SystemSettingsService settingsService; - private OrganisationUnit ouA; private OrganisationUnit ouB; diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/category/CategoryOptionMergeProcessorTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/category/CategoryOptionMergeServiceTest.java similarity index 96% rename from dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/category/CategoryOptionMergeProcessorTest.java rename to dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/category/CategoryOptionMergeServiceTest.java index de4eac03720b..b7b350563eb3 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/category/CategoryOptionMergeProcessorTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/category/CategoryOptionMergeServiceTest.java @@ -50,6 +50,7 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.merge.MergeParams; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.organisationunit.OrganisationUnitService; import org.hisp.dhis.test.config.QueryCountDataSourceProxy; @@ -73,13 +74,13 @@ */ @Transactional @ContextConfiguration(classes = {QueryCountDataSourceProxy.class}) -class CategoryOptionMergeProcessorTest extends PostgresIntegrationTestBase { +class CategoryOptionMergeServiceTest extends PostgresIntegrationTestBase { @Autowired private CategoryService categoryService; @Autowired private OrganisationUnitService organisationUnitService; @Autowired private CategoryDimensionStore dimensionStore; @Autowired private IdentifiableObjectManager manager; - @Autowired private CategoryOptionMergeProcessor mergeProcessor; + @Autowired private MergeService categoryOptionMergeService; private Category cat1; private Category cat2; @@ -145,7 +146,7 @@ void categoryRefsReplacedSourcesNotDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List categorySources = @@ -182,7 +183,7 @@ void categoryRefsReplacedSourcesDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); mergeParams.setDeleteSources(true); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List categorySources = @@ -229,7 +230,7 @@ void catOptComboRefsReplacedSourcesNotDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List sourceCocs = @@ -263,7 +264,7 @@ void catOptMergeQueryTest() throws ConflictException { mergeParams.setDeleteSources(true); SQLStatementCountValidator.reset(); - mergeProcessor.processMerge(mergeParams); + categoryOptionMergeService.processMerge(mergeParams); // then assertDeleteCount(1); @@ -297,7 +298,7 @@ void catOptComboRefsReplacedSourcesDeletedTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); mergeParams.setDeleteSources(true); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List sourceCocs = @@ -340,7 +341,7 @@ void catOptComboSourceAndTargetRefsTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List sourceCocs = @@ -390,7 +391,7 @@ void orgUnitRefsReplacedSourcesNotDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List orgUnitSources = @@ -433,7 +434,7 @@ void orgUnitRefsReplacedSourcesDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); mergeParams.setDeleteSources(true); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List orgUnitSources = @@ -476,7 +477,7 @@ void orgUnitSourceAndTargetRefsTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List sourceOus = @@ -525,7 +526,7 @@ void catOptionGroupSourcesNotDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List cogSources = @@ -568,7 +569,7 @@ void catOptionGroupSourcesDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); mergeParams.setDeleteSources(true); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List cogSources = @@ -610,7 +611,7 @@ void catOptGroupSourceAndTargetRefsTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List sourceCogs = @@ -657,7 +658,7 @@ void catDimensionSourcesNotDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List cogSources = @@ -698,7 +699,7 @@ void catDimensionSourcesDeletedTest() throws ConflictException { // when MergeParams mergeParams = getMergeParams(); mergeParams.setDeleteSources(true); - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = categoryOptionMergeService.processMerge(mergeParams); // then List cdSources = diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeProcessorTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeServiceTest.java similarity index 95% rename from dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeProcessorTest.java rename to dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeServiceTest.java index cb80dd06f7fe..da9b724bf67b 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeProcessorTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/dataelement/DataElementMergeServiceTest.java @@ -77,6 +77,7 @@ import org.hisp.dhis.mapping.MapView; import org.hisp.dhis.merge.DataMergeStrategy; import org.hisp.dhis.merge.MergeParams; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.minmax.MinMaxDataElement; import org.hisp.dhis.minmax.MinMaxDataElementStore; import org.hisp.dhis.organisationunit.OrganisationUnit; @@ -135,11 +136,11 @@ */ @TestInstance(Lifecycle.PER_CLASS) @Transactional -class DataElementMergeProcessorTest extends PostgresIntegrationTestBase { +class DataElementMergeServiceTest extends PostgresIntegrationTestBase { @Autowired private DataElementService dataElementService; @Autowired private PeriodService periodService; - @Autowired private DataElementMergeProcessor mergeProcessor; + @Autowired private MergeService dataElementMergeService; @Autowired private MinMaxDataElementStore minMaxDataElementStore; @Autowired private EventVisualizationStore eventVisualizationStore; @Autowired private AnalyticalObjectStore analyticalEventVizStore; @@ -165,7 +166,7 @@ class DataElementMergeProcessorTest extends PostgresIntegrationTestBase { @Autowired private DataDimensionItemStore dataDimensionItemStore; @Autowired private DataValueStore dataValueStore; @Autowired private DataValueAuditStore dataValueAuditStore; - @Autowired private EventChangeLogService teDataValueChangeLogService; + @Autowired private EventChangeLogService eventChangeLogService; private DataElement deSource1; private DataElement deSource2; @@ -247,7 +248,7 @@ void minMaxDataElementMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List minMaxSources = @@ -282,7 +283,7 @@ void minMaxDataElementMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List minMaxSources = @@ -318,7 +319,7 @@ void eventVisualizationMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List eventVizSources = @@ -353,7 +354,7 @@ void eventVisualizationMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List eventVizSources = @@ -422,7 +423,7 @@ void trackedEntityDataElDimMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then // event viz @@ -498,7 +499,7 @@ void trackedEntityDataElDimMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then // event viz @@ -542,7 +543,7 @@ void smsCodeMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List smsCommandSources = @@ -574,7 +575,7 @@ void smsCodesMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List smsCommandSources = @@ -605,7 +606,7 @@ void predictorMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List predictorSources = @@ -638,7 +639,7 @@ void predictorMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List predictorSources = @@ -665,7 +666,7 @@ void predictorGeneratorMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List predictorSources = @@ -696,7 +697,7 @@ void predictorGeneratorMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List predictorSources = @@ -725,7 +726,7 @@ void predictorSampleSkipTestMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List predictorSources = @@ -757,7 +758,7 @@ void predictorSampleSkipTestMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List predictorSources = @@ -795,7 +796,7 @@ void programStageDEMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List psdeSources = @@ -829,7 +830,7 @@ void programStageDEMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List psdeSources = @@ -864,7 +865,7 @@ void programStageSectionMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List pssSources = @@ -897,7 +898,7 @@ void programStageSectionMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List pssSources = @@ -932,7 +933,7 @@ void programNotificationTemplateMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List pntSources = @@ -965,7 +966,7 @@ void programNotificationTemplateDeleteSourcesMergeTest() throws ConflictExceptio mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List pntSources = @@ -1000,7 +1001,7 @@ void programRuleVariableMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List prvSources = @@ -1033,7 +1034,7 @@ void programRuleVariableSourcesDeletedMergeTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List prvSources = @@ -1068,7 +1069,7 @@ void programRuleActionMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List prvSources = @@ -1100,7 +1101,7 @@ void programRuleActionSourcesDeletedMergeTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List prvSources = @@ -1134,7 +1135,7 @@ void programIndicatorExpressionMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List piSources = @@ -1175,7 +1176,7 @@ void programIndicatorExpressionMergeSourcesDeletedTest() throws ConflictExceptio mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List piSources = @@ -1211,7 +1212,7 @@ void programIndicatorFilterMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List piSources = @@ -1245,7 +1246,7 @@ void programIndicatorFilterMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List piSources = @@ -1316,7 +1317,7 @@ void eventMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List eventSources = @@ -1402,7 +1403,7 @@ void eventMergeDiscardTest() throws ConflictException { mergeParams.setDataMergeStrategy(DataMergeStrategy.DISCARD); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List eventSources = @@ -1495,7 +1496,7 @@ void eventMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List eventSources = @@ -1549,7 +1550,7 @@ void dataElementOperandMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List deoSources = @@ -1582,7 +1583,7 @@ void dataElementOperandSourcesDeletedMergeTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List deoSources = @@ -1632,7 +1633,7 @@ void dataSetElementTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List dseSources = @@ -1694,7 +1695,7 @@ void dataSetElementDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List dseSources = @@ -1751,7 +1752,7 @@ void programSectionMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List
sectionSources = @@ -1781,7 +1782,7 @@ void programSectionMergeDeleteSourcesTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List
sectionSources = @@ -1814,7 +1815,7 @@ void dataElementGroupMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List degSources = @@ -1845,7 +1846,7 @@ void dataElementGroupMergeSourceDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List degSources = @@ -1881,7 +1882,7 @@ void indicatorNumeratorMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceIndicators1 = @@ -1927,7 +1928,7 @@ void indicatorNumeratorMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceIndicators1 = @@ -1968,7 +1969,7 @@ void indicatorDenominatorMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceIndicators1 = @@ -2007,7 +2008,7 @@ void indicatorDenominatorMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceIndicators1 = @@ -2052,7 +2053,7 @@ void indicatorNumeratorDenominatorMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then Set sourceIndicators1 = @@ -2103,7 +2104,7 @@ void indicatorNumeratorDenominatorMergeSourcesDeletedTest() throws ConflictExcep mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then Set sourceIndicators1 = @@ -2145,7 +2146,7 @@ void formHtmlMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then Set sourceForms = @@ -2187,7 +2188,7 @@ void formHtmlMergeSourceDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then Set sourceForms = @@ -2227,7 +2228,7 @@ void dataDimItemMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceItems = @@ -2260,7 +2261,7 @@ void dataDimItemMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceItems = @@ -2303,7 +2304,7 @@ void dataValueMergeLastUpdatedTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceItems = @@ -2340,7 +2341,7 @@ void duplicateDataValueMergeLastUpdatedTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then there should be no source data values present List sourceItems = @@ -2389,7 +2390,7 @@ void dataValueMergeDiscardTest() throws ConflictException { mergeParams.setDataMergeStrategy(DataMergeStrategy.DISCARD); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceItems = @@ -2433,7 +2434,7 @@ void dataValueMergeSourcesDeletedTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then List sourceItems = @@ -2473,7 +2474,7 @@ void dataValueAuditMergeTest() throws ConflictException { MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then DataValueAuditQueryParams sourceDvaQueryParams = @@ -2519,7 +2520,7 @@ void dataValueAuditMergeDeleteTest() throws ConflictException { mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then DataValueAuditQueryParams sourceDvaQueryParams = @@ -2564,17 +2565,17 @@ void trackedEntityDataValueChangeLogMergeTest() throws ConflictException { TrackedEntityDataValueChangeLog tedvcl4 = createTrackedEntityDataValueAudit(e, deSource2, "2"); TrackedEntityDataValueChangeLog tedvcl5 = createTrackedEntityDataValueAudit(e, deTarget, "1"); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl1); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl2); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl3); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl4); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl5); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl1); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl2); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl3); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl4); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl5); // params MergeParams mergeParams = getMergeParams(); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then TrackedEntityDataValueChangeLogQueryParams sourceTeDvChangeLogQuery = @@ -2583,9 +2584,9 @@ void trackedEntityDataValueChangeLogMergeTest() throws ConflictException { getTeQueryParams(e, List.of(deTarget)); List sourceAudits = - teDataValueChangeLogService.getTrackedEntityDataValueChangeLogs(sourceTeDvChangeLogQuery); + eventChangeLogService.getTrackedEntityDataValueChangeLogs(sourceTeDvChangeLogQuery); List targetAudits = - teDataValueChangeLogService.getTrackedEntityDataValueChangeLogs(targeteDvChangeLogQuery); + eventChangeLogService.getTrackedEntityDataValueChangeLogs(targeteDvChangeLogQuery); List allDataElements = dataElementService.getAllDataElements(); @@ -2617,18 +2618,18 @@ void trackedEntityDataValueChangeLogMergeDeletedTest() throws ConflictException TrackedEntityDataValueChangeLog tedvcl4 = createTrackedEntityDataValueAudit(e, deSource2, "2"); TrackedEntityDataValueChangeLog tedvcl5 = createTrackedEntityDataValueAudit(e, deTarget, "1"); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl1); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl2); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl3); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl4); - teDataValueChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl5); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl1); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl2); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl3); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl4); + eventChangeLogService.addTrackedEntityDataValueChangeLog(tedvcl5); // params MergeParams mergeParams = getMergeParams(); mergeParams.setDeleteSources(true); // when - MergeReport report = mergeProcessor.processMerge(mergeParams); + MergeReport report = dataElementMergeService.processMerge(mergeParams); // then TrackedEntityDataValueChangeLogQueryParams sourceTeDvChangeLogQuery = @@ -2637,9 +2638,9 @@ void trackedEntityDataValueChangeLogMergeDeletedTest() throws ConflictException getTeQueryParams(e, List.of(deTarget)); List sourceAudits = - teDataValueChangeLogService.getTrackedEntityDataValueChangeLogs(sourceTeDvChangeLogQuery); + eventChangeLogService.getTrackedEntityDataValueChangeLogs(sourceTeDvChangeLogQuery); List targetAudits = - teDataValueChangeLogService.getTrackedEntityDataValueChangeLogs(targeteDvChangeLogQuery); + eventChangeLogService.getTrackedEntityDataValueChangeLogs(targeteDvChangeLogQuery); List allDataElements = dataElementService.getAllDataElements(); diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/indicator/IndicatorMergeProcessorTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorMergeServiceTest.java similarity index 92% rename from dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/indicator/IndicatorMergeProcessorTest.java rename to dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorMergeServiceTest.java index cbb0e3a75438..a26a65d8bc74 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/indicator/IndicatorMergeProcessorTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorMergeServiceTest.java @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.indicator; +package org.hisp.dhis.merge.indicator; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -48,9 +48,11 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ErrorMessage; import org.hisp.dhis.feedback.MergeReport; +import org.hisp.dhis.indicator.Indicator; +import org.hisp.dhis.indicator.IndicatorGroup; +import org.hisp.dhis.indicator.IndicatorType; import org.hisp.dhis.merge.MergeParams; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeType; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.period.PeriodType; import org.hisp.dhis.period.PeriodTypeEnum; import org.hisp.dhis.test.integration.PostgresIntegrationTestBase; @@ -62,8 +64,8 @@ /** * @author david mackessy */ -class IndicatorMergeProcessorTest extends PostgresIntegrationTestBase { - @Autowired private MergeProcessor indicatorMergeProcessor; +class IndicatorMergeServiceTest extends PostgresIntegrationTestBase { + @Autowired private MergeService indicatorMergeService; @Autowired private IdentifiableObjectManager manager; @Autowired private ConfigurationService configService; @@ -91,18 +93,17 @@ void mergeWithNoSourcesTest() { MergeParams params = new MergeParams(); params.setSources(Set.of()); params.setTarget(UID.of(validTarget.getUid())); - params.setMergeType(MergeType.INDICATOR); // when a merge request is processed ConflictException conflictException = - assertThrows(ConflictException.class, () -> indicatorMergeProcessor.processMerge(params)); + assertThrows(ConflictException.class, () -> indicatorMergeService.processMerge(params)); // then the merge report has the correct error info MergeReport mergeReport = conflictException.getMergeReport(); List list = mergeReport.getMergeErrors().stream().map(ErrorMessage::getMessage).toList(); assertEquals(1, list.size()); - assertTrue(list.contains("At least one source indicator must be specified")); + assertTrue(list.contains("At least one source Indicator must be specified")); } @Test @@ -117,18 +118,17 @@ void mergeWithInvalidTargetTest() { MergeParams params = new MergeParams(); params.setSources(Set.of(UID.of(validSource1.getUid()))); params.setTarget(UID.of("Uid00000011")); - params.setMergeType(MergeType.INDICATOR); // when a merge request is processed ConflictException conflictException = - assertThrows(ConflictException.class, () -> indicatorMergeProcessor.processMerge(params)); + assertThrows(ConflictException.class, () -> indicatorMergeService.processMerge(params)); // then the merge report has the correct error info MergeReport mergeReport = conflictException.getMergeReport(); List list = mergeReport.getMergeErrors().stream().map(ErrorMessage::getMessage).toList(); assertEquals(1, list.size()); - assertTrue(list.contains("TARGET indicator does not exist: `Uid00000011`")); + assertTrue(list.contains("TARGET Indicator does not exist: `Uid00000011`")); } @Test @@ -145,18 +145,17 @@ void mergeWithInvalidSourceTest() { MergeParams params = new MergeParams(); params.setSources(UID.of(validSource1.getUid(), "Uid00000011")); params.setTarget(UID.of(validTarget.getUid())); - params.setMergeType(MergeType.INDICATOR); // when a merge request is processed ConflictException conflictException = - assertThrows(ConflictException.class, () -> indicatorMergeProcessor.processMerge(params)); + assertThrows(ConflictException.class, () -> indicatorMergeService.processMerge(params)); // then the merge report has the correct error info MergeReport mergeReport = conflictException.getMergeReport(); List list = mergeReport.getMergeErrors().stream().map(ErrorMessage::getMessage).toList(); assertEquals(1, list.size()); - assertTrue(list.contains("SOURCE indicator does not exist: `Uid00000011`")); + assertTrue(list.contains("SOURCE Indicator does not exist: `Uid00000011`")); } @Test @@ -171,11 +170,10 @@ void mergeWithTargetAsSourceTest() { MergeParams params = new MergeParams(); params.setSources(Set.of(UID.of(validTarget.getUid()))); params.setTarget(UID.of(validTarget.getUid())); - params.setMergeType(MergeType.INDICATOR); // when a merge request is processed ConflictException conflictException = - assertThrows(ConflictException.class, () -> indicatorMergeProcessor.processMerge(params)); + assertThrows(ConflictException.class, () -> indicatorMergeService.processMerge(params)); // then the merge report has the correct error info MergeReport mergeReport = conflictException.getMergeReport(); @@ -183,7 +181,7 @@ void mergeWithTargetAsSourceTest() { mergeReport.getMergeErrors().stream().map(ErrorMessage::getMessage).toList(); assertEquals(1, list.size()); - assertTrue(list.contains("Target indicator cannot be a source indicator")); + assertTrue(list.contains("Target Indicator cannot be a source Indicator")); } @Test @@ -198,18 +196,17 @@ void mergeWithNoTargetTest() { MergeParams params = new MergeParams(); params.setSources(Set.of(UID.of(validTarget.getUid()))); params.setTarget(null); - params.setMergeType(MergeType.INDICATOR); // when a merge request is processed ConflictException conflictException = - assertThrows(ConflictException.class, () -> indicatorMergeProcessor.processMerge(params)); + assertThrows(ConflictException.class, () -> indicatorMergeService.processMerge(params)); // then the merge report has the correct error info MergeReport mergeReport = conflictException.getMergeReport(); List list = mergeReport.getMergeErrors().stream().map(ErrorMessage::getMessage).toList(); assertEquals(1, list.size()); - assertTrue(list.contains("Target indicator must be specified")); + assertTrue(list.contains("Target Indicator must be specified")); } @Test @@ -222,10 +219,9 @@ void validMergeTest() throws ConflictException { params.setSources(UID.of(validSource1.getUid(), validSource2.getUid())); params.setTarget(UID.of(validTarget.getUid())); params.setDeleteSources(true); - params.setMergeType(MergeType.INDICATOR); // when a merge request is processed - MergeReport report = indicatorMergeProcessor.processMerge(params); + MergeReport report = indicatorMergeService.processMerge(params); // then the merge report has the correct error info assertFalse(report.hasErrorMessages()); diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeServiceTest.java index edf97a3870e3..cf7a3a73b71c 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/merge/indicator/IndicatorTypeMergeServiceTest.java @@ -46,7 +46,6 @@ import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeRequest; import org.hisp.dhis.merge.MergeService; -import org.hisp.dhis.merge.MergeType; import org.hisp.dhis.test.integration.PostgresIntegrationTestBase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -96,7 +95,7 @@ void testGetFromParams() { params.setSources(Set.of(uidA, uidB)); params.setTarget(uidC); params.setDeleteSources(true); - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); // when MergeRequest request = indicatorTypeMergeService.validate(params, mergeReport); @@ -115,7 +114,7 @@ void testGetFromParams() { void testGetFromParamsWithErrors() { // given MergeParams params = new MergeParams(); - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); // when MergeRequest request = indicatorTypeMergeService.validate(params, mergeReport); @@ -127,8 +126,8 @@ void testGetFromParamsWithErrors() { assertMatchesErrorMessages( mergeReport, Set.of( - "At least one source indicator type must be specified", - "Target indicator type must be specified")); + "At least one source IndicatorType must be specified", + "Target IndicatorType must be specified")); } @Test @@ -139,7 +138,7 @@ void testSourceNotFound() { MergeParams params = new MergeParams(); params.setSources(Set.of(uidA, uidX)); params.setTarget(uidC); - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); // when MergeRequest request = indicatorTypeMergeService.validate(params, mergeReport); @@ -150,7 +149,7 @@ void testSourceNotFound() { assertTrue(mergeReport.hasErrorMessages()); assertMatchesErrorCodes(mergeReport, Set.of(ErrorCode.E1533)); assertMatchesErrorMessages( - mergeReport, Set.of("SOURCE indicator type does not exist: `IntY123abgX`")); + mergeReport, Set.of("SOURCE IndicatorType does not exist: `IntY123abgX`")); } @Test @@ -161,7 +160,7 @@ void testTargetNotFound() { MergeParams params = new MergeParams(); params.setSources(Set.of(uidA, uidB)); params.setTarget(uidX); - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); // when MergeRequest request = indicatorTypeMergeService.validate(params, mergeReport); @@ -171,7 +170,7 @@ void testTargetNotFound() { assertTrue(mergeReport.hasErrorMessages()); assertMatchesErrorCodes(mergeReport, Set.of(ErrorCode.E1533)); assertMatchesErrorMessages( - mergeReport, Set.of("TARGET indicator type does not exist: `IntY123abgX`")); + mergeReport, Set.of("TARGET IndicatorType does not exist: `IntY123abgX`")); } @Test @@ -197,7 +196,7 @@ void testValidate() { params.setDeleteSources(true); // when an indicator merge request is validated - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); indicatorTypeMergeService.validate(params, mergeReport); // then @@ -226,7 +225,7 @@ void testValidateWithErrorNoSources() { params.setDeleteSources(true); // when an indicator merge request is validated - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); MergeRequest validatedRequest = indicatorTypeMergeService.validate(params, mergeReport); // then @@ -240,7 +239,7 @@ void testValidateWithErrorNoSources() { assertTrue(validatedRequest.isDeleteSources()); assertMatchesErrorCodes(mergeReport, Set.of(ErrorCode.E1530)); assertMatchesErrorMessages( - mergeReport, Set.of("At least one source indicator type must be specified")); + mergeReport, Set.of("At least one source IndicatorType must be specified")); } @Test @@ -262,7 +261,7 @@ void testValidateWithErrorNoTarget() { params.setDeleteSources(true); // when an indicator merge request is validated - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); MergeRequest validatedRequest = indicatorTypeMergeService.validate(params, mergeReport); // then @@ -274,7 +273,7 @@ void testValidateWithErrorNoTarget() { assertTrue(mergeReport.hasErrorMessages()); assertRequestIsEmpty(validatedRequest); assertMatchesErrorCodes(mergeReport, Set.of(ErrorCode.E1531)); - assertMatchesErrorMessages(mergeReport, Set.of("Target indicator type must be specified")); + assertMatchesErrorMessages(mergeReport, Set.of("Target IndicatorType must be specified")); } @Test @@ -298,7 +297,7 @@ void testValidMergeDeleteSources() { MergeRequest.builder().sources(Set.of(uidA, uidB)).target(uidC).deleteSources(true).build(); // when an indicator merge request is merged - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); MergeReport completeReport = indicatorTypeMergeService.merge(request, mergeReport); // then @@ -345,7 +344,7 @@ void testValidMergeKeepSources() { .build(); // when an indicator merge request is merged - MergeReport mergeReport = new MergeReport(MergeType.INDICATOR_TYPE); + MergeReport mergeReport = new MergeReport(); MergeReport completeReport = indicatorTypeMergeService.merge(request, mergeReport); // then diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventChangeLogServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventChangeLogServiceTest.java index 0f5e7e43031a..df2f3efb3deb 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventChangeLogServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventChangeLogServiceTest.java @@ -27,6 +27,7 @@ */ package org.hisp.dhis.tracker.export.event; +import static org.hisp.dhis.changelog.ChangeLogType.UPDATE; import static org.hisp.dhis.tracker.Assertions.assertNoErrors; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,6 +40,7 @@ import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.UID; +import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundle; import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundleMode; import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundleParams; @@ -54,7 +56,6 @@ import org.hisp.dhis.test.integration.PostgresIntegrationTestBase; import org.hisp.dhis.tracker.export.Page; import org.hisp.dhis.tracker.export.PageParams; -import org.hisp.dhis.tracker.export.event.EventChangeLog.DataValueChange; import org.hisp.dhis.tracker.imports.TrackerImportParams; import org.hisp.dhis.tracker.imports.TrackerImportService; import org.hisp.dhis.tracker.imports.bundle.persister.TrackerObjectDeletionService; @@ -102,12 +103,6 @@ void setUp() throws IOException { importParams, fromJson("tracker/event_and_enrollment.json"))); } - @BeforeEach - void setUpUser() { - importUser = userService.getUser("tTgjgobT1oS"); - injectSecurityContextUser(importUser); - } - @Test void shouldFailWhenEventDoesNotExist() { assertThrows( @@ -268,6 +263,29 @@ void shouldReturnChangeLogsWhenDataValueIsCreatedUpdatedAndDeleted() () -> assertCreate(dataElement, "15", changeLogs.getItems().get(2))); } + @Test + void shouldReturnOnlyUserNameWhenUserDoesNotExistInDatabase() + throws ForbiddenException, NotFoundException { + Event event = getEvent("QRYjLTiJTrA"); + String dataElementUid = event.getEventDataValues().iterator().next().getDataElement(); + DataElement dataElement = manager.get(DataElement.class, dataElementUid); + User deletedUser = new User(); + deletedUser.setUsername("deletedUsername"); + eventChangeLogService.addDataValueChangeLog( + event, dataElement, "current", "previous", UPDATE, deletedUser.getUsername()); + + Page changeLogs = + eventChangeLogService.getEventChangeLog( + UID.of("QRYjLTiJTrA"), defaultOperationParams, defaultPageParams); + + assertNumberOfChanges(2, changeLogs.getItems()); + assertAll( + () -> + assertUpdate( + dataElementUid, "previous", "current", changeLogs.getItems().get(0), deletedUser), + () -> assertCreate(dataElementUid, "15", changeLogs.getItems().get(1))); + } + private void updateDataValue(String event, String dataElementUid, String newValue) throws IOException { TrackerObjects trackerObjects = fromJson("tracker/event_and_enrollment.json"); @@ -314,40 +332,59 @@ private static void assertNumberOfChanges(int expected, List cha private void assertCreate(String dataElement, String currentValue, EventChangeLog changeLog) { assertAll( () -> assertUser(importUser, changeLog), - () -> assertEquals("CREATE", changeLog.type()), + () -> assertEquals("CREATE", changeLog.getChangeLogType().name()), () -> assertChange(dataElement, null, currentValue, changeLog)); } private void assertUpdate( String dataElement, String previousValue, String currentValue, EventChangeLog changeLog) { + assertUpdate(dataElement, previousValue, currentValue, changeLog, importUser); + } + + private void assertUpdate( + String dataElement, + String previousValue, + String currentValue, + EventChangeLog changeLog, + User user) { assertAll( - () -> assertUser(importUser, changeLog), - () -> assertEquals("UPDATE", changeLog.type()), + () -> assertUser(user, changeLog), + () -> assertEquals("UPDATE", changeLog.getChangeLogType().name()), () -> assertChange(dataElement, previousValue, currentValue, changeLog)); } private void assertDelete(String dataElement, String previousValue, EventChangeLog changeLog) { assertAll( () -> assertUser(importUser, changeLog), - () -> assertEquals("DELETE", changeLog.type()), + () -> assertEquals("DELETE", changeLog.getChangeLogType().name()), () -> assertChange(dataElement, previousValue, null, changeLog)); } private static void assertChange( String dataElement, String previousValue, String currentValue, EventChangeLog changeLog) { - DataValueChange expected = new DataValueChange(dataElement, previousValue, currentValue); - assertEquals(expected, changeLog.change().dataValue()); + assertEquals(dataElement, changeLog.getDataElement().getUid()); + assertEquals(currentValue, changeLog.getCurrentValue()); + assertEquals(previousValue, changeLog.getPreviousValue()); } private static void assertUser(User user, EventChangeLog changeLog) { assertAll( - () -> assertEquals(user.getUsername(), changeLog.createdBy().getUsername()), - () -> assertEquals(user.getFirstName(), changeLog.createdBy().getFirstName()), - () -> assertEquals(user.getSurname(), changeLog.createdBy().getSurname()), - () -> assertEquals(user.getUid(), changeLog.createdBy().getUid())); + () -> assertEquals(user.getUsername(), changeLog.getCreatedBy().getUsername()), + () -> + assertEquals( + user.getFirstName(), + changeLog.getCreatedBy() == null ? null : changeLog.getCreatedBy().getFirstName()), + () -> + assertEquals( + user.getSurname(), + changeLog.getCreatedBy() == null ? null : changeLog.getCreatedBy().getSurname()), + () -> + assertEquals( + user.getUid(), + changeLog.getCreatedBy() == null ? null : changeLog.getCreatedBy().getUid())); } - private ObjectBundle setUpMetadata(String path) throws IOException { + private void setUpMetadata(String path) throws IOException { Map, List> metadata = renderService.fromMetadata(new ClassPathResource(path).getInputStream(), RenderFormat.JSON); ObjectBundleParams params = new ObjectBundleParams(); @@ -357,7 +394,6 @@ private ObjectBundle setUpMetadata(String path) throws IOException { ObjectBundle bundle = objectBundleService.create(params); Assertions.assertNoErrors(objectBundleValidationService.validate(bundle)); objectBundleService.commit(bundle); - return bundle; } private TrackerObjects fromJson(String path) throws IOException { diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/imports/bundle/TrackedEntityDataValueChangeLogTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/imports/bundle/TrackedEntityDataValueChangeLogTest.java deleted file mode 100644 index f5cfe98f153b..000000000000 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/imports/bundle/TrackedEntityDataValueChangeLogTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.tracker.imports.bundle; - -import static org.hisp.dhis.tracker.Assertions.assertNoErrors; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.io.IOException; -import java.util.List; -import org.hisp.dhis.changelog.ChangeLogType; -import org.hisp.dhis.common.IdentifiableObjectManager; -import org.hisp.dhis.dataelement.DataElement; -import org.hisp.dhis.program.Event; -import org.hisp.dhis.tracker.TrackerTest; -import org.hisp.dhis.tracker.export.event.EventChangeLogService; -import org.hisp.dhis.tracker.export.event.TrackedEntityDataValueChangeLog; -import org.hisp.dhis.tracker.export.event.TrackedEntityDataValueChangeLogQueryParams; -import org.hisp.dhis.tracker.imports.TrackerImportParams; -import org.hisp.dhis.tracker.imports.TrackerImportService; -import org.hisp.dhis.user.User; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * @author Zubair Asghar - */ -public class TrackedEntityDataValueChangeLogTest extends TrackerTest { - private static final String ORIGINAL_VALUE = "value1"; - - private static final String UPDATED_VALUE = "value1-updated"; - - private static final String PSI = "D9PbzJY8bJO"; - - public static final String DE = "DATAEL00001"; - - @Autowired private TrackerImportService trackerImportService; - - @Autowired private IdentifiableObjectManager manager; - - @Autowired private EventChangeLogService eventChangeLogService; - - private DataElement dataElement; - - private Event event; - - @BeforeAll - void setUp() throws IOException { - setUpMetadata("tracker/simple_metadata.json"); - - User importUser = userService.getUser("tTgjgobT1oS"); - injectSecurityContextUser(importUser); - } - - @Test - void testTrackedEntityDataValueAuditCreate() throws IOException { - TrackerImportParams params = new TrackerImportParams(); - assertNoErrors( - trackerImportService.importTracker( - params, fromJson("tracker/event_and_enrollment_with_data_values.json"))); - assertNoErrors( - trackerImportService.importTracker( - params, fromJson("tracker/event_with_data_values_for_update_audit.json"))); - assertNoErrors( - trackerImportService.importTracker( - params, fromJson("tracker/event_with_data_values_for_delete_audit.json"))); - - dataElement = manager.search(DataElement.class, DE); - event = manager.search(Event.class, PSI); - assertNotNull(dataElement); - assertNotNull(event); - - List createdAudit = - eventChangeLogService.getTrackedEntityDataValueChangeLogs( - new TrackedEntityDataValueChangeLogQueryParams() - .setDataElements(List.of(dataElement)) - .setEvents(List.of(event)) - .setAuditTypes(List.of(ChangeLogType.CREATE))); - List updatedAudit = - eventChangeLogService.getTrackedEntityDataValueChangeLogs( - new TrackedEntityDataValueChangeLogQueryParams() - .setDataElements(List.of(dataElement)) - .setEvents(List.of(event)) - .setAuditTypes(List.of(ChangeLogType.UPDATE))); - List deletedAudit = - eventChangeLogService.getTrackedEntityDataValueChangeLogs( - new TrackedEntityDataValueChangeLogQueryParams() - .setDataElements(List.of(dataElement)) - .setEvents(List.of(event)) - .setAuditTypes(List.of(ChangeLogType.DELETE))); - - assertAll( - () -> assertNotNull(createdAudit), - () -> assertNotNull(updatedAudit), - () -> assertNotNull(deletedAudit)); - assertAuditCollection(createdAudit, ChangeLogType.CREATE, ORIGINAL_VALUE); - assertAuditCollection(updatedAudit, ChangeLogType.UPDATE, ORIGINAL_VALUE); - assertAuditCollection(deletedAudit, ChangeLogType.DELETE, UPDATED_VALUE); - } - - private void assertAuditCollection( - List audits, - ChangeLogType changeLogType, - String expectedValue) { - assertAll( - () -> assertFalse(audits.isEmpty()), - () -> - assertEquals( - changeLogType, - audits.get(0).getAuditType(), - () -> - "Expected audit type is " - + changeLogType - + " but found " - + audits.get(0).getAuditType()), - () -> - assertEquals( - audits.get(0).getDataElement().getUid(), - dataElement.getUid(), - () -> - "Expected dataElement is " - + dataElement.getUid() - + " but found " - + audits.get(0).getDataElement().getUid()), - () -> - assertEquals( - expectedValue, - audits.get(0).getValue(), - () -> - "Expected value is " - + expectedValue - + " but found " - + audits.get(0).getValue())); - } -} diff --git a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment_with_data_values.json b/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment_with_data_values.json deleted file mode 100644 index e7b1f8e69ae8..000000000000 --- a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment_with_data_values.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "importMode": "COMMIT", - "idSchemes": { - "dataElementIdScheme": { - "idScheme": "UID" - }, - "orgUnitIdScheme": { - "idScheme": "UID" - }, - "programIdScheme": { - "idScheme": "UID" - }, - "programStageIdScheme": { - "idScheme": "UID" - }, - "idScheme": { - "idScheme": "UID" - }, - "categoryOptionComboIdScheme": { - "idScheme": "UID" - }, - "categoryOptionIdScheme": { - "idScheme": "UID" - } - }, - "importStrategy": "CREATE", - "atomicMode": "ALL", - "flushMode": "AUTO", - "validationMode": "FULL", - "skipPatternValidation": false, - "skipSideEffects": false, - "skipRuleEngine": false, - "trackedEntities": [ - { - "trackedEntity": "IOR1AXXl24H", - "trackedEntityType": { - "idScheme": "UID", - "identifier": "ja8NY4PW7Xm" - }, - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "inactive": false, - "deleted": false, - "potentialDuplicate": false, - "relationships": [], - "attributes": [], - "enrollments": [] - }, - { - "trackedEntity": "IOR1AXXl24G", - "trackedEntityType": { - "idScheme": "UID", - "identifier": "ja8NY4PW7Xm" - }, - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "inactive": false, - "deleted": false, - "potentialDuplicate": false, - "relationships": [], - "attributes": [], - "enrollments": [] - } - ], - "enrollments": [ - { - "enrollment": "TvctPPhpD8u", - "createdAtClient": "2017-01-26T13:48:13.363", - "trackedEntity": "IOR1AXXl24H", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "status": "ACTIVE", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "orgUnitName": "Mbokie CHP", - "enrolledAt": "2021-03-28T12:05:00.000", - "occurredAt": "2021-03-28T12:05:00.000", - "followUp": false, - "deleted": false, - "events": [], - "relationships": [], - "attributes": [], - "notes": [] - }, - { - "enrollment": "TvctPPhpD8z", - "createdAtClient": "2017-01-26T13:48:13.363", - "trackedEntity": "IOR1AXXl24G", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "status": "ACTIVE", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "orgUnitName": "Mbokie CHP", - "enrolledAt": "2021-03-28T12:05:00.000", - "occurredAt": "2021-03-28T12:05:00.000", - "followUp": false, - "deleted": false, - "events": [], - "relationships": [], - "attributes": [], - "notes": [] - } - ], - "events": [ - { - "event": "D9PbzJY8bJO", - "status": "COMPLETED", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "programStage": { - "idScheme": "UID", - "identifier": "NpsdDv6kKSO" - }, - "enrollment": "TvctPPhpD8u", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "relationships": [], - "occurredAt": "2019-01-28T00:00:00.000", - "scheduledAt": "2019-01-28T12:10:38.100", - "storedBy": "admin", - "deleted": false, - "attributeOptionCombo": { - "idScheme": "UID", - "identifier": "HllvX50cXC0" - }, - "attributeCategoryOptions": [ - { - "idScheme": "UID", - "identifier": "xYerKDKCefk" - } - ], - - "dataValues": [{ - "createdAt": "2019-01-28T12:10:38.113", - "storedBy": "admin", - "providedElsewhere": false, - "dataElement": { - "idScheme": "UID", - "identifier": "DATAEL00001" - }, - "value": "value1" - }, - { - "createdAt": "2019-01-28T12:10:38.113", - "storedBy": "admin", - "providedElsewhere": false, - "dataElement": { - "idScheme": "UID", - "identifier": "DATAEL00002" - }, - "value": "value2" - }], - "notes": [] - }, - { - "event": "D9PbzJY8bJM", - "status": "COMPLETED", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "programStage": { - "idScheme": "UID", - "identifier": "NpsdDv6kKSO" - }, - "enrollment": "TvctPPhpD8z", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "relationships": [], - "occurredAt": "2019-01-28T00:00:00.000", - "scheduledAt": "2019-01-28T12:10:38.100", - "storedBy": "admin", - "deleted": false, - "attributeOptionCombo": { - "idScheme": "UID", - "identifier": "HllvX50cXC0" - }, - "attributeCategoryOptions": [ - { - "idScheme": "UID", - "identifier": "xYerKDKCefk" - } - ], - - "dataValues": [], - "notes": [] - } - ], - "relationships": [], - "username": "system-process" -} \ No newline at end of file diff --git a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_with_data_values_for_delete_audit.json b/dhis-2/dhis-test-integration/src/test/resources/tracker/event_with_data_values_for_delete_audit.json deleted file mode 100644 index 1031568e6247..000000000000 --- a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_with_data_values_for_delete_audit.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "importMode": "COMMIT", - "idSchemes": { - "dataElementIdScheme": { - "idScheme": "UID" - }, - "orgUnitIdScheme": { - "idScheme": "UID" - }, - "programIdScheme": { - "idScheme": "UID" - }, - "programStageIdScheme": { - "idScheme": "UID" - }, - "idScheme": { - "idScheme": "UID" - }, - "categoryOptionComboIdScheme": { - "idScheme": "UID" - }, - "categoryOptionIdScheme": { - "idScheme": "UID" - } - }, - "importStrategy": "CREATE_AND_UPDATE", - "atomicMode": "ALL", - "flushMode": "AUTO", - "validationMode": "FULL", - "skipPatternValidation": false, - "skipSideEffects": false, - "skipRuleEngine": false, - "trackedEntities": [], - "enrollments": [ - { - "enrollment": "TvctPPhpD8u", - "createdAtClient": "2017-01-26T13:48:13.363", - "trackedEntity": "IOR1AXXl24H", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "status": "ACTIVE", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "enrolledAt": "2021-03-28T12:05:00.000", - "occurredAt": "2021-03-28T12:05:00.000", - "followUp": false, - "deleted": false, - "events": [], - "relationships": [], - "attributes": [], - "notes": [] - } - ], - "events": [ - { - "event": "D9PbzJY8bJO", - "status": "COMPLETED", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "programStage": { - "idScheme": "UID", - "identifier": "NpsdDv6kKSO" - }, - "enrollment": "TvctPPhpD8u", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "relationships": [], - "occurredAt": "2019-01-28T00:00:00.000", - "scheduledAt": "2019-01-28T12:10:38.100", - "storedBy": "admin", - "deleted": false, - "attributeOptionCombo": { - "idScheme": "UID", - "identifier": "HllvX50cXC0" - }, - "attributeCategoryOptions": [ - { - "idScheme": "UID", - "identifier": "xYerKDKCefk" - } - ], - - "dataValues": [ - { - "createdAt": "2019-01-28T12:10:38.113", - "storedBy": "admin", - "providedElsewhere": false, - "dataElement": { - "idScheme": "UID", - "identifier": "DATAEL00001" - } - } - ], - "notes": [] - } - ], - "relationships": [], - "username": "system-process" -} \ No newline at end of file diff --git a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_with_data_values_for_update_audit.json b/dhis-2/dhis-test-integration/src/test/resources/tracker/event_with_data_values_for_update_audit.json deleted file mode 100644 index 42abeb8cd6e2..000000000000 --- a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_with_data_values_for_update_audit.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "importMode": "COMMIT", - "idSchemes": { - "dataElementIdScheme": { - "idScheme": "UID" - }, - "orgUnitIdScheme": { - "idScheme": "UID" - }, - "programIdScheme": { - "idScheme": "UID" - }, - "programStageIdScheme": { - "idScheme": "UID" - }, - "idScheme": { - "idScheme": "UID" - }, - "categoryOptionComboIdScheme": { - "idScheme": "UID" - }, - "categoryOptionIdScheme": { - "idScheme": "UID" - } - }, - "importStrategy": "CREATE_AND_UPDATE", - "atomicMode": "ALL", - "flushMode": "AUTO", - "validationMode": "FULL", - "skipPatternValidation": false, - "skipSideEffects": false, - "skipRuleEngine": false, - "trackedEntities": [], - "enrollments": [ - { - "enrollment": "TvctPPhpD8u", - "createdAtClient": "2017-01-26T13:48:13.363", - "trackedEntity": "IOR1AXXl24H", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "status": "ACTIVE", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "enrolledAt": "2021-03-28T12:05:00.000", - "occurredAt": "2021-03-28T12:05:00.000", - "followUp": false, - "deleted": false, - "events": [], - "relationships": [], - "attributes": [], - "notes": [] - } - ], - "events": [ - { - "event": "D9PbzJY8bJO", - "status": "COMPLETED", - "program": { - "idScheme": "UID", - "identifier": "BFcipDERJnf" - }, - "programStage": { - "idScheme": "UID", - "identifier": "NpsdDv6kKSO" - }, - "enrollment": "TvctPPhpD8u", - "orgUnit": { - "idScheme": "UID", - "identifier": "h4w96yEMlzO" - }, - "relationships": [], - "occurredAt": "2019-01-28T00:00:00.000", - "scheduledAt": "2019-01-28T12:10:38.100", - "storedBy": "admin", - "deleted": false, - "attributeOptionCombo": { - "idScheme": "UID", - "identifier": "HllvX50cXC0" - }, - "attributeCategoryOptions": [ - { - "idScheme": "UID", - "identifier": "xYerKDKCefk" - } - ], - - "dataValues": [ - { - "createdAt": "2019-01-28T12:10:38.113", - "storedBy": "admin", - "providedElsewhere": false, - "dataElement": { - "idScheme": "UID", - "identifier": "DATAEL00001" - }, - "value": "value1-updated" - }, - { - "createdAt": "2019-01-28T12:10:38.113", - "storedBy": "admin", - "providedElsewhere": false, - "dataElement": { - "idScheme": "UID", - "identifier": "DATAEL00002" - }, - "value": "value2" - } - ], - "notes": [] - } - ], - "relationships": [], - "username": "system-process" -} \ No newline at end of file diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/CategoryOptionControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/CategoryOptionControllerTest.java index b51f54286e87..fa6c8c86da18 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/CategoryOptionControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/CategoryOptionControllerTest.java @@ -122,10 +122,10 @@ void testInvalidMerge() { JsonObject error1 = errors.getObject(0); JsonObject error2 = errors.getObject(1); assertEquals( - "SOURCE category option does not exist: `Uid00000010`", + "SOURCE CategoryOption does not exist: `Uid00000010`", error1.getString("message").string()); assertEquals( - "TARGET category option does not exist: `Uid00000012`", + "TARGET CategoryOption does not exist: `Uid00000012`", error2.getString("message").string()); } diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/EventVisualizationControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/EventVisualizationControllerTest.java index 5dd4a025af9c..edb920fec7e9 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/EventVisualizationControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/EventVisualizationControllerTest.java @@ -153,6 +153,27 @@ void testPostForSingleEventDate() { assertThat(response.get("filters").toString(), not(containsString(eventDateDimension))); } + @Test + void testDelete() { + // Given + String eventDateDimension = "eventDate"; + String eventDate = "2021-07-21_2021-08-01"; + String dimensionBody = + "{'dimension': '" + eventDateDimension + "', 'items': [{'id': '" + eventDate + "'}]}"; + String body = + "{'name': 'Name Test', 'type': 'STACKED_COLUMN','eventRepetitions':null, 'program': {'id':'" + + mockProgram.getUid() + + "'}, 'columns': [" + + dimensionBody + + "]}"; + + // When + String uid = assertStatus(CREATED, POST("/eventVisualizations/", body)); + + // Then + DELETE("/eventVisualizations/" + uid).content(OK); + } + @Test void testPostForMultiEventDates() { // Given diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/IndicatorControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/IndicatorControllerTest.java index 953a89b11c5a..dd68ef0ff9c9 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/IndicatorControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/IndicatorControllerTest.java @@ -92,9 +92,9 @@ void testInvalidMerge() { JsonObject error1 = errors.getObject(0); JsonObject error2 = errors.getObject(1); assertEquals( - "SOURCE indicator does not exist: `Uid00000010`", error1.getString("message").string()); + "SOURCE Indicator does not exist: `Uid00000010`", error1.getString("message").string()); assertEquals( - "TARGET indicator does not exist: `Uid00000012`", error2.getString("message").string()); + "TARGET Indicator does not exist: `Uid00000012`", error2.getString("message").string()); } @Test diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEventChangeLog.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEventChangeLog.java index f2612ec5614f..7322256446b4 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEventChangeLog.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEventChangeLog.java @@ -28,8 +28,9 @@ package org.hisp.dhis.webapi.controller.tracker; import org.hisp.dhis.jsontree.JsonObject; +import org.hisp.dhis.webapi.controller.tracker.view.EventChangeLog; -/** Representation of {@link org.hisp.dhis.tracker.export.event.EventChangeLog}. */ +/** Representation of {@link EventChangeLog}. */ public interface JsonEventChangeLog extends JsonObject { default JsonUser getCreatedBy() { return get("createdBy").as(JsonUser.class); diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonTrackedEntityChangeLog.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonTrackedEntityChangeLog.java index 97e82e11623b..224bfbe58d9b 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonTrackedEntityChangeLog.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonTrackedEntityChangeLog.java @@ -28,8 +28,9 @@ package org.hisp.dhis.webapi.controller.tracker; import org.hisp.dhis.jsontree.JsonObject; +import org.hisp.dhis.webapi.controller.tracker.view.EventChangeLog; -/** Representation of {@link org.hisp.dhis.tracker.export.event.EventChangeLog}. */ +/** Representation of {@link EventChangeLog}. */ public interface JsonTrackedEntityChangeLog extends JsonObject { default JsonUser getCreatedBy() { return get("createdBy").as(JsonUser.class); diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportControllerUnitTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportControllerUnitTest.java index 4701e82d55e3..021e8e429390 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportControllerUnitTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportControllerUnitTest.java @@ -67,8 +67,7 @@ void shouldFailInstantiatingControllerIfAnyOrderableFieldIsUnsupported() { assertThrows( IllegalStateException.class, () -> - new EventsExportController( - eventService, null, null, null, null, null, null, null, null)); + new EventsExportController(eventService, null, null, null, null, null, null, null)); assertAll( () -> assertStartsWith("event controller supports ordering by", exception.getMessage()), diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/QueryController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/QueryController.java index 7c37c1bb26c7..cc00c1a07914 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/QueryController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/QueryController.java @@ -38,11 +38,11 @@ import lombok.Value; import org.hisp.dhis.cache.Cache; import org.hisp.dhis.cache.CacheProvider; +import org.hisp.dhis.common.HashUtils; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.feedback.BadRequestException; import org.hisp.dhis.feedback.NotFoundException; import org.hisp.dhis.render.RenderService; -import org.hisp.dhis.system.util.CodecUtils; import org.hisp.dhis.webapi.utils.HttpServletRequestPaths; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -139,7 +139,7 @@ private QueryAlias createAlias(String target, HttpServletRequest request) throw new BadRequestException("Target url exceeds maximum length"); } - String alias = CodecUtils.sha1Hex(target); + String alias = HashUtils.hashSHA1(target.getBytes()); aliasCache.put(alias, target); String contextPath = HttpServletRequestPaths.getContextPath(request); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java index 2d3b9268544d..cbe2b8fa9b36 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java @@ -49,8 +49,7 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.merge.MergeParams; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeType; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -73,7 +72,7 @@ @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) public class CategoryOptionController extends AbstractCrudController { private final CategoryService categoryService; - private final MergeProcessor categoryOptionMergeProcessor; + private final MergeService categoryOptionMergeService; @ResponseBody @GetMapping(value = "orgUnits") @@ -95,11 +94,10 @@ public Map> getOrgUnitsAssociations( public @ResponseBody WebMessage mergeCategoryOptions(@RequestBody MergeParams params) throws ConflictException { log.info("CategoryOption merge received"); - params.setMergeType(MergeType.CATEGORY_OPTION); MergeReport report; try { - report = categoryOptionMergeProcessor.processMerge(params); + report = categoryOptionMergeService.processMerge(params); } catch (PersistenceException ex) { String helpfulMessage = getHelpfulMessage(ex); log.error("Error while processing CategoryOption merge: {}", helpfulMessage); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java index a924cc4b986e..bc6b4422970d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java @@ -42,8 +42,7 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.merge.MergeParams; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeType; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -65,7 +64,7 @@ public class DataElementController extends AbstractCrudController { private final DataElementService dataElementService; - private final MergeProcessor dataElementMergeProcessor; + private final MergeService dataElementMergeService; @ResponseStatus(HttpStatus.OK) @RequiresAuthority(anyOf = F_DATA_ELEMENT_MERGE) @@ -73,11 +72,10 @@ public class DataElementController extends AbstractCrudController { public @ResponseBody WebMessage mergeDataElements(@RequestBody MergeParams params) throws ConflictException { log.info("Data element merge received"); - params.setMergeType(MergeType.DATA_ELEMENT); MergeReport report; try { - report = dataElementMergeProcessor.processMerge(params); + report = dataElementMergeService.processMerge(params); } catch (PersistenceException ex) { String helpfulMessage = getHelpfulMessage(ex); log.error("Error while processing Data element merge: {}", helpfulMessage); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java index 42558e221213..e9bd0f940f9c 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java @@ -46,8 +46,7 @@ import org.hisp.dhis.i18n.I18nManager; import org.hisp.dhis.indicator.Indicator; import org.hisp.dhis.merge.MergeParams; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeType; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -72,7 +71,7 @@ public class IndicatorController extends AbstractCrudController { private final I18nManager i18nManager; - private final MergeProcessor indicatorMergeProcessor; + private final MergeService indicatorMergeService; @PostMapping(value = "/expression/description", produces = APPLICATION_JSON_VALUE) @ResponseBody @@ -102,9 +101,8 @@ public WebMessage getExpressionDescription(@RequestBody String expression) { public @ResponseBody WebMessage mergeIndicators(@RequestBody MergeParams params) throws ConflictException { log.info("Indicator merge received"); - params.setMergeType(MergeType.INDICATOR); - MergeReport report = indicatorMergeProcessor.processMerge(params); + MergeReport report = indicatorMergeService.processMerge(params); log.info("Indicator merge processed with report: {}", report); return WebMessageUtils.mergeReport(report); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java index 6bab10edd76e..681799e6055b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java @@ -38,8 +38,7 @@ import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.indicator.IndicatorType; import org.hisp.dhis.merge.MergeParams; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeType; +import org.hisp.dhis.merge.MergeService; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -59,7 +58,7 @@ @Slf4j public class IndicatorTypeController extends AbstractCrudController { - private final MergeProcessor indicatorTypeMergeProcessor; + private final MergeService indicatorTypeMergeService; @ResponseStatus(HttpStatus.OK) @RequiresAuthority(anyOf = F_INDICATOR_TYPE_MERGE) @@ -67,9 +66,8 @@ public class IndicatorTypeController extends AbstractCrudController Page handle( return Page.withPager(jsonKey, page.withItems(objectNodes), requestURL); } - private static String getRequestURL(HttpServletRequest request) { + public static String getRequestURL(HttpServletRequest request) { StringBuilder requestURL = new StringBuilder(getServletPath(request)); requestURL.append(request.getPathInfo()); String queryString = request.getQueryString(); diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeProcessor.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventChangeLogMapper.java similarity index 52% rename from dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeProcessor.java rename to dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventChangeLogMapper.java index 5801f0df039e..b0a7803e002d 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/category/CategoryOptionMergeProcessor.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventChangeLogMapper.java @@ -25,30 +25,33 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.merge.category; +package org.hisp.dhis.webapi.controller.tracker.export.event; -import lombok.RequiredArgsConstructor; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeService; -import org.springframework.stereotype.Component; +import org.hisp.dhis.webapi.controller.tracker.view.EventChangeLog; +import org.hisp.dhis.webapi.controller.tracker.view.EventChangeLog.DataValueChange; +import org.hisp.dhis.webapi.controller.tracker.view.UIDMapper; +import org.hisp.dhis.webapi.controller.tracker.view.User; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; -/** - * Implementation of {@link MergeProcessor} that currently only uses its default method. - * - * @author david mackessy - */ -@Component -@RequiredArgsConstructor -public class CategoryOptionMergeProcessor implements MergeProcessor { +@Mapper(uses = {UIDMapper.class}) +public interface EventChangeLogMapper { + + @Mapping(target = "createdBy", source = "eventChangeLog") + @Mapping(target = "createdAt", source = "created") + @Mapping(target = "type", source = "changeLogType") + @Mapping(target = "change.dataValue", source = "eventChangeLog") + EventChangeLog map(org.hisp.dhis.tracker.export.event.EventChangeLog eventChangeLog); - /** - * Spring injects the correct service based on the variable name (when there are multiple - * implementations to choose from). So The {@link CategoryOptionMergeService} gets injected here. - */ - private final MergeService categoryOptionMergeService; + @Mapping(target = "uid", source = "createdBy.uid") + @Mapping(target = "username", source = "createdBy.username") + @Mapping(target = "firstName", source = "createdBy.firstName") + @Mapping(target = "surname", source = "createdBy.surname") + User mapUser(org.hisp.dhis.tracker.export.event.EventChangeLog eventChangeLog); - @Override - public MergeService getMergeService() { - return categoryOptionMergeService; - } + @Mapping(target = "dataElement", source = "dataElement.uid") + @Mapping(target = "previousValue", source = "previousValue") + @Mapping(target = "currentValue", source = "currentValue") + DataValueChange mapDataValueChange( + org.hisp.dhis.tracker.export.event.EventChangeLog eventChangeLog); } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java index d2f2be65096f..d5496f6cf98a 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java @@ -30,6 +30,7 @@ import static org.hisp.dhis.webapi.controller.tracker.ControllerSupport.assertUserOrderableFieldsAreSupported; import static org.hisp.dhis.webapi.controller.tracker.export.CompressionUtil.writeGzip; import static org.hisp.dhis.webapi.controller.tracker.export.CompressionUtil.writeZip; +import static org.hisp.dhis.webapi.controller.tracker.export.FieldFilterRequestHandler.getRequestURL; import static org.hisp.dhis.webapi.controller.tracker.export.RequestParamsValidator.validatePaginationParameters; import static org.hisp.dhis.webapi.controller.tracker.export.RequestParamsValidator.validateUnsupportedParameter; import static org.hisp.dhis.webapi.controller.tracker.export.event.EventRequestParams.DEFAULT_FIELDS_PARAM; @@ -62,7 +63,6 @@ import org.hisp.dhis.program.Event; import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.tracker.export.PageParams; -import org.hisp.dhis.tracker.export.event.EventChangeLog; import org.hisp.dhis.tracker.export.event.EventChangeLogOperationParams; import org.hisp.dhis.tracker.export.event.EventChangeLogService; import org.hisp.dhis.tracker.export.event.EventOperationParams; @@ -70,9 +70,9 @@ import org.hisp.dhis.tracker.export.event.EventService; import org.hisp.dhis.webapi.controller.tracker.export.ChangeLogRequestParams; import org.hisp.dhis.webapi.controller.tracker.export.CsvService; -import org.hisp.dhis.webapi.controller.tracker.export.FieldFilterRequestHandler; import org.hisp.dhis.webapi.controller.tracker.export.FileResourceRequestHandler; import org.hisp.dhis.webapi.controller.tracker.export.ResponseHeader; +import org.hisp.dhis.webapi.controller.tracker.view.EventChangeLog; import org.hisp.dhis.webapi.controller.tracker.view.Page; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.mapstruct.factory.Mappers; @@ -95,6 +95,9 @@ class EventsExportController { private static final EventMapper EVENTS_MAPPER = Mappers.getMapper(EventMapper.class); + private static final EventChangeLogMapper EVENT_CHANGE_LOG_MAPPER = + Mappers.getMapper(EventChangeLogMapper.class); + private static final String EVENT_CSV_FILE = EVENTS + ".csv"; private static final String EVENT_JSON_FILE = EVENTS + ".json"; @@ -117,8 +120,6 @@ class EventsExportController { private final EventChangeLogService eventChangeLogService; - private final FieldFilterRequestHandler fieldFilterRequestHandler; - private final FileResourceRequestHandler fileResourceRequestHandler; public EventsExportController( @@ -129,7 +130,6 @@ public EventsExportController( EventFieldsParamMapper eventsMapper, ObjectMapper objectMapper, EventChangeLogService eventChangeLogService, - FieldFilterRequestHandler fieldFilterRequestHandler, FileResourceRequestHandler fileResourceRequestHandler) { this.eventService = eventService; this.eventParamsMapper = eventParamsMapper; @@ -138,7 +138,6 @@ public EventsExportController( this.eventsMapper = eventsMapper; this.objectMapper = objectMapper; this.eventChangeLogService = eventChangeLogService; - this.fieldFilterRequestHandler = fieldFilterRequestHandler; this.fileResourceRequestHandler = fileResourceRequestHandler; assertUserOrderableFieldsAreSupported( @@ -347,7 +346,7 @@ ResponseEntity getEventDataValueImage( } @GetMapping("/{event}/changeLogs") - Page getEventChangeLogsByUid( + ResponseEntity> getEventChangeLogsByUid( @OpenApi.Param({UID.class, Event.class}) @PathVariable UID event, ChangeLogRequestParams requestParams, HttpServletRequest request) @@ -357,9 +356,19 @@ Page getEventChangeLogsByUid( PageParams pageParams = new PageParams(requestParams.getPage(), requestParams.getPageSize(), false); - org.hisp.dhis.tracker.export.Page changeLogs = - eventChangeLogService.getEventChangeLog(event, operationParams, pageParams); + org.hisp.dhis.tracker.export.Page + changeLogs = eventChangeLogService.getEventChangeLog(event, operationParams, pageParams); - return fieldFilterRequestHandler.handle(request, "changeLogs", changeLogs, requestParams); + List eventChangeLogs = + changeLogs.getItems().stream().map(EVENT_CHANGE_LOG_MAPPER::map).toList(); + + List objectNodes = + fieldFilterService.toObjectNodes(eventChangeLogs, requestParams.getFields()); + + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body( + Page.withPager( + "changeLogs", changeLogs.withItems(objectNodes), getRequestURL(request))); } } diff --git a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeProcessor.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/view/EventChangeLog.java similarity index 66% rename from dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeProcessor.java rename to dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/view/EventChangeLog.java index ee4a7260e80e..aea046d7a4bd 100644 --- a/dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/merge/indicator/IndicatorMergeProcessor.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/view/EventChangeLog.java @@ -25,30 +25,22 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.merge.indicator; +package org.hisp.dhis.webapi.controller.tracker.view; -import lombok.RequiredArgsConstructor; -import org.hisp.dhis.merge.MergeProcessor; -import org.hisp.dhis.merge.MergeService; -import org.springframework.stereotype.Component; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Date; +import org.hisp.dhis.common.UID; -/** - * Implementation of {@link MergeProcessor} that currently only uses its default method. - * - * @author david mackessy - */ -@Component -@RequiredArgsConstructor -public class IndicatorMergeProcessor implements MergeProcessor { +public record EventChangeLog( + @JsonProperty User createdBy, + @JsonProperty Date createdAt, + @JsonProperty String type, + @JsonProperty Change change) { - /** - * Spring injects the correct service based on the variable name (when there are multiple - * implementations to choose from). So The {@link IndicatorMergeService} gets injected here. - */ - private final MergeService indicatorMergeService; + public record Change(@JsonProperty DataValueChange dataValue) {} - @Override - public MergeService getMergeService() { - return indicatorMergeService; - } + public record DataValueChange( + @JsonProperty UID dataElement, + @JsonProperty String previousValue, + @JsonProperty String currentValue) {} } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/filter/AppOverrideFilter.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/filter/AppOverrideFilter.java index 6bb7fe8f32ec..260ebe77e6ba 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/filter/AppOverrideFilter.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/filter/AppOverrideFilter.java @@ -42,8 +42,8 @@ import org.hisp.dhis.appmanager.App; import org.hisp.dhis.appmanager.AppManager; import org.hisp.dhis.appmanager.AppStatus; +import org.hisp.dhis.common.HashUtils; import org.hisp.dhis.commons.util.StreamUtils; -import org.hisp.dhis.system.util.CodecUtils; import org.hisp.dhis.webapi.utils.HttpServletRequestPaths; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; @@ -131,7 +131,7 @@ private void serveInstalledAppResource( return; } - String etag = CodecUtils.md5Hex(String.valueOf(resource.lastModified())); + String etag = HashUtils.hashMD5(String.valueOf(resource.lastModified()).getBytes()); if (new ServletWebRequest(request, response).checkNotModified(etag)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/ContextUtils.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/ContextUtils.java index eab1e2b243ea..fe5130e188ef 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/ContextUtils.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/ContextUtils.java @@ -37,10 +37,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; +import org.hisp.dhis.common.HashUtils; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.IdentifiableObjectUtils; import org.hisp.dhis.common.cache.CacheStrategy; -import org.hisp.dhis.system.util.CodecUtils; import org.hisp.dhis.user.User; import org.hisp.dhis.user.UserDetails; import org.hisp.dhis.util.DateUtils; @@ -288,7 +288,7 @@ public static String getEtag(Date lastModified, UserDetails user) { String value = String.format("%s-%s", DateUtils.toLongDate(lastModified), user.getUid()); - return CodecUtils.md5Hex(value); + return HashUtils.hashMD5(value.getBytes()); } /** diff --git a/dhis-2/dhis-web-server/pom.xml b/dhis-2/dhis-web-server/pom.xml index 54c0c01f986c..e324fa58628a 100644 --- a/dhis-2/dhis-web-server/pom.xml +++ b/dhis-2/dhis-web-server/pom.xml @@ -207,6 +207,7 @@ ./apps-to-bundle.json master + ${skip.bundle.apps} diff --git a/dhis-2/dhis-web-server/src/test/java/org/hisp/dhis/auth/AuthTest.java b/dhis-2/dhis-web-server/src/test/java/org/hisp/dhis/auth/AuthTest.java index a0fd0f13f3c4..c7a1040eea76 100644 --- a/dhis-2/dhis-web-server/src/test/java/org/hisp/dhis/auth/AuthTest.java +++ b/dhis-2/dhis-web-server/src/test/java/org/hisp/dhis/auth/AuthTest.java @@ -39,6 +39,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Base64; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; @@ -53,6 +54,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; @@ -173,8 +175,6 @@ void testLogin() { assertEquals(LoginResponse.STATUS.SUCCESS, body.getLoginStatus()); HttpHeaders headers = loginResponse.getHeaders(); - log.info("Headers: " + headers); - assertEquals("/dhis-web-dashboard/", body.getRedirectUrl()); assertNotNull(headers); @@ -252,6 +252,13 @@ void testRedirectToCssResourceWorker() { testRedirectUrl("/dhis-web-dashboard/static/css/main.4536e618.css", "/dhis-web-dashboard/"); } + @Test + void testRedirectAccountWhenVerifiedEmailEnforced() { + changeSystemSetting("enforceVerifiedEmail", "true"); + testRedirectUrl("/dhis-web-dashboard/", "/dhis-web-user-profile/#/account"); + changeSystemSetting("enforceVerifiedEmail", "false"); + } + @Test void testRedirectMissingEndingSlash() { testRedirectWhenLoggedIn("/dhis-web-dashboard/", "/dhis-web-dashboard/"); @@ -261,6 +268,44 @@ private static void testRedirectUrl(String url) { testRedirectUrl(url, url); } + private static RestTemplate createRestTemplateWithBasicAuthHeader() { + RestTemplate restTemplate = new RestTemplate(); + + // Create the authentication header + String authHeader = + Base64.getUrlEncoder().encodeToString("admin:district".getBytes(StandardCharsets.UTF_8)); + + // Add header to every request + restTemplate + .getInterceptors() + .add( + (request, body, execution) -> { + request.getHeaders().add(HttpHeaders.AUTHORIZATION, "Basic " + authHeader); + return execution.execute(request, body); + }); + + return restTemplate; + } + + private static void changeSystemSetting(String key, String value) { + String port = Integer.toString(availablePort); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_PLAIN); + + RestTemplate restTemplate = createRestTemplateWithBasicAuthHeader(); + HttpEntity requestEntity = new HttpEntity<>(value, headers); + + ResponseEntity response = + restTemplate.exchange( + "http://localhost:" + port + "/api/systemSettings/" + key, + HttpMethod.POST, + requestEntity, + String.class); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + private static void testRedirectUrl(String url, String redirectUrl) { String port = Integer.toString(availablePort); @@ -340,6 +385,6 @@ protected void prepareConnection(HttpURLConnection connection, String httpMethod List location = respHeaders.get("Location"); assertNotNull(location); assertEquals(1, location.size()); - assertEquals("/dhis-web-dashboard/", location.get(0)); + assertEquals(redirectUrl, location.get(0)); } } diff --git a/dhis-2/pom.xml b/dhis-2/pom.xml index a062ce594b71..1af0f93a89a9 100644 --- a/dhis-2/pom.xml +++ b/dhis-2/pom.xml @@ -119,14 +119,14 @@ 5.6.15.Final 3.10.8 4.0.5 - 3.8.3 + 3.9.0 0.10.1 6.1.0 42.7.4 2.5.1 - 1.10 - 8.3.0 + 1.10.1 + 9.1.0 6.1.0 @@ -168,7 +168,7 @@ 2.38.0 - 4.1.114.Final + 4.1.115.Final 4.8.177 @@ -196,7 +196,6 @@ 3.6.1 1.9.0 2.17.0 - 1.17.1 2.1.1 1.6.0 5.3.1 @@ -232,13 +231,13 @@ 3.4.1 3.3.1 3.5.1 - 3.10.1 + 3.11.1 3.1.0 3.5.0 3.8.1 2.6.0 2.17.1 - 10.0.4 + 11.1.0 4.8.6.5 2.43.0 0.8.12 @@ -247,7 +246,7 @@ 1.1.1 2.11.0 3.1.0 - 2.9.0 + 2.10.0 @@ -921,11 +920,6 @@ commons-io ${commons-io.version} - - commons-codec - commons-codec - ${commons-codec.version} - org.apache.commons commons-jexl