From 582e64edacb6827718678d379678338939ff03bc Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 4 Jul 2023 11:05:47 +1000 Subject: [PATCH 01/24] [1.8.2-RC] Bump to version 1.8.2-SNAPSHOT --- core/gradle.properties | 4 ++-- docs/content/developer/getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/gradle.properties b/core/gradle.properties index c731971e76..c782b7cf6a 100644 --- a/core/gradle.properties +++ b/core/gradle.properties @@ -29,8 +29,8 @@ # Properties which are consumed by plugins/gradle-mvn-push.gradle plugin. # They are used for publishing artifact to snapshot repository. -VERSION_NAME=1.8.1.1 -VERSION_CODE=282 +VERSION_NAME=1.8.2-SNAPSHOT +VERSION_CODE=283 GROUP=org.hisp.dhis diff --git a/docs/content/developer/getting-started.md b/docs/content/developer/getting-started.md index 59ab7d2742..50d96b6bdf 100644 --- a/docs/content/developer/getting-started.md +++ b/docs/content/developer/getting-started.md @@ -6,7 +6,7 @@ Include dependency in build.gradle. ```gradle dependencies { - implementation "org.hisp.dhis:android-core:1.8.1.1" + implementation "org.hisp.dhis:android-core:1.8.2" ... } ``` From 0786aef13d76861592d5d8c1b2a3cb450812c8a7 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 3 Jul 2023 16:57:05 +1000 Subject: [PATCH 02/24] [ANDROSDK-1706] Capture ConnectException at login (offline login) --- .../core/arch/api/executors/internal/APIErrorMapper.kt | 9 +++++++++ .../org/hisp/dhis/android/core/maintenance/D2Error.java | 4 +++- .../hisp/dhis/android/core/maintenance/D2ErrorCode.java | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt index adf53ffbcd..d1ddc5d3fa 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt @@ -42,6 +42,7 @@ import org.hisp.dhis.android.core.maintenance.D2ErrorComponent import retrofit2.Call import retrofit2.HttpException import retrofit2.Response +import java.net.ConnectException @Reusable @Suppress("TooManyFunctions") @@ -51,6 +52,7 @@ internal class APIErrorMapper @Inject constructor() { return when (throwable) { is SocketTimeoutException -> socketTimeoutException(errorBuilder, throwable) is UnknownHostException -> unknownHostException(errorBuilder, throwable) + is ConnectException -> socketConnectException(errorBuilder, throwable) is HttpException -> httpException(errorBuilder, throwable) is SSLException -> sslException(errorBuilder, throwable) is IOException -> ioException(errorBuilder, throwable) @@ -78,6 +80,13 @@ internal class APIErrorMapper @Inject constructor() { .build() } + private fun socketConnectException(errorBuilder: D2Error.Builder, e: ConnectException): D2Error { + return logAndAppendOriginal(errorBuilder, e) + .errorCode(D2ErrorCode.CONNECTION_ERROR) + .errorDescription("API call failed due to a ConnectException.") + .build() + } + private fun sslException(errorBuilder: D2Error.Builder, sslException: SSLException): D2Error { return logAndAppendOriginal(errorBuilder, sslException) .errorDescription(sslException.message) diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java index 8024732d28..c9709ff2ba 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java +++ b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java @@ -85,7 +85,9 @@ public static Builder builder() { public abstract Builder toBuilder(); public boolean isOffline() { - return errorCode() == D2ErrorCode.SOCKET_TIMEOUT || errorCode() == D2ErrorCode.UNKNOWN_HOST; + return errorCode() == D2ErrorCode.SOCKET_TIMEOUT || + errorCode() == D2ErrorCode.UNKNOWN_HOST || + errorCode() == D2ErrorCode.CONNECTION_ERROR; } @AutoValue.Builder diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java index 3d4c665de7..ffb040dc02 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java +++ b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java @@ -41,6 +41,7 @@ public enum D2ErrorCode { CANT_CREATE_EXISTING_OBJECT, CANT_DELETE_NON_EXISTING_OBJECT, CANT_INSTANTIATE_KEYSTORE, + CONNECTION_ERROR, COULD_NOT_RESERVE_VALUE_ON_SERVER, DATABASE_EXPORT_LOGIN_FIRST, DATABASE_EXPORT_ENCRYPTED_NOT_SUPPORTED, From b88efd1b7ce77d184319aa7b3507ba89f79426ab Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 3 Jul 2023 20:25:19 +1000 Subject: [PATCH 03/24] [ANDROSDK-1706] Add integration test for offline login --- .../LogInOfflineCallMockIntegrationShould.kt | 79 +++++++++++++++++++ .../mock/BaseMockIntegrationTest.kt | 5 ++ ...tegrationTestMethodScopedEmptyEnqueable.kt | 44 +++++++++++ .../MockIntegrationTestDatabaseContent.java | 1 + .../mock/MockIntegrationTestObjectsFactory.kt | 8 ++ .../api/executors/internal/APIErrorMapper.kt | 2 +- 6 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/user/internal/LogInOfflineCallMockIntegrationShould.kt create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestMethodScopedEmptyEnqueable.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/user/internal/LogInOfflineCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/user/internal/LogInOfflineCallMockIntegrationShould.kt new file mode 100644 index 0000000000..9c65e3efb9 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/user/internal/LogInOfflineCallMockIntegrationShould.kt @@ -0,0 +1,79 @@ +/* + * 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.android.core.user.internal + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.user.User +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestMethodScopedEmptyEnqueable +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class LogInOfflineCallMockIntegrationShould : BaseMockIntegrationTestMethodScopedEmptyEnqueable() { + + @Test + fun login_offline_on_connection_error() { + dhis2MockServer.enqueueLoginResponses() + + login() + assertThat(getUser()).isNotNull() + + logout() + assertThrowsException { getUser() } + + dhis2MockServer.shutdown() + + login() + assertThat(d2.userModule().user().blockingGet()).isNotNull() + } + + private fun login(): User { + return d2.userModule().blockingLogIn("test_user", "test_password", dhis2MockServer.baseEndpoint) + } + + private fun logout() { + if (d2.userModule().blockingIsLogged()) { + d2.userModule().blockingLogOut() + } + } + + private fun getUser(): User? { + return d2.userModule().user().blockingGet() + } + + private fun assertThrowsException(block: () -> Any?) { + try { + block() + fail("Get user should fail after logout") + } catch (_: RuntimeException) { + // + } + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTest.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTest.kt index fd73098e90..338a7bc17a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTest.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTest.kt @@ -64,5 +64,10 @@ abstract class BaseMockIntegrationTest { } return tuple.isNewInstance } + + @JvmStatic + fun removeObjects(content: MockIntegrationTestDatabaseContent) { + MockIntegrationTestObjectsFactory.removeObjects(content) + } } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestMethodScopedEmptyEnqueable.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestMethodScopedEmptyEnqueable.kt new file mode 100644 index 0000000000..d0967a819f --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestMethodScopedEmptyEnqueable.kt @@ -0,0 +1,44 @@ +/* + * 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.android.core.utils.integration.mock + +import org.junit.After +import org.junit.Before + +abstract class BaseMockIntegrationTestMethodScopedEmptyEnqueable : BaseMockIntegrationTest() { + + @Before + fun setup() { + setUpClass(MockIntegrationTestDatabaseContent.MethodScopedEmptyEnqueable) + } + + @After + fun tearDown() { + removeObjects(MockIntegrationTestDatabaseContent.MethodScopedEmptyEnqueable) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestDatabaseContent.java b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestDatabaseContent.java index fd8d6bc03e..c2c41e1823 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestDatabaseContent.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestDatabaseContent.java @@ -34,6 +34,7 @@ public enum MockIntegrationTestDatabaseContent { FullDispatcher, MetadataEnqueable, MetadataDispatcher, + MethodScopedEmptyEnqueable, LocalAnalyticsDefaultDispatcher, LocalAnalyticsLargeDispatcher, LocalAnalyticsSuperLargeDispatcher diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestObjectsFactory.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestObjectsFactory.kt index 3b94b35b14..0228db6d2d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestObjectsFactory.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/MockIntegrationTestObjectsFactory.kt @@ -43,6 +43,14 @@ internal object MockIntegrationTestObjectsFactory { } } + fun removeObjects(content: MockIntegrationTestDatabaseContent) { + val instance = instances[content] + if (instance != null) { + instance.tearDown() + instances.remove(content) + } + } + @JvmStatic fun tearDown() { if (instances.isNotEmpty()) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt index d1ddc5d3fa..23be572285 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt @@ -30,6 +30,7 @@ package org.hisp.dhis.android.core.arch.api.executors.internal import android.util.Log import dagger.Reusable import java.io.IOException +import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException import javax.inject.Inject @@ -42,7 +43,6 @@ import org.hisp.dhis.android.core.maintenance.D2ErrorComponent import retrofit2.Call import retrofit2.HttpException import retrofit2.Response -import java.net.ConnectException @Reusable @Suppress("TooManyFunctions") From 04da29cba4622031d71864c65ddc62c34dec2e88 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 4 Jul 2023 11:03:04 +1000 Subject: [PATCH 04/24] [ANDROSDK-1706] Refactor to SERVER_CONNECTION_ERROR --- .../core/arch/api/executors/internal/APIErrorMapper.kt | 6 +++--- .../org/hisp/dhis/android/core/maintenance/D2Error.java | 2 +- .../org/hisp/dhis/android/core/maintenance/D2ErrorCode.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt index 23be572285..b337433f60 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APIErrorMapper.kt @@ -52,7 +52,7 @@ internal class APIErrorMapper @Inject constructor() { return when (throwable) { is SocketTimeoutException -> socketTimeoutException(errorBuilder, throwable) is UnknownHostException -> unknownHostException(errorBuilder, throwable) - is ConnectException -> socketConnectException(errorBuilder, throwable) + is ConnectException -> connectException(errorBuilder, throwable) is HttpException -> httpException(errorBuilder, throwable) is SSLException -> sslException(errorBuilder, throwable) is IOException -> ioException(errorBuilder, throwable) @@ -80,9 +80,9 @@ internal class APIErrorMapper @Inject constructor() { .build() } - private fun socketConnectException(errorBuilder: D2Error.Builder, e: ConnectException): D2Error { + private fun connectException(errorBuilder: D2Error.Builder, e: ConnectException): D2Error { return logAndAppendOriginal(errorBuilder, e) - .errorCode(D2ErrorCode.CONNECTION_ERROR) + .errorCode(D2ErrorCode.SERVER_CONNECTION_ERROR) .errorDescription("API call failed due to a ConnectException.") .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java index c9709ff2ba..b1018b82e6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java +++ b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java @@ -87,7 +87,7 @@ public static Builder builder() { public boolean isOffline() { return errorCode() == D2ErrorCode.SOCKET_TIMEOUT || errorCode() == D2ErrorCode.UNKNOWN_HOST || - errorCode() == D2ErrorCode.CONNECTION_ERROR; + errorCode() == D2ErrorCode.SERVER_CONNECTION_ERROR; } @AutoValue.Builder diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java index ffb040dc02..b62192d085 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java +++ b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java @@ -41,7 +41,6 @@ public enum D2ErrorCode { CANT_CREATE_EXISTING_OBJECT, CANT_DELETE_NON_EXISTING_OBJECT, CANT_INSTANTIATE_KEYSTORE, - CONNECTION_ERROR, COULD_NOT_RESERVE_VALUE_ON_SERVER, DATABASE_EXPORT_LOGIN_FIRST, DATABASE_EXPORT_ENCRYPTED_NOT_SUPPORTED, @@ -71,6 +70,7 @@ public enum D2ErrorCode { OWNERSHIP_ACCESS_DENIED, PROGRAM_ACCESS_CLOSED, SEARCH_GRID_PARSE, + SERVER_CONNECTION_ERROR, SERVER_URL_NULL, SERVER_URL_MALFORMED, SETTINGS_APP_NOT_SUPPORTED, From 5e74cad9602a78a373a29cbbc0c3c1076732e79e Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 6 Jul 2023 20:28:36 +1000 Subject: [PATCH 05/24] [ANDROSDK-1706] Add failing integration test --- .../BasePayloadGeneratorMockIntegration.kt | 1 + ...erPayloadGeneratorMockIntegrationShould.kt | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt index 1b9eacb248..55aeeeab89 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt @@ -70,6 +70,7 @@ open class BasePayloadGeneratorMockIntegration : BaseMockIntegrationTestMetadata protected val event2Id = "event2Id" protected val event3Id = "event3Id" protected val singleEventId = "singleEventId" + protected val unassignedDataElementId = "bx6fsa0t90x" @After @Throws(D2Error::class) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt index dc3e15ffa3..efe09ff0fc 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt @@ -205,7 +205,7 @@ class OldTrackerImporterPayloadGeneratorMockIntegrationShould : BasePayloadGener } @Test - fun build_payload_with_non_accessible_adta() { + fun build_payload_with_non_accessible_data() { storeTrackerData() val previousTrackedEntityType = trackedEntityTypeStore.selectFirst()!! val previousProgram = programStore.selectFirst()!! @@ -228,4 +228,22 @@ class OldTrackerImporterPayloadGeneratorMockIntegrationShould : BasePayloadGener trackedEntityTypeStore.update(previousTrackedEntityType) programStore.update(previousProgram) } + + @Test + fun do_not_include_data_values_not_assigned_to_program_stage() { + storeTrackerData() + d2.trackedEntityModule().trackedEntityDataValues() + .value(event1Id, unassignedDataElementId) + .blockingSet("value") + + val instance = teiStore.selectByUid(teiId)!! + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(instance)) + + val events = getEvents(getEnrollments(payload.trackedEntityInstances.first()).first()) + val event1 = events.find { it.uid() == event1Id } + assertThat(event1).isNotNull() + + val eventValues = event1!!.trackedEntityDataValues()!! + assertThat(eventValues.any { it.dataElement() == unassignedDataElementId}).isFalse() + } } From 40ad8a1e6be8b907a4e2e34ea3d858217067cbc4 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 6 Jul 2023 20:30:16 +1000 Subject: [PATCH 06/24] [ANDROSDK-1706] Do not include unassigned programStageDataElements in POST call --- .../OldTrackerImporterPayloadGenerator.kt | 23 ++++++++----------- .../internal/TrackedEntityDataValueStore.java | 2 +- .../TrackedEntityDataValueStoreImpl.java | 23 ++++++++++++++----- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt index 21779cad58..cdfd15a069 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt @@ -41,6 +41,8 @@ import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.event.EventInternalAccessor import org.hisp.dhis.android.core.event.internal.EventStore import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.program.ProgramStageDataElement import org.hisp.dhis.android.core.program.internal.ProgramStoreInterface import org.hisp.dhis.android.core.relationship.Relationship import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository @@ -70,7 +72,6 @@ internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( ) { private data class ExtraData( - val dataValueMap: Map>, val eventMap: Map>, val enrollmentMap: Map>, val attributeValueMap: Map>, @@ -241,7 +242,6 @@ internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( private fun getExtraData(): ExtraData { return ExtraData( - dataValueMap = trackedEntityDataValueStore.queryByUploadableEvents(), eventMap = eventStore.queryEventsAttachedToEnrollmentToPost(), enrollmentMap = enrollmentStore.queryEnrollmentsToPost(), attributeValueMap = trackedEntityAttributeValueStore.queryTrackedEntityAttributeValueToPost(), @@ -318,16 +318,21 @@ internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( getEvent(event, extraData) } ?: emptyList() + val notes = extraData.notes.filter { it.enrollment() == enrollment.uid() } + EnrollmentInternalAccessor.insertEvents(enrollment.toBuilder(), events) - .notes(getEnrollmentNotes(extraData.notes, enrollment)) + .notes(notes) .build() } ?: emptyList() } private fun getEvent(event: Event, extraData: ExtraData): Event { + val eventDataValues = trackedEntityDataValueStore.queryToPostByEvent(event.uid()) + val eventNotes = extraData.notes.filter { it.event() == event.uid() } + val eventBuilder = event.toBuilder() - .trackedEntityDataValues(extraData.dataValueMap[event.uid()]) - .notes(getEventNotes(extraData.notes, event)) + .trackedEntityDataValues(eventDataValues) + .notes(eventNotes) if (versionManager.getVersion() == DHISVersion.V2_30) { eventBuilder.geometry(null) @@ -336,14 +341,6 @@ internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( return eventBuilder.build() } - private fun getEventNotes(notes: List, event: Event): List { - return notes.filter { it.event() == event.uid() } - } - - private fun getEnrollmentNotes(notes: List, enrollment: Enrollment): List { - return notes.filter { it.enrollment() == enrollment.uid() } - } - private fun addProgramOwners(payload: OldTrackerImporterPayload): OldTrackerImporterPayload { return if (payload.trackedEntityInstances.isNotEmpty()) { val programOwnerWhere = WhereClauseBuilder() diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java index 61fc96cbd9..0364a6bf39 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java @@ -48,7 +48,7 @@ public interface TrackedEntityDataValueStore extends ObjectWithoutUidStore> queryTrackerTrackedEntityDataValues(); - Map> queryByUploadableEvents(); + List queryToPostByEvent(@NonNull String eventUid); void removeDeletedDataValuesByEvent(@NonNull String eventUid); } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java index 1860f5d03a..a3a78fded7 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java @@ -43,6 +43,7 @@ import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.event.EventTableInfo; +import org.hisp.dhis.android.core.program.ProgramStageDataElementTableInfo; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueTableInfo; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueTableInfo; @@ -157,13 +158,23 @@ public Map> queryTrackerTrackedEntityDataVa } @Override - public Map> queryByUploadableEvents() { - - String queryStatement = "SELECT TrackedEntityDataValue.* " + - " FROM (TrackedEntityDataValue INNER JOIN Event ON TrackedEntityDataValue.event = Event.uid) " + - " WHERE " + eventInUploadableState() + ";"; + public List queryToPostByEvent(@NonNull String eventUid) { + String psDataElementName = ProgramStageDataElementTableInfo.TABLE_INFO.name(); + String eventName = EventTableInfo.TABLE_INFO.name(); + + String psDataElementQuery = "SELECT " + ProgramStageDataElementTableInfo.Columns.DATA_ELEMENT + + " FROM " + psDataElementName + + " INNER JOIN " + eventName + + " ON " + psDataElementName + "." + ProgramStageDataElementTableInfo.Columns.PROGRAM_STAGE + + " = " + eventName + "." + EventTableInfo.Columns.PROGRAM_STAGE + + " WHERE " + eventName + "." + EventTableInfo.Columns.UID + " = '" + eventUid + "'"; + + String queryStatement = new WhereClauseBuilder() + .appendKeyStringValue(TrackedEntityDataValueTableInfo.Columns.EVENT, eventUid) + .appendInSubQuery(TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT, psDataElementQuery) + .build(); - return queryTrackedEntityDataValues(queryStatement); + return selectWhere(queryStatement); } private Map> queryTrackedEntityDataValues(String queryStatement) { From 1cb4f73ad4df8bb240bab2e8bbec826716917a6f Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 7 Jul 2023 12:17:53 +1000 Subject: [PATCH 07/24] [ANDROSDK-1706] Clean up unassigned eventValues after successful upload --- ...erPayloadGeneratorMockIntegrationShould.kt | 2 +- ...EntityDataValueStoreIntegrationShould.java | 41 +++++++++++++++++++ .../internal/WhereClauseBuilder.java | 4 ++ .../core/event/internal/EventImportHandler.kt | 12 +----- .../ProgramStageDataElementStore.java | 4 +- .../OldTrackerImporterPayloadGenerator.kt | 2 - .../internal/TrackedEntityDataValueStore.java | 2 + .../TrackedEntityDataValueStoreImpl.java | 32 +++++++++++---- .../internal/JobReportEventHandler.kt | 33 ++++++++------- 9 files changed, 94 insertions(+), 38 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt index efe09ff0fc..efd97fdb3b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt @@ -244,6 +244,6 @@ class OldTrackerImporterPayloadGeneratorMockIntegrationShould : BasePayloadGener assertThat(event1).isNotNull() val eventValues = event1!!.trackedEntityDataValues()!! - assertThat(eventValues.any { it.dataElement() == unassignedDataElementId}).isFalse() + assertThat(eventValues.any { it.dataElement() == unassignedDataElementId }).isFalse() } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java index 10967c7776..8558734f2a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java @@ -30,15 +30,25 @@ import com.google.common.collect.Lists; +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; +import org.hisp.dhis.android.core.common.ObjectWithUid; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.data.database.ObjectWithoutUidStoreAbstractIntegrationShould; +import org.hisp.dhis.android.core.data.dataelement.DataElementSamples; +import org.hisp.dhis.android.core.data.program.ProgramStageDataElementSamples; +import org.hisp.dhis.android.core.data.program.ProgramStageSamples; import org.hisp.dhis.android.core.data.trackedentity.EventSamples; import org.hisp.dhis.android.core.data.trackedentity.TrackedEntityDataValueSamples; import org.hisp.dhis.android.core.enrollment.Enrollment; import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore; import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStoreImpl; +import org.hisp.dhis.android.core.event.Event; import org.hisp.dhis.android.core.event.internal.EventStore; import org.hisp.dhis.android.core.event.internal.EventStoreImpl; +import org.hisp.dhis.android.core.program.ProgramStage; +import org.hisp.dhis.android.core.program.ProgramStageDataElement; +import org.hisp.dhis.android.core.program.internal.ProgramStageDataElementStore; +import org.hisp.dhis.android.core.program.internal.ProgramStageStore; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueTableInfo; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; @@ -179,4 +189,35 @@ public void select_tracker_data_values() { assertThat(trackerDataValues.get("event_1").iterator().next().event()).isEqualTo("event_1"); assertThat(trackerDataValues.get("event_2")).isNull(); } + + @Test + public void remove_unassigned_event_values() { + ProgramStage stage = ProgramStageSamples.getProgramStage(); + IdentifiableObjectStore stageStore = ProgramStageStore.create(TestDatabaseAdapterFactory.get()); + stageStore.insert(stage); + + Event event = EventSamples.get().toBuilder().uid("event_1").programStage(stage.uid()).build(); + EventStore eventStore = EventStoreImpl.create(TestDatabaseAdapterFactory.get()); + eventStore.insert(event); + + String dataElement1 = "data_element_1"; + String dataElement2 = "data_element_2"; + IdentifiableObjectStore psStore = + ProgramStageDataElementStore.create(TestDatabaseAdapterFactory.get()); + psStore.insert(ProgramStageDataElementSamples.getProgramStageDataElement().toBuilder() + .dataElement(DataElementSamples.getDataElement().toBuilder().uid(dataElement1).build()) + .programStage(ObjectWithUid.create(stage.uid())) + .build()); + + store.insert(TrackedEntityDataValueSamples.get() + .toBuilder().event(event.uid()).dataElement(dataElement1).build()); + store.insert(TrackedEntityDataValueSamples.get() + .toBuilder().event(event.uid()).dataElement(dataElement2).build()); + assertThat(store.queryTrackedEntityDataValuesByEventUid(event.uid()).size()).isEqualTo(2); + + store.removeUnassignedDataValuesByEvent(event.uid()); + List values = store.queryTrackedEntityDataValuesByEventUid(event.uid()); + assertThat(values.size()).isEqualTo(1); + assertThat(values.get(0).dataElement()).isEqualTo(dataElement1); + } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java index 38ae888865..dc8b02a56e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java @@ -133,6 +133,10 @@ public WhereClauseBuilder appendInSubQuery(String column, String subQuery) { return appendKeyValue(column, subQuery, AND, IN, PARENTHESES_END); } + public WhereClauseBuilder appendNotInSubQuery(String column, String subQuery) { + return appendKeyValue(column, subQuery, AND, NOT_IN, PARENTHESES_END); + } + public WhereClauseBuilder appendOrInSubQuery(String column, String subQuery) { return appendKeyValue(column, subQuery, OR, IN, PARENTHESES_END); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt index 1a10439b34..7e4243e189 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt @@ -43,7 +43,6 @@ import org.hisp.dhis.android.core.imports.internal.EventImportSummary import org.hisp.dhis.android.core.imports.internal.TEIWebResponseHandlerSummary import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityDataValueStore import org.hisp.dhis.android.core.tracker.importer.internal.JobReportEventHandler @Reusable @@ -54,7 +53,6 @@ internal class EventImportHandler @Inject constructor( private val trackerImportConflictParser: TrackerImportConflictParser, private val jobReportEventHandler: JobReportEventHandler, private val dataStatePropagator: DataStatePropagator, - private val trackedEntityDataValueStore: TrackedEntityDataValueStore ) { @Suppress("NestedBlockDepth") @@ -88,7 +86,7 @@ internal class EventImportHandler @Inject constructor( if (state == State.SYNCED && (handleAction == HandleAction.Update || handleAction == HandleAction.Insert) ) { - handleIfSynced(eventUid, state) + jobReportEventHandler.handleSyncedEvent(eventUid) } } } @@ -123,14 +121,6 @@ internal class EventImportHandler @Inject constructor( } } - private fun handleIfSynced( - eventUid: String, - state: State - ) { - jobReportEventHandler.handleEventNotes(eventUid, state) - trackedEntityDataValueStore.removeDeletedDataValuesByEvent(eventUid) - } - private fun storeEventImportConflicts( importSummary: EventImportSummary, enrollmentUid: String? diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageDataElementStore.java b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageDataElementStore.java index 96dd7e42ef..c5860bc43e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageDataElementStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageDataElementStore.java @@ -40,7 +40,7 @@ import androidx.annotation.NonNull; -final class ProgramStageDataElementStore { +public final class ProgramStageDataElementStore { private ProgramStageDataElementStore() {} @@ -62,7 +62,7 @@ public void bindToStatement(@NonNull ProgramStageDataElement programStageDataEle } }; - static IdentifiableObjectStore create(DatabaseAdapter databaseAdapter) { + public static IdentifiableObjectStore create(DatabaseAdapter databaseAdapter) { return StoreFactory.objectWithUidStore(databaseAdapter, ProgramStageDataElementTableInfo.TABLE_INFO, BINDER, ProgramStageDataElement::create); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt index cdfd15a069..8dc16f8e83 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt @@ -41,8 +41,6 @@ import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.event.EventInternalAccessor import org.hisp.dhis.android.core.event.internal.EventStore import org.hisp.dhis.android.core.note.Note -import org.hisp.dhis.android.core.program.ProgramStage -import org.hisp.dhis.android.core.program.ProgramStageDataElement import org.hisp.dhis.android.core.program.internal.ProgramStoreInterface import org.hisp.dhis.android.core.relationship.Relationship import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java index 0364a6bf39..ccc5838bd1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java @@ -51,4 +51,6 @@ public interface TrackedEntityDataValueStore extends ObjectWithoutUidStore queryToPostByEvent(@NonNull String eventUid); void removeDeletedDataValuesByEvent(@NonNull String eventUid); + + void removeUnassignedDataValuesByEvent(@NonNull String eventUid); } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java index a3a78fded7..45a204fe80 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java @@ -159,22 +159,38 @@ public Map> queryTrackerTrackedEntityDataVa @Override public List queryToPostByEvent(@NonNull String eventUid) { + String queryStatement = new WhereClauseBuilder() + .appendKeyStringValue(TrackedEntityDataValueTableInfo.Columns.EVENT, eventUid) + .appendInSubQuery( + TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT, + getInProgramStageDataElementsSubQuery(eventUid) + ).build(); + + return selectWhere(queryStatement); + } + + @Override + public void removeUnassignedDataValuesByEvent(@NonNull String eventUid) { + String queryStatement = new WhereClauseBuilder() + .appendKeyStringValue(TrackedEntityDataValueTableInfo.Columns.EVENT, eventUid) + .appendNotInSubQuery( + TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT, + getInProgramStageDataElementsSubQuery(eventUid) + ).build(); + + deleteWhere(queryStatement); + } + + private String getInProgramStageDataElementsSubQuery(@NonNull String eventUid) { String psDataElementName = ProgramStageDataElementTableInfo.TABLE_INFO.name(); String eventName = EventTableInfo.TABLE_INFO.name(); - String psDataElementQuery = "SELECT " + ProgramStageDataElementTableInfo.Columns.DATA_ELEMENT + + return "SELECT " + ProgramStageDataElementTableInfo.Columns.DATA_ELEMENT + " FROM " + psDataElementName + " INNER JOIN " + eventName + " ON " + psDataElementName + "." + ProgramStageDataElementTableInfo.Columns.PROGRAM_STAGE + " = " + eventName + "." + EventTableInfo.Columns.PROGRAM_STAGE + " WHERE " + eventName + "." + EventTableInfo.Columns.UID + " = '" + eventUid + "'"; - - String queryStatement = new WhereClauseBuilder() - .appendKeyStringValue(TrackedEntityDataValueTableInfo.Columns.EVENT, eventUid) - .appendInSubQuery(TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT, psDataElementQuery) - .build(); - - return selectWhere(queryStatement); } private Map> queryTrackedEntityDataValues(String queryStatement) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt index 17f912cf71..04f3a61f16 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt @@ -55,25 +55,12 @@ internal class JobReportEventHandler @Inject internal constructor( relationshipStore: RelationshipStore ) : JobReportTypeHandler(relationshipStore) { - fun handleEventNotes(eventUid: String, state: State) { - val newNoteState = if (state == State.SYNCED) State.SYNCED else State.TO_POST - val whereClause = WhereClauseBuilder() - .appendInKeyStringValues( - DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } - ) - .appendKeyStringValue(NoteTableInfo.Columns.EVENT, eventUid).build() - for (note in noteStore.selectWhere(whereClause)) { - noteStore.update(note.toBuilder().syncState(newNoteState).build()) - } - } - override fun handleObject(uid: String, state: State): HandleAction { conflictStore.deleteEventConflicts(uid) val handleAction = eventStore.setSyncStateOrDelete(uid, state) if (state == State.SYNCED && (handleAction == HandleAction.Update || handleAction == HandleAction.Insert)) { - handleEventNotes(uid, state) - trackedEntityDataValueStore.removeDeletedDataValuesByEvent(uid) + handleSyncedEvent(uid) } return handleAction @@ -102,4 +89,22 @@ internal class JobReportEventHandler @Inject internal constructor( override fun getRelatedRelationships(uid: String): List { return relationshipStore.getRelationshipsByItem(RelationshipHelper.eventItem(uid)).mapNotNull { it.uid() } } + + fun handleSyncedEvent(eventUid: String) { + handleEventNotes(eventUid, State.SYNCED) + trackedEntityDataValueStore.removeDeletedDataValuesByEvent(eventUid) + trackedEntityDataValueStore.removeUnassignedDataValuesByEvent(eventUid) + } + + private fun handleEventNotes(eventUid: String, state: State) { + val newNoteState = if (state == State.SYNCED) State.SYNCED else State.TO_POST + val whereClause = WhereClauseBuilder() + .appendInKeyStringValues( + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } + ) + .appendKeyStringValue(NoteTableInfo.Columns.EVENT, eventUid).build() + for (note in noteStore.selectWhere(whereClause)) { + noteStore.update(note.toBuilder().syncState(newNoteState).build()) + } + } } From f18a6128bc79c2ca5e2cbaf44d527e9a6e70d4d5 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 7 Jul 2023 13:07:57 +1000 Subject: [PATCH 08/24] [ANDROSDK-1706] Adapt EventImportHandler unit test --- .../android/core/event/internal/EventImportHandlerShould.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.kt index e490d0d75f..a4cfc0a6e6 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.kt @@ -36,7 +36,6 @@ import org.hisp.dhis.android.core.imports.ImportStatus import org.hisp.dhis.android.core.imports.internal.EventImportSummary import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityDataValueStore import org.hisp.dhis.android.core.tracker.importer.internal.JobReportEventHandler import org.junit.Before import org.junit.Test @@ -60,8 +59,6 @@ class EventImportHandlerShould { private val trackerImportConflictParser: TrackerImportConflictParser = mock() - private val trackedEntityDataValueStore: TrackedEntityDataValueStore = mock() - private val events: List = ArrayList() private val event: Event = mock() @@ -76,7 +73,7 @@ class EventImportHandlerShould { eventImportHandler = EventImportHandler( eventStore, enrollmentStore, trackerImportConflictStore, trackerImportConflictParser, jobReportEventHandler, - dataStatePropagator, trackedEntityDataValueStore + dataStatePropagator ) } From 741e20bebf0dc88098f2ad92dd9d9ab592fd3529 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 7 Jul 2023 13:52:21 +1000 Subject: [PATCH 09/24] [ANDROSDK-1706] Adapt store integration tests --- .../data/database/ObjectStoreAbstractIntegrationShould.kt | 6 ++++++ .../TrackedEntityDataValueStoreIntegrationShould.java | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt index a6fee2b337..16333d0398 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt @@ -34,6 +34,7 @@ import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore import org.hisp.dhis.android.core.arch.db.tableinfos.TableInfo import org.hisp.dhis.android.core.common.CoreObject +import org.junit.After import org.junit.Before import org.junit.Test @@ -54,6 +55,11 @@ abstract class ObjectStoreAbstractIntegrationShould internal con store.delete() } + @After + open fun tearDown() { + store.delete() + } + @Test fun insert_and_select_first_object() { store.insert(`object`) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java index 8558734f2a..2b0a05b3a0 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java @@ -83,7 +83,12 @@ protected TrackedEntityDataValue buildObject() { @After public void tearDown() { + super.tearDown(); + TrackedEntityInstanceStoreImpl.create(TestDatabaseAdapterFactory.get()).delete(); + EnrollmentStoreImpl.create(TestDatabaseAdapterFactory.get()).delete(); EventStoreImpl.create(TestDatabaseAdapterFactory.get()).delete(); + ProgramStageStore.create(TestDatabaseAdapterFactory.get()).delete(); + ProgramStageDataElementStore.create(TestDatabaseAdapterFactory.get()).delete(); } @Override @@ -192,7 +197,7 @@ public void select_tracker_data_values() { @Test public void remove_unassigned_event_values() { - ProgramStage stage = ProgramStageSamples.getProgramStage(); + ProgramStage stage = ProgramStageSamples.getProgramStage().toBuilder().uid("stage_uid").build(); IdentifiableObjectStore stageStore = ProgramStageStore.create(TestDatabaseAdapterFactory.get()); stageStore.insert(stage); @@ -205,6 +210,7 @@ public void remove_unassigned_event_values() { IdentifiableObjectStore psStore = ProgramStageDataElementStore.create(TestDatabaseAdapterFactory.get()); psStore.insert(ProgramStageDataElementSamples.getProgramStageDataElement().toBuilder() + .uid(dataElement1) .dataElement(DataElementSamples.getDataElement().toBuilder().uid(dataElement1).build()) .programStage(ObjectWithUid.create(stage.uid())) .build()); From 91b6c6386c99158b503b62fa8127bc073b6d0ccb Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Fri, 7 Jul 2023 10:27:13 +0200 Subject: [PATCH 10/24] [ANDROSDK-1715] Correct relationship orphan cleaner comparison bug --- .../relationship/internal/RelationshipOrphanCleanerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java index 30dbc2fb84..af705181ee 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java @@ -84,7 +84,7 @@ private boolean isSynced(State state) { private boolean isInRelationshipList(Relationship target, Collection list) { for (Relationship relationship : list) { - if (target.from() == null || target.to() == null || relationship.from() == null || target.to() == null) { + if (target.from() == null || target.to() == null || relationship.from() == null || relationship.to() == null) { continue; } if (areItemsEqual(target.from(), relationship.from()) && From 2b894dc1934c3011808f9682d72a4a47a98c27d1 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Fri, 7 Jul 2023 10:37:55 +0200 Subject: [PATCH 11/24] [ANDROSDK-1715] PMD --- .../relationship/internal/RelationshipOrphanCleanerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java index af705181ee..4267664c88 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java @@ -84,7 +84,8 @@ private boolean isSynced(State state) { private boolean isInRelationshipList(Relationship target, Collection list) { for (Relationship relationship : list) { - if (target.from() == null || target.to() == null || relationship.from() == null || relationship.to() == null) { + if (target.from() == null || target.to() == null || + relationship.from() == null || relationship.to() == null) { continue; } if (areItemsEqual(target.from(), relationship.from()) && From c5ba2931298ad6c9bab0514820741d54e6f23f17 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 11 Jul 2023 08:54:07 +0200 Subject: [PATCH 12/24] [ANDROSDK-1680] fix: - ",", ";", ":" characters in filters need to be escaped with "/" --- .../TrackedEntityInstanceQueryOnlineHelper.kt | 6 +++- ...edEntityInstanceQueryOnlineHelperShould.kt | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt index b9c390717c..8856daacfb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt @@ -158,13 +158,17 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( companion object { fun toAPIFilterFormat(items: List, upper: Boolean): List { + // Compatibility for the new Tracker (old Tracker will ignore this format) + // Following characters need to be escaped escaped with a "/" for backend functionality + val charsToEscape = "[;,:]".toRegex() + return items .groupBy { it.key() } .map { (key, items) -> val clause = items.map { item -> val operator = if (upper) item.operator().apiUpperOperator else item.operator().apiOperator - ":" + operator + ":" + getAPIValue(item) + ":" + operator + ":" + getAPIValue(item).replace(charsToEscape, "/\$0" ) } key + clause.joinToString(separator = "") diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt index 1f1fb53d23..2fa45b1790 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt @@ -34,6 +34,7 @@ import org.hisp.dhis.android.core.common.DateFilterPeriodHelper import org.hisp.dhis.android.core.common.FilterOperatorsHelper.listToStr import org.hisp.dhis.android.core.period.internal.CalendarProviderFactory.calendarProvider import org.hisp.dhis.android.core.period.internal.ParentPeriodGeneratorImpl.Companion.create +import org.hisp.dhis.android.core.trackedentity.search.TrackedEntityInstanceQueryOnlineHelper.Companion.toAPIFilterFormat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -85,4 +86,34 @@ class TrackedEntityInstanceQueryOnlineHelperShould { assertThat(onlineQueries.size).isEqualTo(1) assertThat(onlineQueries[0].attributeFilter.size).isEqualTo(1) } + + @Test + fun to_API_filter_format() { + // List of filters + val list = listOf( + "nom,app", + "nom:app", + "nom;app" + ) + + val expectedList = listOf( + "filterItem1:in:nom/,app/;nom/:app/;nom/;app", + "filterItem2:like:nom/,app%%%<>%%%nom/:app%%%<>%%%nom/;app" + ) + + val scope = queryBuilder + .filter( + listOf( + RepositoryScopeFilterItem.builder() + .key("filterItem1").operator(FilterItemOperator.IN).value(listToStr(list)).build(), + RepositoryScopeFilterItem.builder() + .key("filterItem2").operator(FilterItemOperator.LIKE).value(listToStr(list)).build() + ) + ).build() + + val formattedQueries = toAPIFilterFormat(scope.filter(), false) + + assertThat(formattedQueries).isEqualTo(expectedList) + } + } From b8024ceb737c9c8f2be351875b350d5f3ffef35f Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 11 Jul 2023 09:24:33 +0200 Subject: [PATCH 13/24] [ANDROSDK-1219] fix: - ",", ";", ":" characters in filters need to be escaped with "/" Cleaned up the code --- .../search/TrackedEntityInstanceQueryOnlineHelper.kt | 4 ++-- .../search/TrackedEntityInstanceQueryOnlineHelperShould.kt | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt index 8856daacfb..fe11af0eb6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt @@ -160,7 +160,7 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( fun toAPIFilterFormat(items: List, upper: Boolean): List { // Compatibility for the new Tracker (old Tracker will ignore this format) // Following characters need to be escaped escaped with a "/" for backend functionality - val charsToEscape = "[;,:]".toRegex() + val charsToEscape = "[;,:]".toRegex() return items .groupBy { it.key() } @@ -168,7 +168,7 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( val clause = items.map { item -> val operator = if (upper) item.operator().apiUpperOperator else item.operator().apiOperator - ":" + operator + ":" + getAPIValue(item).replace(charsToEscape, "/\$0" ) + ":" + operator + ":" + getAPIValue(item).replace(charsToEscape, "/\$0") } key + clause.joinToString(separator = "") diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt index 2fa45b1790..b665f34869 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt @@ -115,5 +115,4 @@ class TrackedEntityInstanceQueryOnlineHelperShould { assertThat(formattedQueries).isEqualTo(expectedList) } - } From 56751df9b9af66334ef4ee2d5b50fe4d789c3f49 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 18 Jul 2023 17:44:05 +0200 Subject: [PATCH 14/24] [ANDROSDK-1219] fix: - PR corrections for filter listToStr - PR corrections for ";" in "IN" filter --- .../TrackedEntityInstanceQueryOnlineHelper.kt | 9 +++++---- ...TrackedEntityInstanceQueryOnlineHelperShould.kt | 14 ++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt index fe11af0eb6..3fbd132c86 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt @@ -160,7 +160,6 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( fun toAPIFilterFormat(items: List, upper: Boolean): List { // Compatibility for the new Tracker (old Tracker will ignore this format) // Following characters need to be escaped escaped with a "/" for backend functionality - val charsToEscape = "[;,:]".toRegex() return items .groupBy { it.key() } @@ -168,7 +167,7 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( val clause = items.map { item -> val operator = if (upper) item.operator().apiUpperOperator else item.operator().apiOperator - ":" + operator + ":" + getAPIValue(item).replace(charsToEscape, "/\$0") + ":" + operator + ":" + getAPIValue(item) } key + clause.joinToString(separator = "") @@ -176,11 +175,13 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( } private fun getAPIValue(item: RepositoryScopeFilterItem): String { + val charsToEscape = "[;,:]".toRegex() + return if (item.operator() == FilterItemOperator.IN) { - val list = FilterOperatorsHelper.strToList(item.value()) + val list = FilterOperatorsHelper.strToList(item.value()).map { it.replace(charsToEscape, "/\$0") } list.joinToString(";") } else { - item.value() + item.value().replace(charsToEscape, "/\$0") } } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt index b665f34869..edf86876a3 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelperShould.kt @@ -97,17 +97,23 @@ class TrackedEntityInstanceQueryOnlineHelperShould { ) val expectedList = listOf( - "filterItem1:in:nom/,app/;nom/:app/;nom/;app", - "filterItem2:like:nom/,app%%%<>%%%nom/:app%%%<>%%%nom/;app" + "filterItemIN:in:nom/,app;nom/:app;nom/;app", + "filterItemLIKE1:like:nom/,app", + "filterItemLIKE2:like:nom/:app", + "filterItemLIKE3:like:nom/;app", ) val scope = queryBuilder .filter( listOf( RepositoryScopeFilterItem.builder() - .key("filterItem1").operator(FilterItemOperator.IN).value(listToStr(list)).build(), + .key("filterItemIN").operator(FilterItemOperator.IN).value(listToStr(list)).build(), RepositoryScopeFilterItem.builder() - .key("filterItem2").operator(FilterItemOperator.LIKE).value(listToStr(list)).build() + .key("filterItemLIKE1").operator(FilterItemOperator.LIKE).value(list[0]).build(), + RepositoryScopeFilterItem.builder() + .key("filterItemLIKE2").operator(FilterItemOperator.LIKE).value(list[1]).build(), + RepositoryScopeFilterItem.builder() + .key("filterItemLIKE3").operator(FilterItemOperator.LIKE).value(list[2]).build() ) ).build() From 9f71391c3c067b47396d96df96b42fadb422e209 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 19 Jul 2023 13:46:44 +0200 Subject: [PATCH 15/24] [ANDROSDK-1219] refactor: - PR corrections to improve escaping functionality --- .../search/TrackedEntityInstanceQueryOnlineHelper.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt index 3fbd132c86..9646a491c5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceQueryOnlineHelper.kt @@ -157,6 +157,7 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( } companion object { + fun toAPIFilterFormat(items: List, upper: Boolean): List { // Compatibility for the new Tracker (old Tracker will ignore this format) // Following characters need to be escaped escaped with a "/" for backend functionality @@ -175,14 +176,19 @@ internal class TrackedEntityInstanceQueryOnlineHelper @Inject constructor( } private fun getAPIValue(item: RepositoryScopeFilterItem): String { - val charsToEscape = "[;,:]".toRegex() return if (item.operator() == FilterItemOperator.IN) { - val list = FilterOperatorsHelper.strToList(item.value()).map { it.replace(charsToEscape, "/\$0") } + val list = FilterOperatorsHelper.strToList(item.value()).map { escapeChars(it) } list.joinToString(";") } else { - item.value().replace(charsToEscape, "/\$0") + escapeChars(item.value()) } } + + private fun escapeChars(targetString: String): String { + val charsToEscape = "[;,:]".toRegex() + val escapingChar = "/\$0" + return targetString.replace(charsToEscape, escapingChar) + } } } From a20572d90026d5674afe9520726bb89c20176fba Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 20 Jul 2023 12:53:51 +0200 Subject: [PATCH 16/24] [ANDROSDK-1719] Update D2Error column adapter with a default D2ErrorCode --- .../internal/D2ErrorCodeColumnAdapter.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java index d85738ca6f..809ef81b17 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java @@ -28,6 +28,9 @@ package org.hisp.dhis.android.core.arch.db.adapters.enums.internal; +import android.content.ContentValues; +import android.database.Cursor; + import org.hisp.dhis.android.core.maintenance.D2ErrorCode; public class D2ErrorCodeColumnAdapter extends EnumColumnAdapter { @@ -35,4 +38,32 @@ public class D2ErrorCodeColumnAdapter extends EnumColumnAdapter { protected Class getEnumClass() { return D2ErrorCode.class; } + + @Override + public D2ErrorCode fromCursor(Cursor cursor, String columnName) { + int columnIndex = cursor.getColumnIndex(columnName); + String sourceValue = cursor.getString(columnIndex); + + D2ErrorCode enumModel; + if (sourceValue != null) { + try { + enumModel = Enum.valueOf(getEnumClass(), sourceValue); + } catch (Exception exception) { + throw new RuntimeException("Unknown " + getEnumClass().getSimpleName() + " type", exception); + } + } else { + enumModel = D2ErrorCode.UNEXPECTED; + } + + return enumModel; + } + + @Override + public void toContentValues(ContentValues contentValues, String columnName, D2ErrorCode enumValue) { + if (enumValue != null) { + contentValues.put(columnName, enumValue.name()); + } else { + contentValues.put(columnName, D2ErrorCode.UNEXPECTED.name()); + } + } } \ No newline at end of file From 88064bef8de512d3edfa97e57d244007925d91d3 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 20 Jul 2023 12:54:03 +0200 Subject: [PATCH 17/24] [ANDROSDK-1719] Update D2Error builder with a default D2ErrorCode --- .../org/hisp/dhis/android/core/maintenance/D2Error.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java index b1018b82e6..c24fafef36 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java +++ b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2Error.java @@ -108,9 +108,15 @@ public static abstract class Builder extends BaseObject.Builder { public abstract Builder created(Date created); abstract D2Error autoBuild(); + abstract D2ErrorCode errorCode(); public D2Error build() { this.created(new Date()); + try { + errorCode(); + } catch (IllegalStateException e) { + errorCode(D2ErrorCode.UNEXPECTED); + } return autoBuild(); } } From 3658ae44d6da0d358f97cfc34a948296d740504c Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 24 Jul 2023 10:04:10 +0200 Subject: [PATCH 18/24] [ANDROSDK-1719] PMD --- .../enums/internal/D2ErrorCodeColumnAdapter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java index 809ef81b17..769e27186f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java @@ -45,14 +45,14 @@ public D2ErrorCode fromCursor(Cursor cursor, String columnName) { String sourceValue = cursor.getString(columnIndex); D2ErrorCode enumModel; - if (sourceValue != null) { + if (sourceValue == null) { + enumModel = D2ErrorCode.UNEXPECTED; + } else { try { enumModel = Enum.valueOf(getEnumClass(), sourceValue); } catch (Exception exception) { throw new RuntimeException("Unknown " + getEnumClass().getSimpleName() + " type", exception); } - } else { - enumModel = D2ErrorCode.UNEXPECTED; } return enumModel; @@ -60,10 +60,10 @@ public D2ErrorCode fromCursor(Cursor cursor, String columnName) { @Override public void toContentValues(ContentValues contentValues, String columnName, D2ErrorCode enumValue) { - if (enumValue != null) { - contentValues.put(columnName, enumValue.name()); - } else { + if (enumValue == null) { contentValues.put(columnName, D2ErrorCode.UNEXPECTED.name()); + } else { + contentValues.put(columnName, enumValue.name()); } } } \ No newline at end of file From 90abef7b95fa07ae35180208eddcadff8aa9559d Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 24 Jul 2023 11:16:56 +0200 Subject: [PATCH 19/24] [ANDROSDK-1719] Remove code smell --- .../internal/D2ErrorCodeColumnAdapter.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java index 769e27186f..c6604f7847 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java @@ -41,21 +41,8 @@ protected Class getEnumClass() { @Override public D2ErrorCode fromCursor(Cursor cursor, String columnName) { - int columnIndex = cursor.getColumnIndex(columnName); - String sourceValue = cursor.getString(columnIndex); - - D2ErrorCode enumModel; - if (sourceValue == null) { - enumModel = D2ErrorCode.UNEXPECTED; - } else { - try { - enumModel = Enum.valueOf(getEnumClass(), sourceValue); - } catch (Exception exception) { - throw new RuntimeException("Unknown " + getEnumClass().getSimpleName() + " type", exception); - } - } - - return enumModel; + D2ErrorCode d2ErrorCode = super.fromCursor(cursor, columnName); + return d2ErrorCode != null ? d2ErrorCode : D2ErrorCode.UNEXPECTED; } @Override From 126f98c242526fbb0c72b479309b993fd8fef4e8 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 24 Jul 2023 11:27:13 +0200 Subject: [PATCH 20/24] [ANDROSDK-1719] PMD --- .../db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java index c6604f7847..48ddea44aa 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/D2ErrorCodeColumnAdapter.java @@ -42,7 +42,7 @@ protected Class getEnumClass() { @Override public D2ErrorCode fromCursor(Cursor cursor, String columnName) { D2ErrorCode d2ErrorCode = super.fromCursor(cursor, columnName); - return d2ErrorCode != null ? d2ErrorCode : D2ErrorCode.UNEXPECTED; + return d2ErrorCode == null ? D2ErrorCode.UNEXPECTED : d2ErrorCode; } @Override From 7394cfc12e5204b8d0000731b1245601e5c72216 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 27 Jul 2023 14:53:36 +0200 Subject: [PATCH 21/24] [ANDROSDK-1719-2] Apply default errorCode --- .../arch/api/executors/internal/APICallExecutorImpl.java | 4 ++-- .../core/arch/api/executors/internal/APIErrorMapper.kt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APICallExecutorImpl.java b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APICallExecutorImpl.java index 08d6789edb..f65f451066 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APICallExecutorImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/executors/internal/APICallExecutorImpl.java @@ -107,7 +107,7 @@ private

