Skip to content

Commit

Permalink
Merge pull request #38 from niscy-eudiw/feat/service_error_message
Browse files Browse the repository at this point in the history
Proper error messages, new PDF Viewer to support annotations.
  • Loading branch information
stzouvaras authored Dec 5, 2024
2 parents e962b57 + be06307 commit 119b878
Show file tree
Hide file tree
Showing 12 changed files with 51 additions and 38 deletions.
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ org.gradle.jvmargs=-Xmx8192m -Dfile.encoding=UTF-8
android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true
android.enableJetifier=true

# API PROPERTIES
SDK_VERSION=35
TARGET_SDK_VERSION=35
MIN_SDK_VERSION=28

# PROJECT PROPERTIES
VERSION_NAME=0.1.0
VERSION_NAME=0.1.1-snapshot
NAMESPACE=eu.europa.ec.eudi.rqesui
GROUP=eu.europa.ec.eudi

Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mockitoKotlin = "5.3.1"
kover = "0.7.5"
robolectric = "4.11.1"
turbine = "1.1.0"
pdfViewer = "3.2.0-beta.1"

[libraries]
eudi-lib-android-rqes-core = { module = "eu.europa.ec.eudi:eudi-lib-android-rqes-core", version.ref = "eudiRqesCore" }
Expand Down Expand Up @@ -59,6 +60,7 @@ mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", versio
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }
robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" }
android-pdf-viewer = { group = "com.github.barteksc", name = "android-pdf-viewer", version.ref = "pdfViewer" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down
3 changes: 3 additions & 0 deletions rqes-ui-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ dependencies {
//Gson
implementation(libs.gson)

// PDF
implementation(libs.android.pdf.viewer)

// Test Dependencies
testImplementation(libs.junit)
testImplementation(libs.koin.test)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ internal class RqesControllerImpl(
private val genericErrorMsg
get() = resourceProvider.genericErrorMessage()

private val genericServiceErrorMsg
get() = resourceProvider.genericServiceErrorMessage()

override fun getSelectedFile(): EudiRqesGetSelectedFilePartialState {
return runCatching {
val selectedFile = eudiRQESUi.getSessionData().file
Expand Down Expand Up @@ -170,7 +173,7 @@ internal class RqesControllerImpl(
}.getOrElse {
EudiRqesGetServiceAuthorizationUrlPartialState.Failure(
error = EudiRQESUiError(
message = it.localizedMessage ?: genericErrorMsg
message = genericServiceErrorMsg
)
)
}
Expand All @@ -197,7 +200,7 @@ internal class RqesControllerImpl(
}.getOrElse {
EudiRqesAuthorizeServicePartialState.Failure(
error = EudiRQESUiError(
message = it.localizedMessage ?: genericErrorMsg
message = genericServiceErrorMsg
)
)
}
Expand Down Expand Up @@ -237,7 +240,7 @@ internal class RqesControllerImpl(
}.getOrElse {
EudiRqesGetCertificatesPartialState.Failure(
error = EudiRQESUiError(
message = it.localizedMessage ?: genericErrorMsg
message = genericServiceErrorMsg
)
)
}
Expand Down Expand Up @@ -284,7 +287,7 @@ internal class RqesControllerImpl(
}.getOrElse {
EudiRqesGetCredentialAuthorizationUrlPartialState.Failure(
error = EudiRQESUiError(
message = it.localizedMessage ?: genericErrorMsg
message = genericServiceErrorMsg
)
)
}
Expand Down Expand Up @@ -312,7 +315,7 @@ internal class RqesControllerImpl(
}.getOrElse {
EudiRqesAuthorizeCredentialPartialState.Failure(
error = EudiRQESUiError(
message = it.localizedMessage ?: genericErrorMsg
message = genericServiceErrorMsg
)
)
}
Expand All @@ -327,7 +330,7 @@ internal class RqesControllerImpl(
}.getOrElse {
EudiRqesSignDocumentsPartialState.Failure(
error = EudiRQESUiError(
message = it.localizedMessage ?: genericErrorMsg
message = genericServiceErrorMsg
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ enum class LocalizableKey {
CloseSharingMessage,
GenericErrorButtonRetry,
GenericErrorMessage,
GenericServiceErrorMessage,
GenericErrorDescription,
GenericErrorDocumentNotFound,
GenericErrorQtspNotFound,
Expand Down Expand Up @@ -85,6 +86,7 @@ enum class LocalizableKey {
CloseSharingMessage -> "Closing will redirect you back to the dashboard without saving or sharing the document."
GenericErrorButtonRetry -> "TRY AGAIN"
GenericErrorMessage -> "Oups! Something went wrong"
GenericServiceErrorMessage -> "It seems the RQES Signing Service is unavailable. Please try again later."
GenericErrorDescription -> "If the issue persists, please contact customer support"
GenericErrorDocumentNotFound -> "No Document data found"
GenericErrorQtspNotFound -> "No selected QTSP found"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ internal interface ResourceProvider {
fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any): String
fun getString(@StringRes resId: Int, vararg formatArgs: Any): String
fun genericErrorMessage(): String
fun genericServiceErrorMessage(): String
fun getLocalizedString(localizableKey: LocalizableKey, args: List<String> = emptyList()): String
}

Expand All @@ -46,6 +47,9 @@ internal class ResourceProviderImpl(

override fun genericErrorMessage() = getLocalizedString(LocalizableKey.GenericErrorDescription)

override fun genericServiceErrorMessage() =
getLocalizedString(LocalizableKey.GenericServiceErrorMessage)

override fun getString(@StringRes resId: Int): String =
try {
context.getString(resId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package eu.europa.ec.eudi.rqesui.presentation.ui.view_document

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -28,24 +27,21 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavController
import com.github.barteksc.pdfviewer.PDFView
import eu.europa.ec.eudi.rqesui.domain.extension.toUri
import eu.europa.ec.eudi.rqesui.infrastructure.config.data.DocumentData
import eu.europa.ec.eudi.rqesui.infrastructure.theme.values.ThemeColors
import eu.europa.ec.eudi.rqesui.infrastructure.theme.values.divider
import eu.europa.ec.eudi.rqesui.presentation.entities.config.ViewDocumentUiConfig
import eu.europa.ec.eudi.rqesui.presentation.extension.finish
import eu.europa.ec.eudi.rqesui.presentation.ui.component.AppIcons
import eu.europa.ec.eudi.rqesui.presentation.ui.component.content.ContentScreen
import eu.europa.ec.eudi.rqesui.presentation.ui.component.content.ScreenNavigateAction
import eu.europa.ec.eudi.rqesui.presentation.ui.component.content.ToolbarAction
import eu.europa.ec.eudi.rqesui.presentation.ui.component.content.ToolbarConfig
import eu.europa.ec.eudi.rqesui.presentation.ui.component.pdf.PdfViewer
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.PreviewTheme
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.ThemeModePreviews
import eu.europa.ec.eudi.rqesui.presentation.ui.component.utils.SPACING_LARGE
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
Expand All @@ -57,7 +53,6 @@ internal fun ViewDocumentScreen(
navController: NavController,
viewModel: ViewDocumentViewModel,
) {
val context = LocalContext.current
val state = viewModel.viewState.value

ContentScreen(
Expand All @@ -77,7 +72,6 @@ internal fun ViewDocumentScreen(
onEventSend = { viewModel.setEvent(it) },
onNavigationRequested = { navigationEffect ->
when (navigationEffect) {
is Effect.Navigation.Finish -> context.finish()
is Effect.Navigation.Pop -> navController.popBackStack()
}
},
Expand All @@ -102,19 +96,19 @@ private fun Content(
verticalArrangement = Arrangement.Top
) {
state.config.documentData.let { file ->
Box(modifier = Modifier.fillMaxSize()) {
PdfViewer(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = SPACING_LARGE.dp),
documentUri = file.uri,
onLoadingListener = { isLoading ->
onEventSend(
Event.LoadingStateChanged(isLoading = isLoading)
)
}
)
}
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context -> PDFView(context, null) },
update = { pdfView ->
pdfView
.fromUri(file.uri)
.enableAnnotationRendering(true)
.onLoad {
onEventSend(Event.LoadingStateChanged(isLoading = false))
}
.load()
}
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ internal sealed class Event : ViewEvent {

internal sealed class Effect : ViewSideEffect {
sealed class Navigation : Effect() {

data object Finish : Navigation()
data object Pop : Navigation()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import eu.europa.ec.eudi.rqesui.util.mockedDocumentNotFoundMessage
import eu.europa.ec.eudi.rqesui.util.mockedExceptionWithMessage
import eu.europa.ec.eudi.rqesui.util.mockedExceptionWithNoMessage
import eu.europa.ec.eudi.rqesui.util.mockedGenericErrorMessage
import eu.europa.ec.eudi.rqesui.util.mockedGenericServiceErrorMessage
import eu.europa.ec.eudi.rqesui.util.mockedQtspEndpoint
import eu.europa.ec.eudi.rqesui.util.mockedQtspName
import eu.europa.ec.eudi.rqesui.util.mockedQtspNotFound
Expand Down Expand Up @@ -125,6 +126,8 @@ class TestRqesController {
)
whenever(resourceProvider.genericErrorMessage())
.thenReturn(mockedGenericErrorMessage)
whenever(resourceProvider.genericServiceErrorMessage())
.thenReturn(mockedGenericServiceErrorMessage)
}

@After
Expand Down Expand Up @@ -442,7 +445,7 @@ class TestRqesController {
// Assert
assertTrue(result is EudiRqesSignDocumentsPartialState.Failure)
assertEquals(
mockedExceptionWithMessage.message,
mockedGenericServiceErrorMessage,
(result as EudiRqesSignDocumentsPartialState.Failure).error.message
)
}
Expand Down Expand Up @@ -510,21 +513,20 @@ class TestRqesController {
fun `Given Case 3, When authorizeCredential is called, Then the expected result is returned`() =
coroutineRule.runTest {
// Arrange
val authorizationFailedMessage = "Authorization failed"
mockSessionData()
whenever(sessionData.authorizationCode).thenReturn(mockedAuthorizationCode)
whenever(eudiRQESUi.getAuthorizedService()).thenReturn(rqesServiceAuthorized)
whenever(
rqesServiceAuthorized.authorizeCredential(AuthorizationCode(mockedAuthorizationCode)),
).thenThrow(RuntimeException(authorizationFailedMessage))
).thenThrow(mockedExceptionWithMessage)

// Act
val result = rqesController.authorizeCredential()

// Assert
assertTrue(result is EudiRqesAuthorizeCredentialPartialState.Failure)
assertEquals(
authorizationFailedMessage,
mockedGenericServiceErrorMessage,
(result as EudiRqesAuthorizeCredentialPartialState.Failure).error.message,
)
}
Expand Down Expand Up @@ -576,7 +578,7 @@ class TestRqesController {
// Assert
assertTrue(result is EudiRqesGetServiceAuthorizationUrlPartialState.Failure)
assertEquals(
mockedExceptionWithMessage.message,
mockedGenericServiceErrorMessage,
(result as EudiRqesGetServiceAuthorizationUrlPartialState.Failure).error.message,
)
}
Expand Down Expand Up @@ -677,7 +679,7 @@ class TestRqesController {
// Assert
assertTrue(result is EudiRqesAuthorizeServicePartialState.Failure)
assertEquals(
mockedExceptionWithMessage.message,
mockedGenericServiceErrorMessage,
(result as EudiRqesAuthorizeServicePartialState.Failure).error.message,
)
}
Expand Down Expand Up @@ -759,7 +761,7 @@ class TestRqesController {
// Assert
assertTrue(result is EudiRqesGetCertificatesPartialState.Failure)
assertEquals(
mockedExceptionWithMessage.message,
mockedGenericServiceErrorMessage,
(result as EudiRqesGetCertificatesPartialState.Failure).error.message,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package eu.europa.ec.eudi.rqesui.util

const val mockedPlainFailureMessage = "Failure message"
const val mockedGenericErrorMessage = "resourceProvider's genericErrorMessage"
const val mockedGenericServiceErrorMessage = "resourceProvider's genericServiceErrorMessage"
const val mockedAuthorizationUrl = "https://endpoint.com/mockedAuthorizationUrl"
const val mockedDocumentName = "Document.pdf"
const val mockedLocalizedText = "Localized text"
Expand Down
3 changes: 3 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ dependencyResolutionManagement {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
mavenContent { snapshotsOnly() }
}
maven {
url = uri("https://repository.liferay.com/nexus/content/repositories/public/")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private class DefaultConfig : EudiRQESUiConfig {
get() = RqesServiceConfig(
clientId = "wallet-client-tester",
clientSecret = "somesecrettester2",
authFlowRedirectionURI = URI.create("rQES://oauth/callback"),
authFlowRedirectionURI = URI.create("rqes://oauth/callback"),
hashAlgorithm = HashAlgorithmOID.SHA_256,
)

Expand Down

0 comments on commit 119b878

Please sign in to comment.