From 8d576ec7f3fd7943525d4176e92263473ed4a8ab Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 23 Jun 2024 18:30:16 +0200 Subject: [PATCH 01/18] Added Result and TokensExchange messages, adapted handler --- .../dedis/popstellar/di/DataRegistryModule.kt | 16 ++++ .../network/method/message/data/Action.kt | 1 + .../data/federation/FederationResult.kt | 76 +++++++++++++++++++ .../message/data/federation/TokensExchange.kt | 52 +++++++++++++ .../LinkedOrganizationsRepository.kt | 5 ++ .../federation/LinkedOrganizationsFragment.kt | 11 +++ .../LinkedOrganizationsViewModel.kt | 4 + .../data/LinkedOrganizationsHandler.kt | 43 +++++++++++ .../layout/linked_organizations_fragment.xml | 9 ++- .../app/src/main/res/values/strings.xml | 1 + 10 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt create mode 100644 fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt index f4a612b8c5..0057f25465 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt @@ -19,6 +19,8 @@ import com.github.dedis.popstellar.model.network.method.message.data.election.El import com.github.dedis.popstellar.model.network.method.message.data.election.ElectionResult import com.github.dedis.popstellar.model.network.method.message.data.election.ElectionSetup import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange import com.github.dedis.popstellar.model.network.method.message.data.lao.CreateLao import com.github.dedis.popstellar.model.network.method.message.data.lao.GreetLao import com.github.dedis.popstellar.model.network.method.message.data.lao.StateLao @@ -259,6 +261,18 @@ object DataRegistryModule { linkedOrganizationsHandler.handleChallenge(context, challenge) } + builder.add(Objects.FEDERATION, Action.RESULT, FederationResult::class.java) { + context: HandlerContext, + result: FederationResult -> + linkedOrganizationsHandler.handleResult(context, result) + } + + builder.add(Objects.FEDERATION, Action.TOKENS_EXCHANGE, TokensExchange::class.java) { + context: HandlerContext, + tokenExchange: TokensExchange -> + linkedOrganizationsHandler.handleTokensExchange(context, tokenExchange) + } + return builder.build() } @@ -327,6 +341,8 @@ object DataRegistryModule { // Federation builder.add(Objects.FEDERATION, Action.CHALLENGE, Challenge::class.java, null) + builder.add(Objects.FEDERATION, Action.RESULT, FederationResult::class.java, null) + builder.add(Objects.FEDERATION, Action.TOKENS_EXCHANGE, TokensExchange::class.java, null) return builder.build() } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt index db8b5fde08..a663b6d3c3 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt @@ -44,6 +44,7 @@ enum class Action CHALLENGE("challenge"), INIT("init"), EXPECT("expect"), + TOKENS_EXCHANGE("tokens_exchange"), RUMOR("rumor"); /** diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt new file mode 100644 index 0000000000..feb821395b --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -0,0 +1,76 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Data +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.google.gson.annotations.SerializedName + +/** Informs about the result of the authentication procedure */ +class FederationResult +/** + * Constructor for a data Federation Result + * + * @param status status of the result (either success or failure) + * @param reason reason of the failure + * @param publicKey public key of the other LAO organizer + * @param challenge challenge used to connect the LAOs + */ +( + val status: String, + val reason: String? = null, + @SerializedName("public_key") val publicKey: String? = null, + val challenge: Challenge, +) : Data { + + init { + when (status) { + "failure" -> { + require(reason != null) { "Reason must be provided for failure status." } + require(publicKey == null) { "Public key must be null for failure status." } + } + "success" -> { + require(publicKey != null) { "Public key must be provided for success status." } + require(reason == null) { "Reason must be null for success status." } + } + else -> throw IllegalArgumentException("Status must be either 'failure' or 'success'.") + } + } + + override val `object`: String + get() = Objects.FEDERATION.`object` + + override val action: String + get() = Action.RESULT.action + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null || javaClass != other.javaClass) { + return false + } + val that = other as FederationResult + return status == that.status && + reason == that.reason && + publicKey == that.publicKey && + challenge == that.challenge + } + + override fun hashCode(): Int { + return java.util.Objects.hash(status, reason, publicKey, challenge) + } + + override fun toString(): String { + if (status == "failure") { + return "FederationResult{status='$status', reason='$reason'," + "challenge='$challenge'}" + } else if (status == "success") { + return "FederationResult{status='$status', public_key='$publicKey'," + + "challenge='$challenge'}" + } + return "FederationResult{ERROR}" + } + + fun isSuccess(): Boolean { + return status == "success" + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt new file mode 100644 index 0000000000..5ae6bc4bc0 --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt @@ -0,0 +1,52 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Data +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.google.gson.annotations.SerializedName + +/** Token exchange to be broadcast in the LAO */ +class TokensExchange +/** + * Constructor for a data TokenExchange + * + * @param laoId ID of the remote LAO + * @param rollCallId ID of the rollCall of the remote LAO + * @param tokens array of tokens contained in the rollCall + * @param timestamp timestamp of the message + */ +( + @SerializedName("lao_id") val laoId: String, + @SerializedName("roll_call_id") val rollCallId: String, + val tokens: Array, + val timestamp: Long +) : Data { + override val `object`: String + get() = Objects.FEDERATION.`object` + + override val action: String + get() = Action.TOKENS_EXCHANGE.action + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null || javaClass != other.javaClass) { + return false + } + val that = other as TokensExchange + return laoId == that.laoId && + rollCallId == that.rollCallId && + tokens.contentEquals(that.tokens) && + timestamp == that.timestamp + } + + override fun hashCode(): Int { + return java.util.Objects.hash(laoId, rollCallId, tokens, timestamp) + } + + override fun toString(): String { + return "TokensExchange{lao_id='$laoId', roll_call_id='$rollCallId'," + + "tokens='$tokens', timestamp='$timestamp'}" + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index 9b3571169b..3192a960a6 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -16,6 +16,7 @@ class LinkedOrganizationsRepository @Inject constructor() { var otherLaoId: String? = null var otherServerAddr: String? = null var otherPublicKey: String? = null + var linkedLaos: MutableMap> = mutableMapOf() /** * Updates the challenge @@ -35,6 +36,10 @@ class LinkedOrganizationsRepository @Inject constructor() { return challenge } + fun addLinkedLao(lao_id: String, tokens: Array) { + linkedLaos[lao_id] = tokens + } + fun flush() { otherLaoId = null otherServerAddr = null diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt index b772c043c2..47a186a08f 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt @@ -59,6 +59,17 @@ class LinkedOrganizationsFragment : Fragment() { binding.inviteOtherOrganization.setOnClickListener(invitationPage) binding.joinOtherOrganizationInvitation.setOnClickListener(joinButton) + // Displaying the linked organizations + val laos = linkedOrganizationsViewModel.getLinkedLaosMap().keys + if (laos.isNotEmpty()) { + val laosText = laos.joinToString(separator = "\n - ") + val textToDisplay = context?.getString(R.string.list_organizations, laosText) + binding.noOrganizationsText.visibility = View.GONE + binding.listOrganizationsText.text = textToDisplay + } else { + binding.listOrganizationsText.visibility = View.GONE + } + handleBackNav() return binding.root diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt index 7e52183404..4b98767c2c 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt @@ -141,6 +141,10 @@ constructor( return linkedOrgRepo.getChallenge() } + fun getLinkedLaosMap(): Map> { + return linkedOrgRepo.linkedLaos + } + fun isRepositoryValid(): Boolean { return linkedOrgRepo.otherLaoId != null && linkedOrgRepo.otherServerAddr != null && diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index c329833a7a..b800fa859c 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -1,10 +1,14 @@ package com.github.dedis.popstellar.utility.handler.data import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange +import com.github.dedis.popstellar.model.objects.Channel import com.github.dedis.popstellar.repository.LAORepository import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository import com.github.dedis.popstellar.utility.error.UnknownLaoException import javax.inject.Inject +import timber.log.Timber /** Federation messages handler class */ class LinkedOrganizationsHandler @@ -24,4 +28,43 @@ constructor( fun handleChallenge(context: HandlerContext, challenge: Challenge) { linkedOrgRepo.updateChallenge(challenge) } + + /** + * Process a Federation Result message + * + * @param context the HandlerContext of the message + * @param result the message that was received + */ + @Throws(UnknownLaoException::class) + fun handleResult(context: HandlerContext, result: FederationResult) { + if (result.isSuccess()) { + // DO STUFF + } else { + Timber.tag(TAG).d("FederationResult failure : %s", result.reason) + } + } + + /** + * Process a Token Exchange message + * + * @param context the HandlerContext of the message + * @param tokenExchange the message that was received + */ + @Throws(UnknownLaoException::class) + fun handleTokensExchange(context: HandlerContext, tokenExchange: TokensExchange) { + linkedOrgRepo.addLinkedLao(tokenExchange.laoId, tokenExchange.tokens) + tokenExchange.tokens.forEach { t -> + laoRepo.addDisposable( + context.messageSender + .subscribe( + Channel.getLaoChannel(tokenExchange.laoId).subChannel("social").subChannel(t)) + .subscribe( + { Timber.tag(TAG).d("subscription a success") }, + { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) + } + } + + companion object { + private val TAG = LinkedOrganizationsHandler::class.java.simpleName + } } diff --git a/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml b/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml index 295fbdfad5..d59188556a 100644 --- a/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml +++ b/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml @@ -102,7 +102,8 @@ android:id="@+id/empty_events_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_margin="@dimen/main_horizontal_margin"> + android:layout_margin="@dimen/main_horizontal_margin" + android:orientation="horizontal"> + + diff --git a/fe2-android/app/src/main/res/values/strings.xml b/fe2-android/app/src/main/res/values/strings.xml index 047b5fc3bd..b3196b60ae 100644 --- a/fe2-android/app/src/main/res/values/strings.xml +++ b/fe2-android/app/src/main/res/values/strings.xml @@ -334,6 +334,7 @@ Loading… Federation expect sent successfully Federation init sent successfully + List of linked organizations :\n - %s No stored data found From e80f557f76f8097b5cbabd332796dfb39e677582 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 23 Jun 2024 19:53:54 +0200 Subject: [PATCH 02/18] Added Result and TokensExchange messages, adapted handler --- .../data/federation/FederationResult.kt | 4 +- .../message/data/federation/TokensExchange.kt | 2 +- .../data/federation/FederationResultTest.kt | 134 ++++++++++++++++++ .../data/federation/TokensExchangeTest.kt | 77 ++++++++++ 4 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt create mode 100644 fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt index feb821395b..ddaa2d231e 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -62,9 +62,9 @@ class FederationResult override fun toString(): String { if (status == "failure") { - return "FederationResult{status='$status', reason='$reason'," + "challenge='$challenge'}" + return "FederationResult{status='$status', reason='$reason', challenge='$challenge'}" } else if (status == "success") { - return "FederationResult{status='$status', public_key='$publicKey'," + + return "FederationResult{status='$status', public_key='$publicKey', " + "challenge='$challenge'}" } return "FederationResult{ERROR}" diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt index 5ae6bc4bc0..ab96e00426 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt @@ -46,7 +46,7 @@ class TokensExchange } override fun toString(): String { - return "TokensExchange{lao_id='$laoId', roll_call_id='$rollCallId'," + + return "TokensExchange{lao_id='$laoId', roll_call_id='$rollCallId', " + "tokens='$tokens', timestamp='$timestamp'}" } } diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt new file mode 100644 index 0000000000..d0f0ea86b6 --- /dev/null +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt @@ -0,0 +1,134 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.testutils.Base64DataUtils +import org.junit.Assert +import org.junit.Assert.assertThrows +import org.junit.Test +import java.time.Instant + +class FederationResultTest { + + @Test + fun resultStatusTest() { + Assert.assertEquals(SUCCESS, RESULT_SUCCESS.status) + Assert.assertEquals(FAILURE, RESULT_FAILURE.status) + } + + @Test + fun resultReasonTest() { + Assert.assertNull(RESULT_SUCCESS.reason) + Assert.assertEquals(REASON, RESULT_FAILURE.reason) + } + + @Test + fun resultPublicKeyTest() { + Assert.assertEquals(PK.encoded, RESULT_SUCCESS.publicKey) + Assert.assertNull(RESULT_FAILURE.publicKey) + } + + @Test + fun resultChallengeTest() { + Assert.assertEquals(CHALLENGE, RESULT_SUCCESS.challenge) + Assert.assertEquals(CHALLENGE, RESULT_FAILURE.challenge) + } + + @Test + fun resultObjectTest() { + Assert.assertEquals(Objects.FEDERATION.`object`, RESULT_SUCCESS.`object`) + Assert.assertEquals(Objects.FEDERATION.`object`, RESULT_FAILURE.`object`) + } + + @Test + fun resultActionTest() { + Assert.assertEquals(Action.RESULT.action, RESULT_SUCCESS.action) + Assert.assertEquals(Action.RESULT.action, RESULT_FAILURE.action) + } + + @Test + fun resultEqualsTest() { + val result2 = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = CHALLENGE) + val result3 = FederationResult(FAILURE, reason = REASON, challenge = CHALLENGE) + val result4 = FederationResult(FAILURE, reason = "reason2", challenge = CHALLENGE) + Assert.assertEquals(RESULT_SUCCESS, result2) + Assert.assertEquals(RESULT_SUCCESS, RESULT_SUCCESS) + Assert.assertEquals(RESULT_SUCCESS.hashCode().toLong(), result2.hashCode().toLong()) + Assert.assertEquals(RESULT_FAILURE, result3) + Assert.assertEquals(RESULT_FAILURE, RESULT_FAILURE) + Assert.assertEquals(RESULT_FAILURE.hashCode().toLong(), result3.hashCode().toLong()) + + Assert.assertNotEquals(RESULT_SUCCESS, RESULT_FAILURE) + Assert.assertNotEquals(RESULT_SUCCESS, result3) + Assert.assertNotEquals(RESULT_SUCCESS, result4) + Assert.assertNotEquals(RESULT_FAILURE, result2) + Assert.assertNotEquals(RESULT_FAILURE, result4) + Assert.assertNotEquals(RESULT_SUCCESS, null) + Assert.assertNotEquals(RESULT_FAILURE, null) + } + + @Test + fun resultToStringTest() { + Assert.assertEquals( + "FederationResult{status='$SUCCESS', public_key='${PK.encoded}', challenge='$CHALLENGE'}", + RESULT_SUCCESS.toString() + ) + Assert.assertEquals( + "FederationResult{status='$FAILURE', reason='$REASON', challenge='$CHALLENGE'}", + RESULT_FAILURE.toString() + ) + } + + @Test + fun invalidMessageTypeTest() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult("invalid", challenge = CHALLENGE) + } + assert(exception.message == "Status must be either 'failure' or 'success'.") + } + + @Test + fun invalidSuccessTest1() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(SUCCESS, challenge = CHALLENGE) + } + assert(exception.message == "Public key must be provided for success status.") + } + + @Test + fun invalidSuccessTest2() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(SUCCESS, publicKey = PK.encoded, reason = "reason", challenge = CHALLENGE) + } + assert(exception.message == "Reason must be null for success status.") + } + + @Test + fun invalidFailureTest1() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(FAILURE, challenge = CHALLENGE) + } + assert(exception.message == "Reason must be provided for failure status.") + } + + @Test + fun invalidFailureTest2() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(FAILURE, publicKey = PK.encoded, reason = "reason", challenge = CHALLENGE) + } + assert(exception.message == "Public key must be null for failure status.") + } + + companion object { + private val KEY_PAIR = Base64DataUtils.generateKeyPair() + private val PK = KEY_PAIR.publicKey + private val TIMESTAMP = Instant.now().epochSecond + private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" + private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) + private val SUCCESS = "success" + private val FAILURE = "failure" + private val REASON = "reason" + private val RESULT_SUCCESS = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = CHALLENGE) + private val RESULT_FAILURE = FederationResult(FAILURE, reason = REASON, challenge = CHALLENGE) + } +} diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt new file mode 100644 index 0000000000..bf796e2f6e --- /dev/null +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt @@ -0,0 +1,77 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.model.objects.Lao +import com.github.dedis.popstellar.testutils.Base64DataUtils +import org.junit.Assert +import org.junit.Test +import java.time.Instant + +class TokensExchangeTest { + + @Test + fun laoIDTest() { + Assert.assertEquals(LAO_ID, TK_EXCHANGE.laoId) + } + + @Test + fun rollCallIDTest() { + Assert.assertEquals(ROLL_CALL_ID, TK_EXCHANGE.rollCallId) + } + + @Test + fun tokensArrayTest() { + Assert.assertEquals(TOKENS, TK_EXCHANGE.tokens) + } + + @Test + fun timestampTest() { + Assert.assertEquals(TIMESTAMP, TK_EXCHANGE.timestamp) + } + + @Test + fun tokensExchangeObjectTest() { + Assert.assertEquals(Objects.FEDERATION.`object`, TK_EXCHANGE.`object`) + } + + @Test + fun tokensExchangeActionTest() { + Assert.assertEquals(Action.TOKENS_EXCHANGE.action, TK_EXCHANGE.action) + } + + @Test + fun equalsTest() { + val tokensExchange2 = TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP) + Assert.assertEquals(TK_EXCHANGE, tokensExchange2) + Assert.assertEquals(TK_EXCHANGE, TK_EXCHANGE) + Assert.assertEquals(TK_EXCHANGE.hashCode().toLong(), tokensExchange2.hashCode().toLong()) + + val tokensExchange3 = TokensExchange(Lao.generateLaoId(ORGANIZER, CREATION, "LAO2"), ROLL_CALL_ID, TOKENS, TIMESTAMP) + val tokensExchange4 = TokensExchange(LAO_ID, "RC2", TOKENS, TIMESTAMP) + val tokensExchange5 = TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("token1"), TIMESTAMP) + Assert.assertNotEquals(TK_EXCHANGE, tokensExchange3) + Assert.assertNotEquals(TK_EXCHANGE, tokensExchange4) + Assert.assertNotEquals(TK_EXCHANGE, tokensExchange5) + Assert.assertNotEquals(TK_EXCHANGE, null) + } + + @Test + fun toStringTest() { + Assert.assertEquals( + "TokensExchange{lao_id='$LAO_ID', roll_call_id='$ROLL_CALL_ID', tokens='$TOKENS', timestamp='$TIMESTAMP'}", + TK_EXCHANGE.toString() + ) + } + + companion object { + private val ORGANIZER = Base64DataUtils.generatePublicKey() + private val CREATION = Instant.now().epochSecond + private const val NAME = "Lao name" + private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) + private val ROLL_CALL_ID = "RC1" + private val TOKENS = arrayOf("token1", "token2", "token3") + private val TIMESTAMP = Instant.now().epochSecond + private val TK_EXCHANGE = TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP) + } +} From 1677c007177efdeef7604d98551c57b60fa86f5e Mon Sep 17 00:00:00 2001 From: johan Date: Mon, 24 Jun 2024 14:58:34 +0200 Subject: [PATCH 03/18] FederationResult now works as expected --- .../data/federation/FederationResult.kt | 3 +- .../LinkedOrganizationsRepository.kt | 17 ++++++---- .../federation/LinkedOrganizationsFragment.kt | 33 +++++++++++++------ .../LinkedOrganizationsInviteFragment.kt | 6 ++++ .../LinkedOrganizationsViewModel.kt | 14 +++++--- .../data/LinkedOrganizationsHandler.kt | 22 ++++++++++++- .../app/src/main/res/values/strings.xml | 2 +- .../data/federation/FederationResultTest.kt | 31 +++++++++-------- 8 files changed, 91 insertions(+), 37 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt index ddaa2d231e..7d72a79d88 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -1,5 +1,6 @@ package com.github.dedis.popstellar.model.network.method.message.data.federation +import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.Action import com.github.dedis.popstellar.model.network.method.message.data.Data import com.github.dedis.popstellar.model.network.method.message.data.Objects @@ -19,7 +20,7 @@ class FederationResult val status: String, val reason: String? = null, @SerializedName("public_key") val publicKey: String? = null, - val challenge: Challenge, + val challenge: MessageGeneral, ) : Data { init { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index 3192a960a6..94e1c60111 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -13,16 +13,12 @@ import javax.inject.Singleton class LinkedOrganizationsRepository @Inject constructor() { private var challenge: Challenge? = null private var onChallengeUpdatedCallback: ((Challenge) -> Unit)? = null + private var linkedLaos: MutableMap> = mutableMapOf() + private var onLinkedLaosUpdatedCallback: ((MutableMap>) -> Unit)? = null var otherLaoId: String? = null var otherServerAddr: String? = null var otherPublicKey: String? = null - var linkedLaos: MutableMap> = mutableMapOf() - /** - * Updates the challenge - * - * @param challenge the new Challenge - */ fun updateChallenge(challenge: Challenge) { this.challenge = challenge onChallengeUpdatedCallback?.invoke(challenge) @@ -38,6 +34,15 @@ class LinkedOrganizationsRepository @Inject constructor() { fun addLinkedLao(lao_id: String, tokens: Array) { linkedLaos[lao_id] = tokens + onLinkedLaosUpdatedCallback?.invoke(linkedLaos) + } + + fun setOnLinkedLaosUpdatedCallback(callback: (MutableMap>) -> Unit) { + onLinkedLaosUpdatedCallback = callback + } + + fun getLinkedLaos(): MutableMap> { + return linkedLaos } fun flush() { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt index 47a186a08f..5c50165be8 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt @@ -17,6 +17,9 @@ import com.github.dedis.popstellar.ui.lao.event.LaoDetailAnimation.showIn import com.github.dedis.popstellar.ui.lao.event.LaoDetailAnimation.showOut import com.github.dedis.popstellar.ui.qrcode.QrScannerFragment import com.github.dedis.popstellar.ui.qrcode.ScanningAction +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** * A simple [Fragment] subclass. Use the [LinkedOrganizationsFragment.newInstance] factory method to @@ -41,9 +44,6 @@ class LinkedOrganizationsFragment : Fragment() { linkedOrganizationsViewModel = obtainLinkedOrganizationsViewModel(requireActivity(), laoViewModel.laoId) - // Starts from a clean repository - linkedOrganizationsViewModel.flushRepository() - // Sets the text and the button depending on the user's role laoViewModel.role.observe(viewLifecycleOwner) { role: Role -> if (role == Role.ORGANIZER) { @@ -61,13 +61,12 @@ class LinkedOrganizationsFragment : Fragment() { // Displaying the linked organizations val laos = linkedOrganizationsViewModel.getLinkedLaosMap().keys - if (laos.isNotEmpty()) { - val laosText = laos.joinToString(separator = "\n - ") - val textToDisplay = context?.getString(R.string.list_organizations, laosText) - binding.noOrganizationsText.visibility = View.GONE - binding.listOrganizationsText.text = textToDisplay - } else { - binding.listOrganizationsText.visibility = View.GONE + displayLinkedOrganizations(laos) + linkedOrganizationsViewModel.doWhenLinkedLaosIsUpdated { laoMap -> + CoroutineScope(Dispatchers.Main).launch { + val currentLaos = laoMap.keys + displayLinkedOrganizations(currentLaos) + } } handleBackNav() @@ -108,6 +107,7 @@ class LinkedOrganizationsFragment : Fragment() { private var joinButton = View.OnClickListener { + linkedOrganizationsViewModel.flushRepository() laoViewModel.setIsTab(false) linkedOrganizationsViewModel.manager = parentFragmentManager LaoActivity.setCurrentFragment(parentFragmentManager, R.id.fragment_qr_scanner) { @@ -115,6 +115,19 @@ class LinkedOrganizationsFragment : Fragment() { } } + private fun displayLinkedOrganizations(laos: Set) { + if (laos.isNotEmpty()) { + val laosText = laos.joinToString(separator = "\n\n - ") + val textToDisplay = context?.getString(R.string.list_organizations, laosText) + binding.noOrganizationsText.visibility = View.GONE + binding.listOrganizationsText.visibility = View.VISIBLE + binding.listOrganizationsText.text = textToDisplay + } else { + binding.listOrganizationsText.visibility = View.GONE + binding.noOrganizationsText.visibility = View.VISIBLE + } + } + private fun handleBackNav() { LaoActivity.addBackNavigationCallbackToEvents(requireActivity(), viewLifecycleOwner, TAG) } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt index f44e9664c6..7a9fae574a 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt @@ -106,6 +106,7 @@ class LinkedOrganizationsInviteFragment(createsInvitation: Boolean) : Fragment() } private fun createInvitationSetup(binding: LinkedOrganizationsInviteFragmentBinding) { + // Setup buttons binding.nextStepButton.setText(R.string.next_step) binding.nextStepButton.visibility = View.GONE binding.nextStepButton.setOnClickListener { openScanner() } @@ -113,6 +114,11 @@ class LinkedOrganizationsInviteFragment(createsInvitation: Boolean) : Fragment() binding.loadingText.visibility = View.VISIBLE binding.linkedOrganizationsNameTitle.visibility = View.GONE binding.linkedOrganizationsServerTitle.visibility = View.GONE + + // Starts with a clean repository + linkedOrganizationsViewModel.flushRepository() + + // Sends the challenge request laoViewModel.addDisposable( linkedOrganizationsViewModel .sendChallengeRequest(Instant.now().epochSecond) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt index 4b98767c2c..d749258fd1 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt @@ -142,7 +142,7 @@ constructor( } fun getLinkedLaosMap(): Map> { - return linkedOrgRepo.linkedLaos + return linkedOrgRepo.getLinkedLaos() } fun isRepositoryValid(): Boolean { @@ -163,6 +163,10 @@ constructor( linkedOrgRepo.setOnChallengeUpdatedCallback(function) } + fun doWhenLinkedLaosIsUpdated(function: (MutableMap>) -> Unit) { + linkedOrgRepo.setOnLinkedLaosUpdatedCallback(function) + } + override fun handleData(data: String?) { // Don't process another data from the scanner if I'm already trying to scan if (connecting.get()) { @@ -183,6 +187,11 @@ constructor( return } + // Saving the other organization details to the repository + linkedOrgRepo.otherLaoId = federationDetails.laoId + linkedOrgRepo.otherServerAddr = federationDetails.serverAddress + linkedOrgRepo.otherPublicKey = federationDetails.publicKey + if (federationDetails.challenge == null) { // The federationDetails object is sent by the invitation creator disposables.add( @@ -200,9 +209,6 @@ constructor( )) } else { // The federationDetails object is sent by the one who joins the invitation - linkedOrgRepo.otherLaoId = federationDetails.laoId - linkedOrgRepo.otherServerAddr = federationDetails.serverAddress - linkedOrgRepo.otherPublicKey = federationDetails.publicKey linkedOrgRepo.updateChallenge(federationDetails.challenge) } connecting.set(false) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index b800fa859c..a0d59b4632 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -1,5 +1,6 @@ package com.github.dedis.popstellar.utility.handler.data +import com.github.dedis.popstellar.R import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange @@ -38,7 +39,21 @@ constructor( @Throws(UnknownLaoException::class) fun handleResult(context: HandlerContext, result: FederationResult) { if (result.isSuccess()) { - // DO STUFF + if (result.challenge.data == linkedOrgRepo.getChallenge() + && result.publicKey == linkedOrgRepo.otherPublicKey + && linkedOrgRepo.otherLaoId != null) + { + linkedOrgRepo.addLinkedLao(linkedOrgRepo.otherLaoId!!, arrayOf()) + laoRepo.addDisposable( + context.messageSender + .subscribe( + Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) + .subscribe( + { Timber.tag(TAG).d("subscription a success") }, + { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) + } else { + Timber.tag(TAG).d("Invalid FederationResult success") + } } else { Timber.tag(TAG).d("FederationResult failure : %s", result.reason) } @@ -52,7 +67,12 @@ constructor( */ @Throws(UnknownLaoException::class) fun handleTokensExchange(context: HandlerContext, tokenExchange: TokensExchange) { + // Adds the tokens in the repository linkedOrgRepo.addLinkedLao(tokenExchange.laoId, tokenExchange.tokens) + + // Subscribes to social of the linked organization automatically + // Note that for now the participants of an LAO automatically subscribe to social of the other LAO + // This might be changed in the future (making a pop-up asking the user if he/she wants to subscribe to that) tokenExchange.tokens.forEach { t -> laoRepo.addDisposable( context.messageSender diff --git a/fe2-android/app/src/main/res/values/strings.xml b/fe2-android/app/src/main/res/values/strings.xml index b3196b60ae..84b5a87a34 100644 --- a/fe2-android/app/src/main/res/values/strings.xml +++ b/fe2-android/app/src/main/res/values/strings.xml @@ -334,7 +334,7 @@ Loading… Federation expect sent successfully Federation init sent successfully - List of linked organizations :\n - %s + List of linked organizations :\n\n - %s No stored data found diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt index d0f0ea86b6..bb728b12b0 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt @@ -1,8 +1,10 @@ package com.github.dedis.popstellar.model.network.method.message.data.federation +import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.Action import com.github.dedis.popstellar.model.network.method.message.data.Objects import com.github.dedis.popstellar.testutils.Base64DataUtils +import com.google.gson.Gson import org.junit.Assert import org.junit.Assert.assertThrows import org.junit.Test @@ -30,8 +32,8 @@ class FederationResultTest { @Test fun resultChallengeTest() { - Assert.assertEquals(CHALLENGE, RESULT_SUCCESS.challenge) - Assert.assertEquals(CHALLENGE, RESULT_FAILURE.challenge) + Assert.assertEquals(MG_CHALLENGE, RESULT_SUCCESS.challenge) + Assert.assertEquals(MG_CHALLENGE, RESULT_FAILURE.challenge) } @Test @@ -48,9 +50,9 @@ class FederationResultTest { @Test fun resultEqualsTest() { - val result2 = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = CHALLENGE) - val result3 = FederationResult(FAILURE, reason = REASON, challenge = CHALLENGE) - val result4 = FederationResult(FAILURE, reason = "reason2", challenge = CHALLENGE) + val result2 = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = MG_CHALLENGE) + val result3 = FederationResult(FAILURE, reason = REASON, challenge = MG_CHALLENGE) + val result4 = FederationResult(FAILURE, reason = "reason2", challenge = MG_CHALLENGE) Assert.assertEquals(RESULT_SUCCESS, result2) Assert.assertEquals(RESULT_SUCCESS, RESULT_SUCCESS) Assert.assertEquals(RESULT_SUCCESS.hashCode().toLong(), result2.hashCode().toLong()) @@ -70,11 +72,11 @@ class FederationResultTest { @Test fun resultToStringTest() { Assert.assertEquals( - "FederationResult{status='$SUCCESS', public_key='${PK.encoded}', challenge='$CHALLENGE'}", + "FederationResult{status='$SUCCESS', public_key='${PK.encoded}', challenge='$MG_CHALLENGE'}", RESULT_SUCCESS.toString() ) Assert.assertEquals( - "FederationResult{status='$FAILURE', reason='$REASON', challenge='$CHALLENGE'}", + "FederationResult{status='$FAILURE', reason='$REASON', challenge='$MG_CHALLENGE'}", RESULT_FAILURE.toString() ) } @@ -82,7 +84,7 @@ class FederationResultTest { @Test fun invalidMessageTypeTest() { val exception = assertThrows(IllegalArgumentException::class.java) { - FederationResult("invalid", challenge = CHALLENGE) + FederationResult("invalid", challenge = MG_CHALLENGE) } assert(exception.message == "Status must be either 'failure' or 'success'.") } @@ -90,7 +92,7 @@ class FederationResultTest { @Test fun invalidSuccessTest1() { val exception = assertThrows(IllegalArgumentException::class.java) { - FederationResult(SUCCESS, challenge = CHALLENGE) + FederationResult(SUCCESS, challenge = MG_CHALLENGE) } assert(exception.message == "Public key must be provided for success status.") } @@ -98,7 +100,7 @@ class FederationResultTest { @Test fun invalidSuccessTest2() { val exception = assertThrows(IllegalArgumentException::class.java) { - FederationResult(SUCCESS, publicKey = PK.encoded, reason = "reason", challenge = CHALLENGE) + FederationResult(SUCCESS, publicKey = PK.encoded, reason = "reason", challenge = MG_CHALLENGE) } assert(exception.message == "Reason must be null for success status.") } @@ -106,7 +108,7 @@ class FederationResultTest { @Test fun invalidFailureTest1() { val exception = assertThrows(IllegalArgumentException::class.java) { - FederationResult(FAILURE, challenge = CHALLENGE) + FederationResult(FAILURE, challenge = MG_CHALLENGE) } assert(exception.message == "Reason must be provided for failure status.") } @@ -114,7 +116,7 @@ class FederationResultTest { @Test fun invalidFailureTest2() { val exception = assertThrows(IllegalArgumentException::class.java) { - FederationResult(FAILURE, publicKey = PK.encoded, reason = "reason", challenge = CHALLENGE) + FederationResult(FAILURE, publicKey = PK.encoded, reason = "reason", challenge = MG_CHALLENGE) } assert(exception.message == "Public key must be null for failure status.") } @@ -125,10 +127,11 @@ class FederationResultTest { private val TIMESTAMP = Instant.now().epochSecond private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) + private val MG_CHALLENGE = MessageGeneral(Base64DataUtils.generateKeyPair(), CHALLENGE, Gson()) private val SUCCESS = "success" private val FAILURE = "failure" private val REASON = "reason" - private val RESULT_SUCCESS = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = CHALLENGE) - private val RESULT_FAILURE = FederationResult(FAILURE, reason = REASON, challenge = CHALLENGE) + private val RESULT_SUCCESS = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = MG_CHALLENGE) + private val RESULT_FAILURE = FederationResult(FAILURE, reason = REASON, challenge = MG_CHALLENGE) } } From bbc7b5ef76976c17b99bcd3afa8d4e960fb60849 Mon Sep 17 00:00:00 2001 From: johan Date: Mon, 24 Jun 2024 17:46:06 +0200 Subject: [PATCH 04/18] Added the function to send TokensExchange data --- .../LinkedOrganizationsRepository.kt | 11 +++++ .../federation/LinkedOrganizationsFragment.kt | 3 +- .../LinkedOrganizationsViewModel.kt | 41 +++++++++++++++++++ .../data/LinkedOrganizationsHandler.kt | 40 +++++++++++------- .../app/src/main/res/values/strings.xml | 4 +- .../popstellar/di/DataRegistryModuleHelper.kt | 2 +- .../LinkedOrganizationsRepositoryTest.kt | 3 +- 7 files changed, 86 insertions(+), 18 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index 94e1c60111..b8ce2fe90d 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -15,6 +15,7 @@ class LinkedOrganizationsRepository @Inject constructor() { private var onChallengeUpdatedCallback: ((Challenge) -> Unit)? = null private var linkedLaos: MutableMap> = mutableMapOf() private var onLinkedLaosUpdatedCallback: ((MutableMap>) -> Unit)? = null + private var newTokensNotifyFunction: ((String, String, Array) -> Unit)? = null var otherLaoId: String? = null var otherServerAddr: String? = null var otherPublicKey: String? = null @@ -37,10 +38,20 @@ class LinkedOrganizationsRepository @Inject constructor() { onLinkedLaosUpdatedCallback?.invoke(linkedLaos) } + fun updateAndNotifyLinkedLao(lao_id: String, tokens: Array, rollCallId: String) { + linkedLaos[lao_id] = tokens + newTokensNotifyFunction?.invoke(lao_id, rollCallId, tokens) + onLinkedLaosUpdatedCallback?.invoke(linkedLaos) + } + fun setOnLinkedLaosUpdatedCallback(callback: (MutableMap>) -> Unit) { onLinkedLaosUpdatedCallback = callback } + fun setNewTokensNotifyFunction(function: (String, String, Array) -> Unit) { + newTokensNotifyFunction = function + } + fun getLinkedLaos(): MutableMap> { return linkedLaos } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt index 5c50165be8..4cb7be5a3c 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt @@ -69,6 +69,7 @@ class LinkedOrganizationsFragment : Fragment() { } } + linkedOrganizationsViewModel.setLinkedLaosNotifyFunction() handleBackNav() return binding.root @@ -117,7 +118,7 @@ class LinkedOrganizationsFragment : Fragment() { private fun displayLinkedOrganizations(laos: Set) { if (laos.isNotEmpty()) { - val laosText = laos.joinToString(separator = "\n\n - ") + val laosText = laos.joinToString(separator = "\n\n") val textToDisplay = context?.getString(R.string.list_organizations, laosText) binding.noOrganizationsText.visibility = View.GONE binding.listOrganizationsText.visibility = View.VISIBLE diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt index d749258fd1..78b71003f6 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt @@ -10,6 +10,7 @@ import com.github.dedis.popstellar.model.network.method.message.data.federation. import com.github.dedis.popstellar.model.network.method.message.data.federation.ChallengeRequest import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationExpect import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationInit +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange import com.github.dedis.popstellar.model.objects.view.LaoView import com.github.dedis.popstellar.model.qrcode.FederationDetails import com.github.dedis.popstellar.repository.LAORepository @@ -23,6 +24,7 @@ import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import io.reactivex.Completable import io.reactivex.disposables.CompositeDisposable +import java.time.Instant import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject @@ -137,6 +139,31 @@ constructor( ) } + /** + * Sends a Token Exchange data + * + * @param remoteLaoId ID of the remote LAO + * @param rollCallId ID of the rollCall of the remote LAO + * @param attendees array with the token of each attendee + */ + fun sendTokensExchange( + remoteLaoId: String, + rollCallId: String, + attendees: Array + ): Completable { + val laoView: LaoView = + try { + laoRepo.getLaoView(laoId) + } catch (e: UnknownLaoException) { + ErrorUtils.logAndShow(getApplication(), TAG, e, R.string.unknown_lao_exception) + return Completable.error(UnknownLaoException()) + } + val timestamp = Instant.now().epochSecond + val tokensExchange = TokensExchange(remoteLaoId, rollCallId, attendees, timestamp) + return networkManager.messageSender.publish( + keyManager.mainKeyPair, laoView.channel.subChannel(FEDERATION), tokensExchange) + } + fun getChallenge(): Challenge? { return linkedOrgRepo.getChallenge() } @@ -167,6 +194,20 @@ constructor( linkedOrgRepo.setOnLinkedLaosUpdatedCallback(function) } + fun setLinkedLaosNotifyFunction() { + linkedOrgRepo.setNewTokensNotifyFunction { otherLaoId, rollCallId, tokens -> + disposables.add( + sendTokensExchange(otherLaoId, rollCallId, tokens) + .subscribe( + { ErrorUtils.logAndShow(getApplication(), TAG, R.string.tokens_exchange_sent) }, + { error: Throwable -> + ErrorUtils.logAndShow( + getApplication(), TAG, error, R.string.error_sending_tokens_exchange) + }, + )) + } + } + override fun handleData(data: String?) { // Don't process another data from the scanner if I'm already trying to scan if (connecting.get()) { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index a0d59b4632..fafcb424ca 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -1,13 +1,14 @@ package com.github.dedis.popstellar.utility.handler.data -import com.github.dedis.popstellar.R import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange import com.github.dedis.popstellar.model.objects.Channel import com.github.dedis.popstellar.repository.LAORepository import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository +import com.github.dedis.popstellar.repository.RollCallRepository import com.github.dedis.popstellar.utility.error.UnknownLaoException +import com.github.dedis.popstellar.utility.error.keys.NoRollCallException import javax.inject.Inject import timber.log.Timber @@ -16,7 +17,8 @@ class LinkedOrganizationsHandler @Inject constructor( private val laoRepo: LAORepository, - private val linkedOrgRepo: LinkedOrganizationsRepository + private val linkedOrgRepo: LinkedOrganizationsRepository, + private val rollCallRepo: RollCallRepository ) { /** @@ -39,18 +41,16 @@ constructor( @Throws(UnknownLaoException::class) fun handleResult(context: HandlerContext, result: FederationResult) { if (result.isSuccess()) { - if (result.challenge.data == linkedOrgRepo.getChallenge() - && result.publicKey == linkedOrgRepo.otherPublicKey - && linkedOrgRepo.otherLaoId != null) - { + if (result.challenge.data == linkedOrgRepo.getChallenge() && + result.publicKey == linkedOrgRepo.otherPublicKey && + linkedOrgRepo.otherLaoId != null) { linkedOrgRepo.addLinkedLao(linkedOrgRepo.otherLaoId!!, arrayOf()) laoRepo.addDisposable( - context.messageSender - .subscribe( - Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) - .subscribe( - { Timber.tag(TAG).d("subscription a success") }, - { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) + context.messageSender + .subscribe(Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) + .subscribe( + { putRemoteLaoTokensInRepository() }, + { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) } else { Timber.tag(TAG).d("Invalid FederationResult success") } @@ -71,8 +71,9 @@ constructor( linkedOrgRepo.addLinkedLao(tokenExchange.laoId, tokenExchange.tokens) // Subscribes to social of the linked organization automatically - // Note that for now the participants of an LAO automatically subscribe to social of the other LAO - // This might be changed in the future (making a pop-up asking the user if he/she wants to subscribe to that) + // Note that for now the participants of an LAO automatically subscribe to social of the other + // LAO. This might be changed in the future (making a pop-up asking the user if he/she wants + // to subscribe to that) tokenExchange.tokens.forEach { t -> laoRepo.addDisposable( context.messageSender @@ -84,6 +85,17 @@ constructor( } } + private fun putRemoteLaoTokensInRepository() { + try { + val rollCall = rollCallRepo.getLastClosedRollCall(linkedOrgRepo.otherLaoId!!) + val attendees = rollCall.attendees.map { e -> e.encoded }.toTypedArray() + linkedOrgRepo.updateAndNotifyLinkedLao( + linkedOrgRepo.otherLaoId!!, attendees, rollCall.persistentId) + } catch (e: NoRollCallException) { + Timber.tag(TAG).d("No RollCall was found on the linked LAO") + } + } + companion object { private val TAG = LinkedOrganizationsHandler::class.java.simpleName } diff --git a/fe2-android/app/src/main/res/values/strings.xml b/fe2-android/app/src/main/res/values/strings.xml index 84b5a87a34..ea5a2741a0 100644 --- a/fe2-android/app/src/main/res/values/strings.xml +++ b/fe2-android/app/src/main/res/values/strings.xml @@ -334,7 +334,8 @@ Loading… Federation expect sent successfully Federation init sent successfully - List of linked organizations :\n\n - %s + List of linked organizations :\n\n%s + Tokens exchange sent successfully No stored data found @@ -405,6 +406,7 @@ Could not send the federation expect message Could not send the federation init message Invalid federation information + Could not send the tokens exchange message An error response was replied by the server\nError %1$d - %2$s diff --git a/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt b/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt index 9266b70757..20d48d47b3 100644 --- a/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt +++ b/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt @@ -299,7 +299,7 @@ object DataRegistryModuleHelper { val reactionHandler = ReactionHandler(laoRepo, socialMediaRepo) val transactionCoinHandler = TransactionCoinHandler(digitalCashRepo) val witnessingHandler = WitnessingHandler(laoRepo, witnessingRepo) - val linkedOrganizationsHandler = LinkedOrganizationsHandler(laoRepo, linkedOrgRepo) + val linkedOrganizationsHandler = LinkedOrganizationsHandler(laoRepo, linkedOrgRepo, rollCallRepo) return DataRegistryModule.provideDataRegistry( laoHandler, rollCallHandler, diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt index ce2460710e..55b436b900 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt @@ -63,8 +63,9 @@ class LinkedOrganizationsRepositoryTest { fun handlerAndRepoTest() { val mockLaoRepo = Mockito.mock(LAORepository::class.java) val mockContext = Mockito.mock(HandlerContext::class.java) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) REPO.flush() - val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) handler.handleChallenge(mockContext, CHALLENGE) Assert.assertEquals(CHALLENGE, REPO.getChallenge()) } From 71e63ad92feb0aa85823617ff2509a69f14b83c0 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 06:40:36 +0200 Subject: [PATCH 05/18] Removed bad condition --- .../utility/handler/data/LinkedOrganizationsHandler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index fafcb424ca..e3fc6f8a9c 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -42,7 +42,6 @@ constructor( fun handleResult(context: HandlerContext, result: FederationResult) { if (result.isSuccess()) { if (result.challenge.data == linkedOrgRepo.getChallenge() && - result.publicKey == linkedOrgRepo.otherPublicKey && linkedOrgRepo.otherLaoId != null) { linkedOrgRepo.addLinkedLao(linkedOrgRepo.otherLaoId!!, arrayOf()) laoRepo.addDisposable( From 05c3de96337aa703a24332a896b855068e3cd8d1 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 07:36:16 +0200 Subject: [PATCH 06/18] Added federation support for multiple LAOs in the same app --- .../LinkedOrganizationsRepository.kt | 31 +++++++++++++------ .../LinkedOrganizationsViewModel.kt | 2 +- .../data/LinkedOrganizationsHandler.kt | 12 ++++--- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index b8ce2fe90d..c46773d47d 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -13,7 +13,7 @@ import javax.inject.Singleton class LinkedOrganizationsRepository @Inject constructor() { private var challenge: Challenge? = null private var onChallengeUpdatedCallback: ((Challenge) -> Unit)? = null - private var linkedLaos: MutableMap> = mutableMapOf() + private var linkedLaos: MutableMap>> = mutableMapOf() private var onLinkedLaosUpdatedCallback: ((MutableMap>) -> Unit)? = null private var newTokensNotifyFunction: ((String, String, Array) -> Unit)? = null var otherLaoId: String? = null @@ -33,15 +33,22 @@ class LinkedOrganizationsRepository @Inject constructor() { return challenge } - fun addLinkedLao(lao_id: String, tokens: Array) { - linkedLaos[lao_id] = tokens - onLinkedLaosUpdatedCallback?.invoke(linkedLaos) + fun addLinkedLao(laoId: String, otherLaoId: String, tokens: Array) { + if (!linkedLaos.containsKey(laoId)) { + linkedLaos[laoId] = mutableMapOf() + } + linkedLaos[laoId]!![otherLaoId] = tokens + onLinkedLaosUpdatedCallback?.invoke(linkedLaos[laoId]!!) } - fun updateAndNotifyLinkedLao(lao_id: String, tokens: Array, rollCallId: String) { - linkedLaos[lao_id] = tokens - newTokensNotifyFunction?.invoke(lao_id, rollCallId, tokens) - onLinkedLaosUpdatedCallback?.invoke(linkedLaos) + fun updateAndNotifyLinkedLao( + laoId: String, + otherLaoId: String, + tokens: Array, + rollCallId: String + ) { + addLinkedLao(laoId, otherLaoId, tokens) + newTokensNotifyFunction?.invoke(laoId, rollCallId, tokens) } fun setOnLinkedLaosUpdatedCallback(callback: (MutableMap>) -> Unit) { @@ -52,8 +59,12 @@ class LinkedOrganizationsRepository @Inject constructor() { newTokensNotifyFunction = function } - fun getLinkedLaos(): MutableMap> { - return linkedLaos + fun getLinkedLaos(laoId: String): MutableMap> { + return if (linkedLaos.containsKey(laoId)) { + linkedLaos[laoId]!! + } else { + mutableMapOf() + } } fun flush() { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt index 78b71003f6..70054ed050 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt @@ -169,7 +169,7 @@ constructor( } fun getLinkedLaosMap(): Map> { - return linkedOrgRepo.getLinkedLaos() + return linkedOrgRepo.getLinkedLaos(laoId) } fun isRepositoryValid(): Boolean { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index e3fc6f8a9c..863412df95 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -43,12 +43,13 @@ constructor( if (result.isSuccess()) { if (result.challenge.data == linkedOrgRepo.getChallenge() && linkedOrgRepo.otherLaoId != null) { - linkedOrgRepo.addLinkedLao(linkedOrgRepo.otherLaoId!!, arrayOf()) + val laoId = context.channel.extractLaoId() + linkedOrgRepo.addLinkedLao(laoId, linkedOrgRepo.otherLaoId!!, arrayOf()) laoRepo.addDisposable( context.messageSender .subscribe(Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) .subscribe( - { putRemoteLaoTokensInRepository() }, + { putRemoteLaoTokensInRepository(laoId) }, { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) } else { Timber.tag(TAG).d("Invalid FederationResult success") @@ -67,7 +68,8 @@ constructor( @Throws(UnknownLaoException::class) fun handleTokensExchange(context: HandlerContext, tokenExchange: TokensExchange) { // Adds the tokens in the repository - linkedOrgRepo.addLinkedLao(tokenExchange.laoId, tokenExchange.tokens) + linkedOrgRepo.addLinkedLao( + context.channel.extractLaoId(), tokenExchange.laoId, tokenExchange.tokens) // Subscribes to social of the linked organization automatically // Note that for now the participants of an LAO automatically subscribe to social of the other @@ -84,12 +86,12 @@ constructor( } } - private fun putRemoteLaoTokensInRepository() { + private fun putRemoteLaoTokensInRepository(myLaoId: String) { try { val rollCall = rollCallRepo.getLastClosedRollCall(linkedOrgRepo.otherLaoId!!) val attendees = rollCall.attendees.map { e -> e.encoded }.toTypedArray() linkedOrgRepo.updateAndNotifyLinkedLao( - linkedOrgRepo.otherLaoId!!, attendees, rollCall.persistentId) + myLaoId, linkedOrgRepo.otherLaoId!!, attendees, rollCall.persistentId) } catch (e: NoRollCallException) { Timber.tag(TAG).d("No RollCall was found on the linked LAO") } From 62f22bb55df1f687564182eed3016249158a28b0 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 08:16:09 +0200 Subject: [PATCH 07/18] Fix for a few bugs --- .../LinkedOrganizationsRepository.kt | 27 +++++++++---------- .../federation/LinkedOrganizationsFragment.kt | 10 ++++--- .../LinkedOrganizationsViewModel.kt | 24 +++++++++-------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index c46773d47d..1042cf8331 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -14,8 +14,9 @@ class LinkedOrganizationsRepository @Inject constructor() { private var challenge: Challenge? = null private var onChallengeUpdatedCallback: ((Challenge) -> Unit)? = null private var linkedLaos: MutableMap>> = mutableMapOf() - private var onLinkedLaosUpdatedCallback: ((MutableMap>) -> Unit)? = null - private var newTokensNotifyFunction: ((String, String, Array) -> Unit)? = null + private var onLinkedLaosUpdatedCallback: ((String, MutableMap>) -> Unit)? = + null + private var newTokensNotifyFunction: ((String, String, String, Array) -> Unit)? = null var otherLaoId: String? = null var otherServerAddr: String? = null var otherPublicKey: String? = null @@ -34,11 +35,9 @@ class LinkedOrganizationsRepository @Inject constructor() { } fun addLinkedLao(laoId: String, otherLaoId: String, tokens: Array) { - if (!linkedLaos.containsKey(laoId)) { - linkedLaos[laoId] = mutableMapOf() - } - linkedLaos[laoId]!![otherLaoId] = tokens - onLinkedLaosUpdatedCallback?.invoke(linkedLaos[laoId]!!) + val laoMap = linkedLaos.getOrPut(laoId) { mutableMapOf() } + laoMap[otherLaoId] = tokens + onLinkedLaosUpdatedCallback?.invoke(laoId, laoMap) } fun updateAndNotifyLinkedLao( @@ -48,23 +47,21 @@ class LinkedOrganizationsRepository @Inject constructor() { rollCallId: String ) { addLinkedLao(laoId, otherLaoId, tokens) - newTokensNotifyFunction?.invoke(laoId, rollCallId, tokens) + newTokensNotifyFunction?.invoke(laoId, otherLaoId, rollCallId, tokens) } - fun setOnLinkedLaosUpdatedCallback(callback: (MutableMap>) -> Unit) { + fun setOnLinkedLaosUpdatedCallback( + callback: (String, MutableMap>) -> Unit + ) { onLinkedLaosUpdatedCallback = callback } - fun setNewTokensNotifyFunction(function: (String, String, Array) -> Unit) { + fun setNewTokensNotifyFunction(function: (String, String, String, Array) -> Unit) { newTokensNotifyFunction = function } fun getLinkedLaos(laoId: String): MutableMap> { - return if (linkedLaos.containsKey(laoId)) { - linkedLaos[laoId]!! - } else { - mutableMapOf() - } + return linkedLaos.getOrDefault(laoId, mutableMapOf()) } fun flush() { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt index 4cb7be5a3c..9010d88f9b 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt @@ -62,10 +62,12 @@ class LinkedOrganizationsFragment : Fragment() { // Displaying the linked organizations val laos = linkedOrganizationsViewModel.getLinkedLaosMap().keys displayLinkedOrganizations(laos) - linkedOrganizationsViewModel.doWhenLinkedLaosIsUpdated { laoMap -> - CoroutineScope(Dispatchers.Main).launch { - val currentLaos = laoMap.keys - displayLinkedOrganizations(currentLaos) + linkedOrganizationsViewModel.doWhenLinkedLaosIsUpdated { laoId, laoMap -> + if (laoId == laoViewModel.laoId) { + CoroutineScope(Dispatchers.Main).launch { + val currentLaos = laoMap.keys + displayLinkedOrganizations(currentLaos) + } } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt index 70054ed050..213854bac8 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt @@ -190,21 +190,23 @@ constructor( linkedOrgRepo.setOnChallengeUpdatedCallback(function) } - fun doWhenLinkedLaosIsUpdated(function: (MutableMap>) -> Unit) { + fun doWhenLinkedLaosIsUpdated(function: (String, MutableMap>) -> Unit) { linkedOrgRepo.setOnLinkedLaosUpdatedCallback(function) } fun setLinkedLaosNotifyFunction() { - linkedOrgRepo.setNewTokensNotifyFunction { otherLaoId, rollCallId, tokens -> - disposables.add( - sendTokensExchange(otherLaoId, rollCallId, tokens) - .subscribe( - { ErrorUtils.logAndShow(getApplication(), TAG, R.string.tokens_exchange_sent) }, - { error: Throwable -> - ErrorUtils.logAndShow( - getApplication(), TAG, error, R.string.error_sending_tokens_exchange) - }, - )) + linkedOrgRepo.setNewTokensNotifyFunction { receivedLaoId, otherLaoId, rollCallId, tokens -> + if (receivedLaoId == laoId) { + disposables.add( + sendTokensExchange(otherLaoId, rollCallId, tokens) + .subscribe( + { ErrorUtils.logAndShow(getApplication(), TAG, R.string.tokens_exchange_sent) }, + { error: Throwable -> + ErrorUtils.logAndShow( + getApplication(), TAG, error, R.string.error_sending_tokens_exchange) + }, + )) + } } } From 05c5dcb230b70226de1424f1ba390ba5ab45cef9 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 12:38:28 +0200 Subject: [PATCH 08/18] Adapted chirps for federation, first test --- .../dedis/popstellar/model/objects/Chirp.kt | 12 ++- .../ui/lao/socialmedia/ChirpListAdapter.kt | 1 + .../lao/socialmedia/SocialMediaViewModel.kt | 99 +++++++++++++------ .../utility/handler/data/ChirpHandler.kt | 3 +- .../data/LinkedOrganizationsHandler.kt | 2 + 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt index 4d96c1c53b..979ececcae 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt @@ -13,6 +13,7 @@ class Chirp : Copyable { val timestamp: Long val isDeleted: Boolean val parentId: MessageID + val laoId: String constructor( id: MessageID, @@ -20,7 +21,8 @@ class Chirp : Copyable { text: String, timestamp: Long, isDeleted: Boolean, - parentId: MessageID + parentId: MessageID, + laoId: String ) { require(id.encoded.isNotEmpty()) { "The id of the Chirp is empty" } require(timestamp >= 0) { "The timestamp of the Chirp is negative" } @@ -32,6 +34,7 @@ class Chirp : Copyable { this.timestamp = timestamp this.parentId = parentId this.isDeleted = isDeleted + this.laoId = laoId } constructor( @@ -39,8 +42,9 @@ class Chirp : Copyable { sender: PublicKey, text: String, timestamp: Long, - parentId: MessageID - ) : this(id, sender, text, timestamp, false, parentId) + parentId: MessageID, + laoId: String + ) : this(id, sender, text, timestamp, false, parentId, laoId) constructor(chirp: Chirp, deleted: Boolean) { id = chirp.id @@ -49,6 +53,7 @@ class Chirp : Copyable { timestamp = chirp.timestamp parentId = chirp.parentId isDeleted = deleted + laoId = chirp.laoId } constructor(chirp: Chirp) { @@ -58,6 +63,7 @@ class Chirp : Copyable { timestamp = chirp.timestamp isDeleted = chirp.isDeleted parentId = chirp.parentId + laoId = chirp.laoId } override fun copy(): Chirp { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt index d42fff6d47..5b7255dca2 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt @@ -101,6 +101,7 @@ class ChirpListAdapter( val reactionDisposable = socialMediaViewModel .getReactions( + chirp.laoId, chirp.id) // Each time the observable changes the counter and the selection is // notified .subscribe( diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt index 784a4c0608..80108dbfcc 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt @@ -16,6 +16,7 @@ import com.github.dedis.popstellar.model.objects.security.MessageID import com.github.dedis.popstellar.model.objects.security.PoPToken import com.github.dedis.popstellar.model.objects.view.LaoView import com.github.dedis.popstellar.repository.LAORepository +import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository import com.github.dedis.popstellar.repository.RollCallRepository import com.github.dedis.popstellar.repository.SocialMediaRepository import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager @@ -47,6 +48,7 @@ constructor( */ private val laoRepo: LAORepository, private val rollCallRepo: RollCallRepository, + private val linkedOrgRepo: LinkedOrganizationsRepository, private val schedulerProvider: SchedulerProvider, private val socialMediaRepository: SocialMediaRepository, private val networkManager: GlobalNetworkManager, @@ -225,36 +227,77 @@ constructor( } val chirps: Observable> - get() = - socialMediaRepository - .getChirpsOfLao(laoId) - // Retrieve chirp subjects per id - .map { ids: Set -> - val chirps: MutableList> = ArrayList(ids.size) - for (id in ids) { - chirps.add(socialMediaRepository.getChirp(laoId, id)) - } - chirps - } - // Zip the subjects together to a sorted list - .flatMap { observables: List> -> - Observable.combineLatest(observables) { chirps: Array -> - Arrays.stream(chirps) - .map { obj: Any? -> Chirp::class.java.cast(obj) } - .sorted( - Comparator.comparing { chirp: Chirp? -> - if (chirp != null) -chirp.timestamp else 0 - }) - .collect(Collectors.toList()) - } - } - // We want to observe these changes on the main thread such that any modification done - // to - // the view are done on the thread. Otherwise, the app might crash - .observeOn(schedulerProvider.mainThread()) + get() { + val laoIds = listOf(laoId) + linkedOrgRepo.getLinkedLaos(laoId).keys + val laoObservables: MutableList>> = ArrayList() + for (lao in laoIds) { + val observable = + socialMediaRepository + .getChirpsOfLao(lao) + .map { ids: Set -> + val chirps: MutableList> = ArrayList(ids.size) + for (id in ids) { + chirps.add(socialMediaRepository.getChirp(lao, id)) + } + chirps + } + .flatMap { observables: List> -> + Observable.combineLatest(observables) { chirps: Array -> + Arrays.stream(chirps) + .map { obj: Any? -> Chirp::class.java.cast(obj) } + .sorted( + Comparator.comparing { chirp: Chirp? -> + if (chirp != null) -chirp.timestamp else 0 + }) + .collect(Collectors.toList()) + } + } + laoObservables.add(observable) + } + val combinedObservables = + Observable.combineLatest(laoObservables) { chirpsLists: Array -> + Arrays.stream(chirpsLists) + .flatMap { chirpsList: Any? -> (chirpsList as List).stream() } + .sorted( + Comparator.comparing { chirp: Chirp? -> + if (chirp != null) -chirp.timestamp else 0 + }) + .collect(Collectors.toList()) + } + // We want to observe these changes on the main thread such that any modification done + // to + // the view are done on the thread. Otherwise, the app might crash + return combinedObservables.observeOn(schedulerProvider.mainThread()) + /*return socialMediaRepository + .getChirpsOfLao(laoId) + // Retrieve chirp subjects per id + .map { ids: Set -> + val chirps: MutableList> = ArrayList(ids.size) + for (id in ids) { + chirps.add(socialMediaRepository.getChirp(laoId, id)) + } + chirps + } + // Zip the subjects together to a sorted list + .flatMap { observables: List> -> + Observable.combineLatest(observables) { chirps: Array -> + Arrays.stream(chirps) + .map { obj: Any? -> Chirp::class.java.cast(obj) } + .sorted( + Comparator.comparing { chirp: Chirp? -> + if (chirp != null) -chirp.timestamp else 0 + }) + .collect(Collectors.toList()) + } + } + // We want to observe these changes on the main thread such that any modification done + // to + // the view are done on the thread. Otherwise, the app might crash + .observeOn(schedulerProvider.mainThread())*/ + } @Throws(UnknownChirpException::class) - fun getReactions(chirpId: MessageID): Observable> { + fun getReactions(laoId: String, chirpId: MessageID): Observable> { return socialMediaRepository .getReactions(laoId, chirpId) .observeOn(schedulerProvider.mainThread()) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt index 11b5de5e30..7c76dfa71c 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt @@ -44,7 +44,8 @@ constructor( senderPk, addChirp.text, addChirp.timestamp, - addChirp.getParentId().orElse(MessageID(""))) + addChirp.getParentId().orElse(MessageID("")), + context.channel.extractLaoId()) socialMediaRepo.addChirp(laoView.id, chirp) } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index 863412df95..282ed7482f 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -67,6 +67,7 @@ constructor( */ @Throws(UnknownLaoException::class) fun handleTokensExchange(context: HandlerContext, tokenExchange: TokensExchange) { + Timber.tag(TAG).d("TokensExchange received") // Adds the tokens in the repository linkedOrgRepo.addLinkedLao( context.channel.extractLaoId(), tokenExchange.laoId, tokenExchange.tokens) @@ -84,6 +85,7 @@ constructor( { Timber.tag(TAG).d("subscription a success") }, { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) } + Timber.tag(TAG).d("TokensExchange successful") } private fun putRemoteLaoTokensInRepository(myLaoId: String) { From 7bf43cbe7e410f1623cbb9ef0af39615d9f50d28 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 15:15:12 +0200 Subject: [PATCH 09/18] Adapted chirps for federation, update --- .../dedis/popstellar/utility/handler/data/ChirpHandler.kt | 8 +++----- .../utility/handler/data/LinkedOrganizationsHandler.kt | 7 +++++++ .../popstellar/utility/handler/data/ReactionHandler.kt | 6 ++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt index 7c76dfa71c..758b6ce8b7 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt @@ -37,7 +37,6 @@ constructor( channel, addChirp.getParentId().orElse(MessageID(""))) - val laoView = laoRepo.getLaoViewByChannel(channel) val chirp = Chirp( messageId, @@ -45,9 +44,9 @@ constructor( addChirp.text, addChirp.timestamp, addChirp.getParentId().orElse(MessageID("")), - context.channel.extractLaoId()) + channel.extractLaoId()) - socialMediaRepo.addChirp(laoView.id, chirp) + socialMediaRepo.addChirp(channel.extractLaoId(), chirp) } /** @@ -61,8 +60,7 @@ constructor( val channel = context.channel Timber.tag(TAG).d("handleDeleteChirp: channel: %s, id: %s", channel, deleteChirp.chirpId) - val laoView = laoRepo.getLaoViewByChannel(channel) - val chirpExist = socialMediaRepo.deleteChirp(laoView.id, deleteChirp.chirpId) + val chirpExist = socialMediaRepo.deleteChirp(channel.extractLaoId(), deleteChirp.chirpId) if (!chirpExist) { throw InvalidMessageIdException(deleteChirp, deleteChirp.chirpId) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index 282ed7482f..80ca2d1e4f 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -85,6 +85,13 @@ constructor( { Timber.tag(TAG).d("subscription a success") }, { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) } + laoRepo.addDisposable( + context.messageSender + .subscribe( + Channel.getLaoChannel(tokenExchange.laoId).subChannel("social").subChannel("reactions")) + .subscribe( + { Timber.tag(TAG).d("subscription a success") }, + { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) Timber.tag(TAG).d("TokensExchange successful") } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt index fd2f8044da..05280bd263 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt @@ -31,12 +31,11 @@ constructor( val senderPk = context.senderPk Timber.tag(TAG).d("handleAddReaction: channel: %s, chirp id: %s", channel, addReaction.chirpId) - val laoView = laoRepo.getLaoViewByChannel(channel) val reaction = Reaction( messageId, senderPk, addReaction.codepoint, addReaction.chirpId, addReaction.timestamp) - if (!socialMediaRepo.addReaction(laoView.id, reaction)) { + if (!socialMediaRepo.addReaction(channel.extractLaoId(), reaction)) { throw InvalidMessageIdException(addReaction, addReaction.chirpId) } } @@ -53,8 +52,7 @@ constructor( Timber.tag(TAG) .d("handleDeleteReaction: channel: %s, reaction id: %s", channel, deleteReaction.reactionID) - val laoView = laoRepo.getLaoViewByChannel(channel) - if (!socialMediaRepo.deleteReaction(laoView.id, deleteReaction.reactionID)) { + if (!socialMediaRepo.deleteReaction(channel.extractLaoId(), deleteReaction.reactionID)) { throw InvalidMessageIdException(deleteReaction, deleteReaction.reactionID) } } From 65dc966919e4f257069f69030f9c210f39319ea9 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 21:54:27 +0200 Subject: [PATCH 10/18] Corrected tests --- .../lao/socialmedia/SocialMediaViewModel.kt | 29 +------------------ .../data/LinkedOrganizationsHandler.kt | 14 +++++---- .../lao/socialmedia/ChirpListAdapterTest.kt | 5 ++-- .../SocialMediaHomeFragmentTest.kt | 9 +++++- .../popstellar/model/objects/ChirpTest.kt | 16 +++++++--- .../repository/SocialMediaRepositoryTest.kt | 9 ++++-- .../database/SocialMediaDatabaseTest.kt | 6 ++-- .../utility/handler/ChirpHandlerTest.kt | 2 +- 8 files changed, 43 insertions(+), 47 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt index 80108dbfcc..bb70f57ada 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt @@ -265,35 +265,8 @@ constructor( .collect(Collectors.toList()) } // We want to observe these changes on the main thread such that any modification done - // to - // the view are done on the thread. Otherwise, the app might crash + // to the view are done on the thread. Otherwise, the app might crash return combinedObservables.observeOn(schedulerProvider.mainThread()) - /*return socialMediaRepository - .getChirpsOfLao(laoId) - // Retrieve chirp subjects per id - .map { ids: Set -> - val chirps: MutableList> = ArrayList(ids.size) - for (id in ids) { - chirps.add(socialMediaRepository.getChirp(laoId, id)) - } - chirps - } - // Zip the subjects together to a sorted list - .flatMap { observables: List> -> - Observable.combineLatest(observables) { chirps: Array -> - Arrays.stream(chirps) - .map { obj: Any? -> Chirp::class.java.cast(obj) } - .sorted( - Comparator.comparing { chirp: Chirp? -> - if (chirp != null) -chirp.timestamp else 0 - }) - .collect(Collectors.toList()) - } - } - // We want to observe these changes on the main thread such that any modification done - // to - // the view are done on the thread. Otherwise, the app might crash - .observeOn(schedulerProvider.mainThread())*/ } @Throws(UnknownChirpException::class) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index 80ca2d1e4f..adefffbc33 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -86,12 +86,14 @@ constructor( { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) } laoRepo.addDisposable( - context.messageSender - .subscribe( - Channel.getLaoChannel(tokenExchange.laoId).subChannel("social").subChannel("reactions")) - .subscribe( - { Timber.tag(TAG).d("subscription a success") }, - { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) + context.messageSender + .subscribe( + Channel.getLaoChannel(tokenExchange.laoId) + .subChannel("social") + .subChannel("reactions")) + .subscribe( + { Timber.tag(TAG).d("subscription a success") }, + { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) Timber.tag(TAG).d("TokensExchange successful") } diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt index 23bbdbc216..0b70b28371 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt @@ -315,9 +315,10 @@ class ChirpListAdapterTest { private const val TEXT_2 = "text2" private const val TIMESTAMP_1: Long = 1632204910 private const val TIMESTAMP_2: Long = 1632204900 - private val CHIRP_1 = Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID("")) + private val CHIRP_1 = + Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID(""), LAO_ID) private val CHIRP_2 = - Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID("")).deleted() + Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID(""), LAO_ID).deleted() private val TIMESTAMP = Instant.now().epochSecond private fun createChirpList(): List { diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt index 3d4f78cfd0..8143361ed5 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt @@ -196,7 +196,14 @@ class SocialMediaHomeFragmentTest { val socialMediaViewModel = LaoActivity.obtainSocialMediaViewModel(activity, LAO_ID) - val chirp = Chirp(Base64DataUtils.generateMessageID(), SENDER_1, "text", Instant.now().epochSecond, MessageID("")) + val chirp = Chirp( + Base64DataUtils.generateMessageID(), + SENDER_1, + "text", + Instant.now().epochSecond, + MessageID(""), + LAO_ID + ) socialMediaViewModel.sendChirp(chirp.text, chirp.parentId, chirp.timestamp) socialMediaViewModel.sendReaction(Reaction.ReactionEmoji.UPVOTE.code, chirp.id, Instant.now().epochSecond) diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt index f7d1610381..1eb4c240c3 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt @@ -10,14 +10,14 @@ class ChirpTest { fun createChirpWithEmptyIdFails() { Assert.assertThrows( IllegalArgumentException::class.java - ) { Chirp(EMPTY_MESSAGE_ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID) } + ) { Chirp(EMPTY_MESSAGE_ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) } } @Test fun createChirpWithNegativeTimestampFails() { Assert.assertThrows( IllegalArgumentException::class.java - ) { Chirp(ID, SENDER, TEXT, -5, EMPTY_MESSAGE_ID) } + ) { Chirp(ID, SENDER, TEXT, -5, EMPTY_MESSAGE_ID, LAO_ID) } } @Test @@ -30,12 +30,12 @@ class ChirpTest { + " the threshold.") Assert.assertThrows( IllegalArgumentException::class.java - ) { Chirp(ID, SENDER, textTooLong, TIMESTAMP, EMPTY_MESSAGE_ID) } + ) { Chirp(ID, SENDER, textTooLong, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) } } @Test fun deletedChirpProducesASimilarChirpWithEmptyTextAndDeletedProperty() { - val chirp = Chirp(ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID) + val chirp = Chirp(ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) val deleted = chirp.deleted() Assert.assertEquals(chirp.id, deleted.id) Assert.assertEquals(chirp.sender, deleted.sender) @@ -43,6 +43,13 @@ class ChirpTest { Assert.assertEquals(chirp.timestamp, deleted.timestamp) Assert.assertEquals(chirp.parentId, deleted.parentId) Assert.assertTrue(deleted.isDeleted) + Assert.assertEquals(chirp.laoId, deleted.laoId) + } + + @Test + fun laoIdIsStoredCorrectlyInChirp() { + val chirp = Chirp(ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) + Assert.assertEquals(LAO_ID, chirp.laoId) } companion object { @@ -52,5 +59,6 @@ class ChirpTest { private const val TEXT = "This is a Chirp !" private const val TIMESTAMP: Long = 10000 private val EMPTY_MESSAGE_ID = MessageID("") + private val LAO_ID = Lao.generateLaoId(Base64DataUtils.generatePublicKey(), 1000, "LAO") } } \ No newline at end of file diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt index 10fa527935..ebd3646768 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt @@ -111,7 +111,8 @@ class SocialMediaRepositoryTest { Base64DataUtils.generatePublicKey(), "This is another chirp !", 1003, - MessageID("") + MessageID(""), + LAO_ID ) repo.addChirp(LAO_ID, invalidChirp) @@ -214,8 +215,10 @@ class SocialMediaRepositoryTest { private val CHIRP1_ID = Base64DataUtils.generateMessageID() private val CHIRP2_ID = Base64DataUtils.generateMessageID() private const val EMOJI = "\uD83D\uDC4D" - private val CHIRP_1 = Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID("")) - private val CHIRP_2 = Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID("")) + private val CHIRP_1 = + Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID(""), LAO_ID) + private val CHIRP_2 = + Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID(""), LAO_ID) private val REACTION_1 = Reaction( Base64DataUtils.generateMessageID(), diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt index db1a92ad8a..92b854d7f2 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt @@ -84,8 +84,10 @@ class SocialMediaDatabaseTest { private val CHIRP1_ID = Base64DataUtils.generateMessageID() private val CHIRP2_ID = Base64DataUtils.generateMessageID() private const val EMOJI = "\uD83D\uDC4D" - private val CHIRP_1 = Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID("")) - private val CHIRP_2 = Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID("")) + private val CHIRP_1 = + Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID(""), LAO_ID) + private val CHIRP_2 = + Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID(""), LAO_ID) private val REACTION_1 = Reaction( Base64DataUtils.generateMessageID(), diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt index 03a8fc68d9..f8f281715b 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt @@ -122,7 +122,7 @@ class ChirpHandlerTest { private const val TEXT = "textOfTheChirp" private val PARENT_ID = Base64DataUtils.generateMessageID() private val CHIRP_ID = Base64DataUtils.generateMessageID() - private val CHIRP = Chirp(CHIRP_ID, SENDER, TEXT, CREATION_TIME, PARENT_ID) + private val CHIRP = Chirp(CHIRP_ID, SENDER, TEXT, CREATION_TIME, PARENT_ID, LAO_ID) private val ADD_CHIRP = AddChirp(TEXT, PARENT_ID, CREATION_TIME) private val DELETE_CHIRP = DeleteChirp(CHIRP_ID, DELETION_TIME) } From ac25784cc6110bfc07592d337096f5be8cfe553f Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 22:03:51 +0200 Subject: [PATCH 11/18] Corrected ChirpListFragmentTest --- .../popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt index 8ae049821c..29420f234b 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt @@ -338,9 +338,10 @@ class ChirpListFragmentTest { private const val TEXT_2 = "text2" private const val TIMESTAMP_1: Long = 1632204910 private const val TIMESTAMP_2: Long = 1632204900 - private val CHIRP_1 = Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID("")) + private val CHIRP_1 = + Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID(""), LAO_ID) private val CHIRP_2 = - Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID("")).deleted() + Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID(""), LAO_ID).deleted() private val TIMESTAMP = Instant.now().epochSecond private fun createChirpList(): List { From 4a32b597d6afbc52633464a74fc9271303f437e2 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 26 Jun 2024 23:39:20 +0200 Subject: [PATCH 12/18] Cleaned handler and added tests --- .../data/LinkedOrganizationsHandler.kt | 22 +++--- ...LinkedOrganizationsFragmentPageObject.java | 8 ++ ...inkedOrganizationsFragmentOrganizerTest.kt | 6 ++ .../LinkedOrganizationsRepositoryTest.kt | 78 ++++++++++++++++++- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index adefffbc33..edc4cf6b8d 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -50,7 +50,7 @@ constructor( .subscribe(Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) .subscribe( { putRemoteLaoTokensInRepository(laoId) }, - { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) + { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) } else { Timber.tag(TAG).d("Invalid FederationResult success") } @@ -67,7 +67,6 @@ constructor( */ @Throws(UnknownLaoException::class) fun handleTokensExchange(context: HandlerContext, tokenExchange: TokensExchange) { - Timber.tag(TAG).d("TokensExchange received") // Adds the tokens in the repository linkedOrgRepo.addLinkedLao( context.channel.extractLaoId(), tokenExchange.laoId, tokenExchange.tokens) @@ -80,21 +79,18 @@ constructor( laoRepo.addDisposable( context.messageSender .subscribe( - Channel.getLaoChannel(tokenExchange.laoId).subChannel("social").subChannel(t)) + Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(t)) .subscribe( - { Timber.tag(TAG).d("subscription a success") }, - { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) + { Timber.tag(TAG).d(SUCCESS) }, + { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) } laoRepo.addDisposable( context.messageSender .subscribe( - Channel.getLaoChannel(tokenExchange.laoId) - .subChannel("social") - .subChannel("reactions")) + Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(REACTIONS)) .subscribe( - { Timber.tag(TAG).d("subscription a success") }, - { error: Throwable -> Timber.tag(TAG).e(error, "subscription error") })) - Timber.tag(TAG).d("TokensExchange successful") + { Timber.tag(TAG).d(SUCCESS) }, + { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) } private fun putRemoteLaoTokensInRepository(myLaoId: String) { @@ -110,5 +106,9 @@ constructor( companion object { private val TAG = LinkedOrganizationsHandler::class.java.simpleName + private val SOCIAL = "social" + private val REACTIONS = "reactions" + private val SUCCESS = "subscription is a success" + private val ERROR = "subscription error" } } diff --git a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java index 493d93aa41..9be9a3749a 100644 --- a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java +++ b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java @@ -32,4 +32,12 @@ public static ViewInteraction nextInvitationFragment() { public static ViewInteraction nextQrScannerFragment() { return onView(withId(R.id.fragment_qr_scanner)); } + + public static ViewInteraction noOrganizationsText() { + return onView(withId(R.id.no_organizations_text)); + } + + public static ViewInteraction listOrganizationsText() { + return onView(withId(R.id.list_organizations_text)); + } } diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt index 6a73fea5eb..f0a13e01a5 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt @@ -105,6 +105,12 @@ class LinkedOrganizationsFragmentOrganizerTest { LinkedOrganizationsFragmentPageObject.nextQrScannerFragment().check(matches(isDisplayed())) } + @Test + fun testTextDisplayed() { + LinkedOrganizationsFragmentPageObject.noOrganizationsText().check(matches(isDisplayed())) + LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(withEffectiveVisibility(Visibility.GONE))) + } + companion object { private val KEY_PAIR = Base64DataUtils.generateKeyPair() private const val LAO_NAME = "LAO" diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt index 55b436b900..47427b2cc7 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt @@ -1,10 +1,15 @@ package com.github.dedis.popstellar.repository +import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange +import com.github.dedis.popstellar.model.objects.Channel import com.github.dedis.popstellar.model.objects.Lao import com.github.dedis.popstellar.testutils.Base64DataUtils import com.github.dedis.popstellar.utility.handler.data.HandlerContext import com.github.dedis.popstellar.utility.handler.data.LinkedOrganizationsHandler +import com.google.gson.Gson import org.junit.Assert import org.junit.Test import org.mockito.Mockito @@ -60,7 +65,7 @@ class LinkedOrganizationsRepositoryTest { } @Test - fun handlerAndRepoTest() { + fun handleChallengeAndRepoTest() { val mockLaoRepo = Mockito.mock(LAORepository::class.java) val mockContext = Mockito.mock(HandlerContext::class.java) val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) @@ -70,15 +75,86 @@ class LinkedOrganizationsRepositoryTest { Assert.assertEquals(CHALLENGE, REPO.getChallenge()) } + @Test + fun repoLinkedLaoTest() { + Assert.assertEquals(mutableMapOf>(), REPO.getLinkedLaos(LAO_ID)) + + REPO.addLinkedLao(LAO_ID, LAO_ID_2, TOKENS_ARRAY_1) + REPO.addLinkedLao(LAO_ID_3, LAO_ID_2, TOKENS_ARRAY_2) + REPO.updateAndNotifyLinkedLao(LAO_ID_4, LAO_ID_3, TOKENS_ARRAY_2, "rollcall") + REPO.addLinkedLao(LAO_ID, LAO_ID_3, TOKENS_ARRAY_2) + + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID).keys.contains(LAO_ID_2)) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID).keys.contains(LAO_ID_3)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID).keys.contains(LAO_ID_4)) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_3).keys.contains(LAO_ID_2)) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID_3)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID_2)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID_4)) + + Assert.assertEquals(TOKENS_ARRAY_1, REPO.getLinkedLaos(LAO_ID)[LAO_ID_2]) + Assert.assertEquals(TOKENS_ARRAY_2, REPO.getLinkedLaos(LAO_ID)[LAO_ID_3]) + Assert.assertEquals(TOKENS_ARRAY_2, REPO.getLinkedLaos(LAO_ID_4)[LAO_ID_3]) + } + + @Test + fun handleResultAndRepoTest() { + val mockLaoRepo = Mockito.mock(LAORepository::class.java) + val mockContext = Mockito.mock(HandlerContext::class.java) + val mockChannel = Mockito.mock(Channel::class.java) + Mockito.`when`(mockContext.channel).thenReturn(mockChannel) + Mockito.`when`(mockChannel.extractLaoId()).thenReturn(LAO_ID_5) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + REPO.updateChallenge(CHALLENGE) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) + Assert.assertThrows(NullPointerException::class.java) { + handler.handleResult(mockContext, RESULT) + } + Assert.assertEquals(mutableSetOf(LAO_ID_4), REPO.getLinkedLaos(LAO_ID_5).keys) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_5)[LAO_ID_4]!!.isEmpty()) + } + + @Test + fun handleTokensExchangeAndRepoTest() { + val mockLaoRepo = Mockito.mock(LAORepository::class.java) + val mockContext = Mockito.mock(HandlerContext::class.java) + val mockChannel = Mockito.mock(Channel::class.java) + Mockito.`when`(mockContext.channel).thenReturn(mockChannel) + Mockito.`when`(mockChannel.extractLaoId()).thenReturn(LAO_ID_6) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + REPO.updateChallenge(CHALLENGE) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) + Assert.assertThrows(NullPointerException::class.java) { + handler.handleTokensExchange(mockContext, TOKENS_EXCHANGE) + } + Assert.assertEquals(mutableSetOf(LAO_ID_3), REPO.getLinkedLaos(LAO_ID_6).keys) + Assert.assertEquals(TOKENS_ARRAY_1, REPO.getLinkedLaos(LAO_ID_6)[LAO_ID_3]) + } + companion object { private val ORGANIZER = Base64DataUtils.generatePublicKey() private val CREATION = Instant.now().epochSecond private const val NAME = "Lao name" private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) + private val LAO_ID_2 = "id2" + private val LAO_ID_3 = "id3" + private val LAO_ID_4 = "id4" + private val LAO_ID_5 = "id5" + private val LAO_ID_6 = "id6" private val TIMESTAMP = Instant.now().epochSecond private const val SERVER_ADDRESS = "wss://1.1.1.1:9000/client" private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" + private val TOKENS_ARRAY_1 = arrayOf("token1", "token2") + private val TOKENS_ARRAY_2 = arrayOf("token7", "token8", "token9") private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) + private val MG_CHALLENGE = MessageGeneral(Base64DataUtils.generateKeyPair(), CHALLENGE, Gson()) + private val RESULT = FederationResult("success", publicKey = "PK", challenge = MG_CHALLENGE) + private val TOKENS_EXCHANGE = TokensExchange(LAO_ID_3, "rollCall", TOKENS_ARRAY_1, TIMESTAMP) private val REPO = LinkedOrganizationsRepository() } } \ No newline at end of file From 4b52dbf459f0149a7e4097140b87dc16faf616de Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 27 Jun 2024 00:26:54 +0200 Subject: [PATCH 13/18] Added one test --- .../data/LinkedOrganizationsHandler.kt | 8 +++---- .../LinkedOrganizationsRepositoryTest.kt | 22 +++++++++++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index edc4cf6b8d..a3b107199f 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -106,9 +106,9 @@ constructor( companion object { private val TAG = LinkedOrganizationsHandler::class.java.simpleName - private val SOCIAL = "social" - private val REACTIONS = "reactions" - private val SUCCESS = "subscription is a success" - private val ERROR = "subscription error" + private const val SOCIAL = "social" + private const val REACTIONS = "reactions" + private const val SUCCESS = "subscription is a success" + private const val ERROR = "subscription error" } } diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt index 47427b2cc7..92496984ca 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt @@ -111,12 +111,28 @@ class LinkedOrganizationsRepositoryTest { REPO.updateChallenge(CHALLENGE) val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) Assert.assertThrows(NullPointerException::class.java) { - handler.handleResult(mockContext, RESULT) + handler.handleResult(mockContext, RESULT_SUCCESS) } Assert.assertEquals(mutableSetOf(LAO_ID_4), REPO.getLinkedLaos(LAO_ID_5).keys) Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_5)[LAO_ID_4]!!.isEmpty()) } + @Test + fun handleResultAndRepoTest2() { + val mockLaoRepo = Mockito.mock(LAORepository::class.java) + val mockContext = Mockito.mock(HandlerContext::class.java) + val mockChannel = Mockito.mock(Channel::class.java) + Mockito.`when`(mockContext.channel).thenReturn(mockChannel) + Mockito.`when`(mockChannel.extractLaoId()).thenReturn(LAO_ID_7) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + REPO.updateChallenge(CHALLENGE) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) + handler.handleResult(mockContext, RESULT_FAILURE) + Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) + } + @Test fun handleTokensExchangeAndRepoTest() { val mockLaoRepo = Mockito.mock(LAORepository::class.java) @@ -146,6 +162,7 @@ class LinkedOrganizationsRepositoryTest { private val LAO_ID_4 = "id4" private val LAO_ID_5 = "id5" private val LAO_ID_6 = "id6" + private val LAO_ID_7 = "id7" private val TIMESTAMP = Instant.now().epochSecond private const val SERVER_ADDRESS = "wss://1.1.1.1:9000/client" private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" @@ -153,7 +170,8 @@ class LinkedOrganizationsRepositoryTest { private val TOKENS_ARRAY_2 = arrayOf("token7", "token8", "token9") private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) private val MG_CHALLENGE = MessageGeneral(Base64DataUtils.generateKeyPair(), CHALLENGE, Gson()) - private val RESULT = FederationResult("success", publicKey = "PK", challenge = MG_CHALLENGE) + private val RESULT_SUCCESS = FederationResult("success", publicKey = "PK", challenge = MG_CHALLENGE) + private val RESULT_FAILURE = FederationResult("failure", reason = "fail", challenge = MG_CHALLENGE) private val TOKENS_EXCHANGE = TokensExchange(LAO_ID_3, "rollCall", TOKENS_ARRAY_1, TIMESTAMP) private val REPO = LinkedOrganizationsRepository() } From 07913e0dd43d3aaef41e42d4252597f7fe5b5de3 Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 27 Jun 2024 00:46:24 +0200 Subject: [PATCH 14/18] Corrected one condition --- .../repository/LinkedOrganizationsRepositoryTest.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt index 92496984ca..2e48f54f17 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt @@ -131,6 +131,14 @@ class LinkedOrganizationsRepositoryTest { val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) handler.handleResult(mockContext, RESULT_FAILURE) Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) + + REPO.otherLaoId = null + handler.handleResult(mockContext, RESULT_SUCCESS) + Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + handler.handleResult(mockContext, RESULT_SUCCESS) + Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) } @Test From 147f0be73b617527ece1bc18002c3cbf66537582 Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 27 Jun 2024 12:57:14 +0200 Subject: [PATCH 15/18] Improved result, handler and new tests --- .../data/federation/FederationResult.kt | 25 +++++++++++-------- .../data/LinkedOrganizationsHandler.kt | 8 +++--- ...edOrganizationsFragmentNonOrganizerTest.kt | 16 ++++++++++++ ...inkedOrganizationsFragmentOrganizerTest.kt | 2 +- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt index 7d72a79d88..0befc2f8f1 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -25,15 +25,15 @@ class FederationResult init { when (status) { - "failure" -> { - require(reason != null) { "Reason must be provided for failure status." } - require(publicKey == null) { "Public key must be null for failure status." } + FAILURE -> { + require(reason != null) { "Reason must be provided for $FAILURE status." } + require(publicKey == null) { "Public key must be null for $FAILURE status." } } - "success" -> { - require(publicKey != null) { "Public key must be provided for success status." } - require(reason == null) { "Reason must be null for success status." } + SUCCESS -> { + require(publicKey != null) { "Public key must be provided for $SUCCESS status." } + require(reason == null) { "Reason must be null for $SUCCESS status." } } - else -> throw IllegalArgumentException("Status must be either 'failure' or 'success'.") + else -> throw IllegalArgumentException("Status must be either '$FAILURE' or '$SUCCESS'.") } } @@ -62,9 +62,9 @@ class FederationResult } override fun toString(): String { - if (status == "failure") { + if (status == FAILURE) { return "FederationResult{status='$status', reason='$reason', challenge='$challenge'}" - } else if (status == "success") { + } else if (status == SUCCESS) { return "FederationResult{status='$status', public_key='$publicKey', " + "challenge='$challenge'}" } @@ -72,6 +72,11 @@ class FederationResult } fun isSuccess(): Boolean { - return status == "success" + return status == SUCCESS + } + + companion object { + private const val SUCCESS = "success" + private const val FAILURE = "failure" } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index a3b107199f..f4c47a4b17 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -9,6 +9,7 @@ import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository import com.github.dedis.popstellar.repository.RollCallRepository import com.github.dedis.popstellar.utility.error.UnknownLaoException import com.github.dedis.popstellar.utility.error.keys.NoRollCallException +import io.reactivex.disposables.CompositeDisposable import javax.inject.Inject import timber.log.Timber @@ -45,7 +46,7 @@ constructor( linkedOrgRepo.otherLaoId != null) { val laoId = context.channel.extractLaoId() linkedOrgRepo.addLinkedLao(laoId, linkedOrgRepo.otherLaoId!!, arrayOf()) - laoRepo.addDisposable( + disposables.add( context.messageSender .subscribe(Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) .subscribe( @@ -76,7 +77,7 @@ constructor( // LAO. This might be changed in the future (making a pop-up asking the user if he/she wants // to subscribe to that) tokenExchange.tokens.forEach { t -> - laoRepo.addDisposable( + disposables.add( context.messageSender .subscribe( Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(t)) @@ -84,7 +85,7 @@ constructor( { Timber.tag(TAG).d(SUCCESS) }, { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) } - laoRepo.addDisposable( + disposables.add( context.messageSender .subscribe( Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(REACTIONS)) @@ -106,6 +107,7 @@ constructor( companion object { private val TAG = LinkedOrganizationsHandler::class.java.simpleName + private val disposables = CompositeDisposable() private const val SOCIAL = "social" private const val REACTIONS = "reactions" private const val SUCCESS = "subscription is a success" diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt index 8bf4cd2b6d..3861adeb69 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt @@ -35,6 +35,9 @@ class LinkedOrganizationsFragmentNonOrganizerTest { @Inject lateinit var laoRepository: LAORepository + @BindValue @Mock + lateinit var linkedOrganizationsViewModel: LinkedOrganizationsViewModel + @BindValue @Mock lateinit var keyManager: KeyManager @@ -54,6 +57,7 @@ class LinkedOrganizationsFragmentNonOrganizerTest { laoRepository.updateLao(LAO) Mockito.`when`(keyManager.mainKeyPair).thenReturn(KEY_PAIR) Mockito.`when`(keyManager.mainPublicKey).thenReturn(POP_TOKEN.publicKey) + Mockito.`when`(linkedOrganizationsViewModel.getLinkedLaosMap()).thenReturn(mutableMapOf(LAO_ID to arrayOf())) } } @@ -75,6 +79,18 @@ class LinkedOrganizationsFragmentNonOrganizerTest { .check(matches(withEffectiveVisibility(Visibility.GONE))) } + @Test + fun testLAOTextDisplayed() { + LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(isDisplayed())) + LinkedOrganizationsFragmentPageObject.noOrganizationsText().check(matches(withEffectiveVisibility(Visibility.GONE))) + } + + @Test + fun testLAOTextIsCorrect() { + val validText = "List of linked organizations :\n\n$LAO_ID" + LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(withText(validText))) + } + companion object { private val KEY_PAIR = Base64DataUtils.generateKeyPair() private val POP_TOKEN = Base64DataUtils.generatePoPToken() diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt index f0a13e01a5..5908e64d8f 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt @@ -106,7 +106,7 @@ class LinkedOrganizationsFragmentOrganizerTest { } @Test - fun testTextDisplayed() { + fun testNoLAOTextDisplayed() { LinkedOrganizationsFragmentPageObject.noOrganizationsText().check(matches(isDisplayed())) LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(withEffectiveVisibility(Visibility.GONE))) } From 20998cac360793aef3140bc141cec1f929208b01 Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 27 Jun 2024 13:55:54 +0200 Subject: [PATCH 16/18] Added message validation --- .../method/message/data/federation/FederationResult.kt | 2 ++ .../method/message/data/federation/TokensExchange.kt | 7 +++++++ .../method/message/data/federation/TokensExchangeTest.kt | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt index 0befc2f8f1..6fddbfe096 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -4,6 +4,7 @@ import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.Action import com.github.dedis.popstellar.model.network.method.message.data.Data import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.utility.MessageValidator import com.google.gson.annotations.SerializedName /** Informs about the result of the authentication procedure */ @@ -35,6 +36,7 @@ class FederationResult } else -> throw IllegalArgumentException("Status must be either '$FAILURE' or '$SUCCESS'.") } + MessageValidator.verify().validMessage(challenge) } override val `object`: String diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt index ab96e00426..d1bd403ebb 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt @@ -3,6 +3,7 @@ package com.github.dedis.popstellar.model.network.method.message.data.federation import com.github.dedis.popstellar.model.network.method.message.data.Action import com.github.dedis.popstellar.model.network.method.message.data.Data import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.utility.MessageValidator import com.google.gson.annotations.SerializedName /** Token exchange to be broadcast in the LAO */ @@ -21,6 +22,12 @@ class TokensExchange val tokens: Array, val timestamp: Long ) : Data { + + init { + MessageValidator.verify().isBase64(rollCallId, "rollCallId") + MessageValidator.verify().validPastTimes(timestamp) + } + override val `object`: String get() = Objects.FEDERATION.`object` diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt index bf796e2f6e..58a543ce81 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt @@ -48,7 +48,7 @@ class TokensExchangeTest { Assert.assertEquals(TK_EXCHANGE.hashCode().toLong(), tokensExchange2.hashCode().toLong()) val tokensExchange3 = TokensExchange(Lao.generateLaoId(ORGANIZER, CREATION, "LAO2"), ROLL_CALL_ID, TOKENS, TIMESTAMP) - val tokensExchange4 = TokensExchange(LAO_ID, "RC2", TOKENS, TIMESTAMP) + val tokensExchange4 = TokensExchange(LAO_ID, "UkMy", TOKENS, TIMESTAMP) val tokensExchange5 = TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("token1"), TIMESTAMP) Assert.assertNotEquals(TK_EXCHANGE, tokensExchange3) Assert.assertNotEquals(TK_EXCHANGE, tokensExchange4) @@ -69,7 +69,7 @@ class TokensExchangeTest { private val CREATION = Instant.now().epochSecond private const val NAME = "Lao name" private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) - private val ROLL_CALL_ID = "RC1" + private val ROLL_CALL_ID = "UkMx" private val TOKENS = arrayOf("token1", "token2", "token3") private val TIMESTAMP = Instant.now().epochSecond private val TK_EXCHANGE = TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP) From 9550e1ef58ea27ef824fa2bcb0122f642347ef01 Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 27 Jun 2024 15:43:26 +0200 Subject: [PATCH 17/18] More message validation, added disposables in the repository --- .../data/federation/FederationResult.kt | 17 +++---------- .../message/data/federation/TokensExchange.kt | 7 ++++-- .../LinkedOrganizationsRepository.kt | 22 +++++++++++++++- .../popstellar/utility/MessageValidator.kt | 25 +++++++++++++++++++ .../data/LinkedOrganizationsHandler.kt | 8 +++--- .../popstellar/di/DataRegistryModuleHelper.kt | 18 ++++++------- .../data/federation/TokensExchangeTest.kt | 22 ++++++++++++++-- .../LinkedOrganizationsRepositoryTest.kt | 25 +++++++++++-------- 8 files changed, 100 insertions(+), 44 deletions(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt index 6fddbfe096..285a97044a 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -25,18 +25,7 @@ class FederationResult ) : Data { init { - when (status) { - FAILURE -> { - require(reason != null) { "Reason must be provided for $FAILURE status." } - require(publicKey == null) { "Public key must be null for $FAILURE status." } - } - SUCCESS -> { - require(publicKey != null) { "Public key must be provided for $SUCCESS status." } - require(reason == null) { "Reason must be null for $SUCCESS status." } - } - else -> throw IllegalArgumentException("Status must be either '$FAILURE' or '$SUCCESS'.") - } - MessageValidator.verify().validMessage(challenge) + MessageValidator.verify().isValidFederationResult(status, reason, publicKey, challenge) } override val `object`: String @@ -78,7 +67,7 @@ class FederationResult } companion object { - private const val SUCCESS = "success" - private const val FAILURE = "failure" + const val SUCCESS = "success" + const val FAILURE = "failure" } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt index d1bd403ebb..229a0974bc 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt @@ -24,8 +24,11 @@ class TokensExchange ) : Data { init { - MessageValidator.verify().isBase64(rollCallId, "rollCallId") - MessageValidator.verify().validPastTimes(timestamp) + MessageValidator.verify() + .isNotEmptyBase64(laoId, "lao_id") + .isBase64(rollCallId, "roll_call_id") + .areNotEmptyBase64(*tokens, field = "tokens") + .validPastTimes(timestamp) } override val `object`: String diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index 1042cf8331..b932dc1bb2 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -1,6 +1,14 @@ package com.github.dedis.popstellar.repository +import android.app.Activity +import android.app.Application +import androidx.lifecycle.Lifecycle import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.utility.GeneralUtils +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable +import java.util.EnumMap +import java.util.function.Consumer import javax.inject.Inject import javax.inject.Singleton @@ -10,17 +18,25 @@ import javax.inject.Singleton * Its main purpose is to store received messages */ @Singleton -class LinkedOrganizationsRepository @Inject constructor() { +class LinkedOrganizationsRepository @Inject constructor(application: Application) { private var challenge: Challenge? = null private var onChallengeUpdatedCallback: ((Challenge) -> Unit)? = null private var linkedLaos: MutableMap>> = mutableMapOf() private var onLinkedLaosUpdatedCallback: ((String, MutableMap>) -> Unit)? = null private var newTokensNotifyFunction: ((String, String, String, Array) -> Unit)? = null + private val disposables = CompositeDisposable() var otherLaoId: String? = null var otherServerAddr: String? = null var otherPublicKey: String? = null + init { + val consumerMap: MutableMap> = + EnumMap(Lifecycle.Event::class.java) + consumerMap[Lifecycle.Event.ON_STOP] = Consumer { disposables.clear() } + application.registerActivityLifecycleCallbacks(GeneralUtils.buildLifecycleCallback(consumerMap)) + } + fun updateChallenge(challenge: Challenge) { this.challenge = challenge onChallengeUpdatedCallback?.invoke(challenge) @@ -64,6 +80,10 @@ class LinkedOrganizationsRepository @Inject constructor() { return linkedLaos.getOrDefault(laoId, mutableMapOf()) } + fun addDisposable(disposable: Disposable) { + disposables.add(disposable) + } + fun flush() { otherLaoId = null otherServerAddr = null diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt index 537599f83e..88619aeb39 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt @@ -3,6 +3,7 @@ package com.github.dedis.popstellar.utility import android.net.Uri import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.election.Vote +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult import com.github.dedis.popstellar.model.objects.Lao import com.github.dedis.popstellar.model.objects.Meeting import com.github.dedis.popstellar.model.objects.Reaction @@ -369,6 +370,30 @@ object MessageValidator { return this } + fun isValidFederationResult( + status: String, + reason: String? = null, + publicKey: String? = null, + challenge: MessageGeneral + ): MessageValidatorBuilder { + val success = FederationResult.SUCCESS + val failure = FederationResult.FAILURE + when (status) { + failure -> { + require(reason != null) { "Reason must be provided for $failure status." } + require(publicKey == null) { "Public key must be null for $failure status." } + } + success -> { + require(publicKey != null) { "Public key must be provided for $success status." } + require(reason == null) { "Reason must be null for $success status." } + verify().isNotEmptyBase64(publicKey, "public_key") + } + else -> throw IllegalArgumentException("Status must be either '$failure' or '$success'.") + } + verify().validMessage(challenge) + return this + } + companion object { // Defines how old messages can be to be considered valid, keeping it non-restrictive here for // now diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index f4c47a4b17..77ffbd442d 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -9,7 +9,6 @@ import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository import com.github.dedis.popstellar.repository.RollCallRepository import com.github.dedis.popstellar.utility.error.UnknownLaoException import com.github.dedis.popstellar.utility.error.keys.NoRollCallException -import io.reactivex.disposables.CompositeDisposable import javax.inject.Inject import timber.log.Timber @@ -46,7 +45,7 @@ constructor( linkedOrgRepo.otherLaoId != null) { val laoId = context.channel.extractLaoId() linkedOrgRepo.addLinkedLao(laoId, linkedOrgRepo.otherLaoId!!, arrayOf()) - disposables.add( + linkedOrgRepo.addDisposable( context.messageSender .subscribe(Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) .subscribe( @@ -77,7 +76,7 @@ constructor( // LAO. This might be changed in the future (making a pop-up asking the user if he/she wants // to subscribe to that) tokenExchange.tokens.forEach { t -> - disposables.add( + linkedOrgRepo.addDisposable( context.messageSender .subscribe( Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(t)) @@ -85,7 +84,7 @@ constructor( { Timber.tag(TAG).d(SUCCESS) }, { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) } - disposables.add( + linkedOrgRepo.addDisposable( context.messageSender .subscribe( Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(REACTIONS)) @@ -107,7 +106,6 @@ constructor( companion object { private val TAG = LinkedOrganizationsHandler::class.java.simpleName - private val disposables = CompositeDisposable() private const val SOCIAL = "social" private const val REACTIONS = "reactions" private const val SUCCESS = "subscription is a success" diff --git a/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt b/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt index 20d48d47b3..ca3d4c7972 100644 --- a/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt +++ b/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt @@ -64,7 +64,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), consensusRepository, - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -86,7 +86,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -109,7 +109,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -132,7 +132,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -163,7 +163,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -187,7 +187,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -219,7 +219,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -243,7 +243,7 @@ object DataRegistryModuleHelper { keyManager, serverRepo, ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -271,7 +271,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt index 58a543ce81..20bb0dacc2 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt @@ -23,6 +23,8 @@ class TokensExchangeTest { @Test fun tokensArrayTest() { Assert.assertEquals(TOKENS, TK_EXCHANGE.tokens) + val emptyArray = arrayOf() + Assert.assertEquals(emptyArray, TokensExchange(LAO_ID, ROLL_CALL_ID, emptyArray, TIMESTAMP).tokens) } @Test @@ -49,7 +51,7 @@ class TokensExchangeTest { val tokensExchange3 = TokensExchange(Lao.generateLaoId(ORGANIZER, CREATION, "LAO2"), ROLL_CALL_ID, TOKENS, TIMESTAMP) val tokensExchange4 = TokensExchange(LAO_ID, "UkMy", TOKENS, TIMESTAMP) - val tokensExchange5 = TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("token1"), TIMESTAMP) + val tokensExchange5 = TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("dG9rZW4x"), TIMESTAMP) Assert.assertNotEquals(TK_EXCHANGE, tokensExchange3) Assert.assertNotEquals(TK_EXCHANGE, tokensExchange4) Assert.assertNotEquals(TK_EXCHANGE, tokensExchange5) @@ -64,13 +66,29 @@ class TokensExchangeTest { ) } + @Test + fun invalidTokensExchangeTest() { + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange("LAOID", ROLL_CALL_ID, TOKENS, TIMESTAMP) + } + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange(LAO_ID, "RollCallId", TOKENS, TIMESTAMP) + } + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("dG9rZW4x", "token2"), TIMESTAMP) + } + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP + 200) + } + } + companion object { private val ORGANIZER = Base64DataUtils.generatePublicKey() private val CREATION = Instant.now().epochSecond private const val NAME = "Lao name" private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) private val ROLL_CALL_ID = "UkMx" - private val TOKENS = arrayOf("token1", "token2", "token3") + private val TOKENS = arrayOf("dG9rZW4x", "dG9rZW4y", "dG9rZW4z") private val TIMESTAMP = Instant.now().epochSecond private val TK_EXCHANGE = TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP) } diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt index 2e48f54f17..b4469d60ff 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt @@ -1,5 +1,6 @@ package com.github.dedis.popstellar.repository +import android.app.Application import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult @@ -16,6 +17,7 @@ import org.mockito.Mockito import java.time.Instant class LinkedOrganizationsRepositoryTest { + @Test fun repoFlushTest() { REPO.flush() @@ -165,22 +167,23 @@ class LinkedOrganizationsRepositoryTest { private val CREATION = Instant.now().epochSecond private const val NAME = "Lao name" private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) - private val LAO_ID_2 = "id2" - private val LAO_ID_3 = "id3" - private val LAO_ID_4 = "id4" - private val LAO_ID_5 = "id5" - private val LAO_ID_6 = "id6" - private val LAO_ID_7 = "id7" + private val LAO_ID_2 = "aWQy" + private val LAO_ID_3 = "aWQz" + private val LAO_ID_4 = "aWQ0" + private val LAO_ID_5 = "aWQ1" + private val LAO_ID_6 = "aWQ2" + private val LAO_ID_7 = "aWQ3" private val TIMESTAMP = Instant.now().epochSecond private const val SERVER_ADDRESS = "wss://1.1.1.1:9000/client" private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" - private val TOKENS_ARRAY_1 = arrayOf("token1", "token2") - private val TOKENS_ARRAY_2 = arrayOf("token7", "token8", "token9") + private val TOKENS_ARRAY_1 = arrayOf("dG9rZW4x", "dG9rZW4y") + private val TOKENS_ARRAY_2 = arrayOf("dG9rZW43", "dG9rZW44", "dG9rZW45") private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) private val MG_CHALLENGE = MessageGeneral(Base64DataUtils.generateKeyPair(), CHALLENGE, Gson()) - private val RESULT_SUCCESS = FederationResult("success", publicKey = "PK", challenge = MG_CHALLENGE) + private val RESULT_SUCCESS = FederationResult("success", publicKey = "UEs=", challenge = MG_CHALLENGE) private val RESULT_FAILURE = FederationResult("failure", reason = "fail", challenge = MG_CHALLENGE) private val TOKENS_EXCHANGE = TokensExchange(LAO_ID_3, "rollCall", TOKENS_ARRAY_1, TIMESTAMP) - private val REPO = LinkedOrganizationsRepository() + private val application = Application() + private val REPO = LinkedOrganizationsRepository(application) } -} \ No newline at end of file +} From 5008da97d5b0644a67febf3c0790e5c56dc32e86 Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 27 Jun 2024 16:02:12 +0200 Subject: [PATCH 18/18] Corrected sonarcloud issue --- .../message/data/federation/TokensExchange.kt | 2 +- .../dedis/popstellar/utility/MessageValidator.kt | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt index 229a0974bc..f9540491ff 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt @@ -27,7 +27,7 @@ class TokensExchange MessageValidator.verify() .isNotEmptyBase64(laoId, "lao_id") .isBase64(rollCallId, "roll_call_id") - .areNotEmptyBase64(*tokens, field = "tokens") + .arrayElementsNotEmptyBase64(tokens, field = "tokens") .validPastTimes(timestamp) } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt index 88619aeb39..4df33745fc 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt @@ -162,6 +162,19 @@ object MessageValidator { return this } + /** + * Helper method to ensure all strings in an Array are valid, not-empty URL-safe base64 + * encodings. + * + * @param inputs the strings to check + * @param field the name of the field (to print in case of error) + * @throws IllegalArgumentException if any string is empty or not a URL-safe base64 encoding + */ + fun arrayElementsNotEmptyBase64(inputs: Array, field: String): MessageValidatorBuilder { + inputs.forEach { input -> isNotEmptyBase64(input, field) } + return this + } + fun isNotNull(input: Any?, field: String): MessageValidatorBuilder { requireNotNull(input) { "$field cannot be null" } return this