P executeObjectCallInternal(Call

call, } else if (errorCatcher != null) { this.catchAndThrow(errorCatcher, errorBuilder(call), response, errorBody); } - throw storeAndReturn(errorMapper.responseException(errorBuilder(call), response, errorBody)); + throw storeAndReturn(errorMapper.responseException(errorBuilder(call), response, null, errorBody)); } } catch (D2Error d2Error) { throw d2Error; @@ -136,7 +136,7 @@ private

P processSuccessfulResponse(D2Error.Builder errorBuilder, Response

, - errorCode: D2ErrorCode? = D2ErrorCode.API_UNSUCCESSFUL_RESPONSE, + errorCode: D2ErrorCode?, errorBody: String? ): D2Error { + val code = errorCode ?: D2ErrorCode.API_UNSUCCESSFUL_RESPONSE val serverMessage = errorBody ?: getServerMessage(response) Log.e(this.javaClass.simpleName, serverMessage) return errorBuilder - .errorCode(errorCode) + .errorCode(code) .httpErrorCode(response.code()) .errorDescription("API call failed, server message: $serverMessage") .build() From 415e5ecfb170cfd9a3250044ad91b49868d11ccd Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 24 Aug 2023 13:00:35 +0200 Subject: [PATCH 22/24] [ANDROSDK-1739] Replace authentication encoding to support special characters --- .../internal/OpenIDConnectAuthenticator.kt | 6 +- .../PasswordAndCookieAuthenticator.kt | 12 +-- .../internal/UserIdAuthenticatorHelper.kt | 11 +++ .../{UserHelper.java => UserHelper.kt} | 84 +++++++++---------- .../core/arch/storage/internal/Credentials.kt | 2 +- .../android/core/user/internal/LogInCall.kt | 3 +- .../core/user/internal/UserService.java | 3 +- ...rHelperShould.java => UserHelperShould.kt} | 58 ++++++++----- 8 files changed, 100 insertions(+), 79 deletions(-) rename core/src/main/java/org/hisp/dhis/android/core/arch/helpers/{UserHelper.java => UserHelper.kt} (50%) rename core/src/test/java/org/hisp/dhis/android/core/arch/helpers/{UserHelperShould.java => UserHelperShould.kt} (52%) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/OpenIDConnectAuthenticator.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/OpenIDConnectAuthenticator.kt index 22b5f7c6d6..f77c0b0ee4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/OpenIDConnectAuthenticator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/OpenIDConnectAuthenticator.kt @@ -32,7 +32,6 @@ import javax.inject.Inject import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response -import org.hisp.dhis.android.core.arch.api.authentication.internal.UserIdAuthenticatorHelper.Companion.AUTHORIZATION_KEY import org.hisp.dhis.android.core.arch.storage.internal.Credentials import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore import org.hisp.dhis.android.core.user.openid.OpenIDConnectLogoutHandler @@ -70,6 +69,9 @@ internal class OpenIDConnectAuthenticator @Inject constructor( } private fun addTokenHeader(builder: Request.Builder, token: String): Request.Builder { - return builder.addHeader(AUTHORIZATION_KEY, "Bearer $token") + return builder.addHeader( + UserIdAuthenticatorHelper.AUTHORIZATION_KEY, + "Bearer $token" + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/PasswordAndCookieAuthenticator.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/PasswordAndCookieAuthenticator.kt index dac0bce87e..7d96ba3eb5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/PasswordAndCookieAuthenticator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/PasswordAndCookieAuthenticator.kt @@ -32,8 +32,6 @@ import javax.inject.Inject import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response -import org.hisp.dhis.android.core.arch.api.authentication.internal.UserIdAuthenticatorHelper.Companion.AUTHORIZATION_KEY -import org.hisp.dhis.android.core.arch.helpers.UserHelper import org.hisp.dhis.android.core.arch.storage.internal.Credentials @Reusable @@ -73,11 +71,9 @@ internal class PasswordAndCookieAuthenticator @Inject constructor( } private fun addPasswordHeader(builder: Request.Builder, credentials: Credentials): Request.Builder { - return builder.addHeader(AUTHORIZATION_KEY, getAuthorizationForPassword(credentials)) - } - - private fun getAuthorizationForPassword(credentials: Credentials): String { - val base64Credentials = UserHelper.base64(credentials.username, credentials.password) - return "Basic $base64Credentials" + return builder.addHeader( + UserIdAuthenticatorHelper.AUTHORIZATION_KEY, + UserIdAuthenticatorHelper.basic(credentials) + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/UserIdAuthenticatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/UserIdAuthenticatorHelper.kt index 932173cacb..72fa9883b1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/UserIdAuthenticatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/api/authentication/internal/UserIdAuthenticatorHelper.kt @@ -31,6 +31,8 @@ import dagger.Reusable import javax.inject.Inject import okhttp3.Interceptor import okhttp3.Request +import org.hisp.dhis.android.core.arch.helpers.UserHelper +import org.hisp.dhis.android.core.arch.storage.internal.Credentials import org.hisp.dhis.android.core.arch.storage.internal.UserIdInMemoryStore @Reusable @@ -41,6 +43,15 @@ internal class UserIdAuthenticatorHelper @Inject constructor( companion object { const val AUTHORIZATION_KEY = "Authorization" private const val USER_ID_KEY = "x-dhis2-user-id" + + fun basic(credentials: Credentials): String { + return basic(credentials.username, credentials.password!!) + } + + fun basic(username: String, password: String): String { + val base64Credentials = UserHelper.base64(username, password) + return "Basic $base64Credentials" + } } fun builderWithUserId(chain: Interceptor.Chain): Request.Builder { diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/UserHelper.java b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/UserHelper.kt similarity index 50% rename from core/src/main/java/org/hisp/dhis/android/core/arch/helpers/UserHelper.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/helpers/UserHelper.kt index 5bd7aa5ad7..825c7fc35a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/UserHelper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/UserHelper.kt @@ -25,77 +25,71 @@ * (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.android.core.arch.helpers -package org.hisp.dhis.android.core.arch.helpers; - - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import okio.ByteString; - -public final class UserHelper { - - private UserHelper() { - // no instances - } +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import okio.ByteString +object UserHelper { /** - * Encode the given username and password to a base 64 {@link String}. + * Encode the given username and password to a base 64 [String]. * * @param username The username of the user account. * @param password The password of the user account. - * @return An encoded base 64 {@link String}. + * @return An encoded base 64 [String]. */ - public static String base64(String username, String password) { - return base64(username + ":" + password); + fun base64(username: String, password: String): String { + return base64(usernameAndPassword(username, password)) } /** - * Encode the given string to a base 64 {@link String}. + * Encode the given string to a base 64 [String]. * * @param value The value to encode - * @return An encoded base 64 {@link String}. + * @return An encoded base 64 [String]. */ - public static String base64(String value) { - byte[] bytes = value.getBytes(StandardCharsets.ISO_8859_1); - return ByteString.of(bytes).base64(); + @Suppress("SpreadOperator") + fun base64(value: String): String { + val bytes = value.toByteArray(StandardCharsets.UTF_8) + return ByteString.of(*bytes).base64() } /** - * Encode the given username and password to a MD5 {@link String}. + * Encode the given username and password to a MD5 [String]. * * @param username The username of the user account. * @param password The password of the user account. - * @return An encoded MD5 {@link String}. + * @return An encoded MD5 [String]. */ - @SuppressWarnings({"PMD.UseLocaleWithCaseConversions"}) - public static String md5(String username, String password) { - try { - String credentials = usernameAndPassword(username, password); - MessageDigest md = MessageDigest.getInstance("MD5"); - md.reset(); - md.update(credentials.getBytes(StandardCharsets.ISO_8859_1)); - return bytesToHex(md.digest()).toLowerCase(); - } catch (NoSuchAlgorithmException noSuchAlgorithmException) { + fun md5(username: String, password: String): String { + return try { + val credentials = usernameAndPassword(username, password) + val md = MessageDigest.getInstance("MD5") + md.reset() + md.update(credentials.toByteArray(StandardCharsets.UTF_8)) + bytesToHex(md.digest()).lowercase() + } catch (noSuchAlgorithmException: NoSuchAlgorithmException) { // noop. Every implementation of Java is required to support MD5 - throw new AssertionError(noSuchAlgorithmException); + throw AssertionError(noSuchAlgorithmException) } } - private static String bytesToHex(byte[] bytes) { - char[] hexArray = "0123456789ABCDEF".toCharArray(); - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + @Suppress("MagicNumber") + private fun bytesToHex(bytes: ByteArray): String { + val hexArray = "0123456789ABCDEF".toCharArray() + val hexChars = CharArray(bytes.size * 2) + + for (j in bytes.indices) { + val v = bytes[j].toInt() and 0xFF + hexChars[j * 2] = hexArray[v ushr 4] + hexChars[j * 2 + 1] = hexArray[v and 0x0F] } - return new String(hexChars); + return String(hexChars) } - private static String usernameAndPassword(String username, String password) { - return username + ":" + password; + private fun usernameAndPassword(username: String, password: String): String { + return "$username:$password" } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/storage/internal/Credentials.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/storage/internal/Credentials.kt index 46c7661018..09fa92a350 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/storage/internal/Credentials.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/storage/internal/Credentials.kt @@ -37,7 +37,7 @@ data class Credentials( val openIDConnectState: AuthState? ) { fun getHash(): String? { - return password.let { UserHelper.md5(username, it) } + return password?.let { UserHelper.md5(username, it) } } override fun equals(other: Any?) = diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt index a095eb7ce1..cfd46ba415 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt @@ -37,6 +37,7 @@ import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.arch.helpers.UserHelper import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository import org.hisp.dhis.android.core.arch.storage.internal.Credentials import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore @@ -87,7 +88,7 @@ internal class LogInCall @Inject internal constructor( ServerURLWrapper.setServerUrl(parsedServerUrl.toString()) val authenticateCall = userService.authenticate( - okhttp3.Credentials.basic(username!!, password!!), + UserHelper.base64(username!!, password!!), UserFields.allFieldsWithoutOrgUnit(null) ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserService.java b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserService.java index d3363fcf94..0e2b651453 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserService.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserService.java @@ -28,6 +28,7 @@ package org.hisp.dhis.android.core.user.internal; +import org.hisp.dhis.android.core.arch.api.authentication.internal.UserIdAuthenticatorHelper; import org.hisp.dhis.android.core.arch.api.fields.internal.Fields; import org.hisp.dhis.android.core.arch.api.filters.internal.Which; import org.hisp.dhis.android.core.user.User; @@ -40,7 +41,7 @@ interface UserService { @GET("me") - Call authenticate(@Header("Authorization") String credentials, + Call authenticate(@Header(UserIdAuthenticatorHelper.AUTHORIZATION_KEY) String credentials, @Query("fields") @Which Fields fields); @GET("me") diff --git a/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/UserHelperShould.java b/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/UserHelperShould.kt similarity index 52% rename from core/src/test/java/org/hisp/dhis/android/core/arch/helpers/UserHelperShould.java rename to core/src/test/java/org/hisp/dhis/android/core/arch/helpers/UserHelperShould.kt index 77ec448982..b6d74b25e6 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/UserHelperShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/UserHelperShould.kt @@ -25,38 +25,54 @@ * (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.android.core.arch.helpers -package org.hisp.dhis.android.core.arch.helpers; +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import static com.google.common.truth.Truth.assertThat; +@RunWith(JUnit4::class) +class UserHelperShould { + @Test + fun md5_evaluate_same_string() { + val md5s1 = UserHelper.md5("user1", "password1") + val md5s2 = UserHelper.md5("user1", "password1") -@RunWith(JUnit4.class) -public class UserHelperShould { + assertThat(md5s1.length).isEqualTo(32) + assertThat(md5s2.length).isEqualTo(32) + assertThat(md5s1 == md5s2).isTrue() + } @Test - public void md5_evaluate_same_string() { - String md5s1 = UserHelper.md5("user1","password1"); - String md5s2 = UserHelper.md5("user1","password1"); - - assertThat(md5s1.length()).isEqualTo(32); - assertThat(md5s2.length()).isEqualTo(32); + fun md5_evaluate_different_string() { + val md5s1 = UserHelper.md5("user2", "password2") + val md5s2 = UserHelper.md5("user3", "password3") - assertThat(md5s1.equals(md5s2)).isTrue(); + assertThat(md5s1.length).isEqualTo(32) + assertThat(md5s2.length).isEqualTo(32) + assertThat(md5s1 == md5s2).isFalse() } @Test - public void md5_evaluate_different_string() { - String md5s1 = UserHelper.md5("user2", "password2"); - String md5s2 = UserHelper.md5("user3", "password3"); + fun md5_evaluate_special_chars() { + val md5s1 = UserHelper.md5("user1", "pässword") + val md5s2 = UserHelper.md5("user1", "password") - assertThat(md5s1.length()).isEqualTo(32); - assertThat(md5s2.length()).isEqualTo(32); + assertThat(md5s1.length).isEqualTo(32) + assertThat(md5s2.length).isEqualTo(32) + assertThat(md5s1 == md5s2).isFalse() + } - assertThat(md5s1.equals(md5s2)).isFalse(); + @Test + fun base64_encode_credentials() { + val base64 = UserHelper.base64("user", "password") + assertThat(base64).isEqualTo("dXNlcjpwYXNzd29yZA==") } + @Test + fun base64_encode_special_chars() { + val base64 = UserHelper.base64("user", "pässword") + assertThat(base64).isEqualTo("dXNlcjpww6Rzc3dvcmQ=") + } } From 1d2197559d39136c1598954bde12dc3c17575f3b Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 24 Aug 2023 13:09:26 +0200 Subject: [PATCH 23/24] [ANDROSDK-1739] Use Basic in LoginCall --- .../org/hisp/dhis/android/core/user/internal/LogInCall.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt index cfd46ba415..f2bc238cd6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt @@ -31,13 +31,13 @@ import dagger.Reusable import io.reactivex.Single import javax.inject.Inject import net.openid.appauth.AuthState +import org.hisp.dhis.android.core.arch.api.authentication.internal.UserIdAuthenticatorHelper import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.api.internal.ServerURLWrapper import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore import org.hisp.dhis.android.core.arch.handlers.internal.Handler -import org.hisp.dhis.android.core.arch.helpers.UserHelper import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository import org.hisp.dhis.android.core.arch.storage.internal.Credentials import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore @@ -88,7 +88,7 @@ internal class LogInCall @Inject internal constructor( ServerURLWrapper.setServerUrl(parsedServerUrl.toString()) val authenticateCall = userService.authenticate( - UserHelper.base64(username!!, password!!), + UserIdAuthenticatorHelper.basic(username!!, password!!), UserFields.allFieldsWithoutOrgUnit(null) ) From e4c7721e2544af1c514a282c5d798da602173b88 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 30 Aug 2023 09:51:16 +0200 Subject: [PATCH 24/24] [1.8.2-RC] Version 1.8.2 --- core/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/gradle.properties b/core/gradle.properties index c782b7cf6a..b92d881dff 100644 --- a/core/gradle.properties +++ b/core/gradle.properties @@ -29,7 +29,7 @@ # Properties which are consumed by plugins/gradle-mvn-push.gradle plugin. # They are used for publishing artifact to snapshot repository. -VERSION_NAME=1.8.2-SNAPSHOT +VERSION_NAME=1.8.2 VERSION_CODE=283 GROUP=org.hisp.dhis