diff options
author | Torsten Grote <t@grobox.de> | 2020-08-04 12:46:48 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-08-04 12:48:19 -0300 |
commit | a35337b991f8b3d38c3480fb76acd9fc6df47885 (patch) | |
tree | a7389fd479cc13292de41db5b64ec5ffc848ae9d | |
parent | 7e445c534ed34bb8097920d3505130da5c3b1d17 (diff) | |
download | wallet-kotlin-a35337b991f8b3d38c3480fb76acd9fc6df47885.tar.gz wallet-kotlin-a35337b991f8b3d38c3480fb76acd9fc6df47885.tar.bz2 wallet-kotlin-a35337b991f8b3d38c3480fb76acd9fc6df47885.zip |
Add exchange management to public wallet API
The API already works, but data is stored in-memory only.
There's a live integration test available, but ignored in CI.
4 files changed, 140 insertions, 14 deletions
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/WalletApi.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/WalletApi.kt index 61a3a1a..11fd181 100644 --- a/src/commonMain/kotlin/net/taler/wallet/kotlin/WalletApi.kt +++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/WalletApi.kt @@ -16,13 +16,25 @@ package net.taler.wallet.kotlin +import io.ktor.client.HttpClient +import net.taler.wallet.kotlin.crypto.Crypto +import net.taler.wallet.kotlin.crypto.CryptoFactory +import net.taler.wallet.kotlin.crypto.Signature +import net.taler.wallet.kotlin.exchange.Exchange +import net.taler.wallet.kotlin.exchange.ExchangeListItem +import net.taler.wallet.kotlin.exchange.GetExchangeTosResult import net.taler.wallet.kotlin.operations.Withdraw import net.taler.wallet.kotlin.operations.WithdrawalDetails import net.taler.wallet.kotlin.operations.WithdrawalDetailsForUri public class WalletApi { - private val withdrawManager = Withdraw() + private val httpClient: HttpClient = getDefaultHttpClient() + private val db: Db = DbFactory().openDb() + private val crypto: Crypto = CryptoFactory.getCrypto() + private val signature: Signature = Signature(crypto) + private val exchangeManager: Exchange = Exchange(crypto, signature, httpClient, db = db) + private val withdrawManager = Withdraw(httpClient, db, crypto, signature, exchangeManager) public fun getVersions(): SupportedVersions { return SupportedVersions( @@ -42,7 +54,10 @@ public class WalletApi { ) } - public suspend fun getWithdrawalDetailsForAmount(exchangeBaseUrl: String, amount: Amount): WithdrawalDetails { + public suspend fun getWithdrawalDetailsForAmount( + exchangeBaseUrl: String, + amount: Amount + ): WithdrawalDetails { val details = withdrawManager.getWithdrawalDetails(exchangeBaseUrl, amount) return WithdrawalDetails( tosAccepted = details.exchange.termsOfServiceAccepted, @@ -51,4 +66,36 @@ public class WalletApi { ) } + public suspend fun listExchanges(): List<ExchangeListItem> { + return db.listExchanges().mapNotNull { exchange -> + ExchangeListItem.fromExchangeRecord(exchange) + } + } + + public suspend fun addExchange(exchangeBaseUrl: String): ExchangeListItem { + val exchange = exchangeManager.updateFromUrl(exchangeBaseUrl) + db.put(exchange) + return ExchangeListItem.fromExchangeRecord(exchange) ?: TODO("error handling") + } + + public suspend fun getExchangeTos(exchangeBaseUrl: String): GetExchangeTosResult { + val record = db.getExchangeByBaseUrl(exchangeBaseUrl) ?: TODO("error handling") + return GetExchangeTosResult( + tos = record.termsOfServiceText ?: TODO("error handling"), + currentEtag = record.termsOfServiceLastEtag ?: TODO("error handling"), + acceptedEtag = record.termsOfServiceAcceptedEtag + ) + } + + public suspend fun setExchangeTosAccepted(exchangeBaseUrl: String, acceptedEtag: String) { + db.transaction { + val record = getExchangeByBaseUrl(exchangeBaseUrl) ?: TODO("error handling") + val updatedRecord = record.copy( + termsOfServiceAcceptedEtag = acceptedEtag, + termsOfServiceAcceptedTimestamp = Timestamp.now() + ) + put(updatedRecord) + } + } + } diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/exchange/Exchange.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/exchange/Exchange.kt index 3af430f..7a6ac7f 100644 --- a/src/commonMain/kotlin/net/taler/wallet/kotlin/exchange/Exchange.kt +++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/exchange/Exchange.kt @@ -223,3 +223,40 @@ internal class Exchange( } } + + +data class ExchangeListItem( + val exchangeBaseUrl: String, + val currency: String, + val paytoUris: List<String> +) { + companion object { + fun fromExchangeRecord(exchange: ExchangeRecord): ExchangeListItem? { + return if (exchange.details == null || exchange.wireInfo == null) null + else ExchangeListItem( + exchangeBaseUrl = exchange.baseUrl, + currency = exchange.details.currency, + paytoUris = exchange.wireInfo.accounts.map { + it.paytoUri + } + ) + } + } +} + +data class GetExchangeTosResult( + /** + * Markdown version of the current ToS. + */ + val tos: String, + + /** + * Version tag of the current ToS. + */ + val currentEtag: String, + + /** + * Version tag of the last ToS that the user has accepted, if any. + */ + val acceptedEtag: String? = null +) diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/operations/Withdraw.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/operations/Withdraw.kt index 3981012..e51e9ec 100644 --- a/src/commonMain/kotlin/net/taler/wallet/kotlin/operations/Withdraw.kt +++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/operations/Withdraw.kt @@ -35,6 +35,7 @@ import net.taler.wallet.kotlin.exchange.DenominationStatus.Unverified import net.taler.wallet.kotlin.exchange.DenominationStatus.VerifiedBad import net.taler.wallet.kotlin.exchange.DenominationStatus.VerifiedGood import net.taler.wallet.kotlin.exchange.Exchange +import net.taler.wallet.kotlin.exchange.ExchangeListItem import net.taler.wallet.kotlin.exchange.ExchangeRecord import net.taler.wallet.kotlin.exchange.ExchangeWireInfo import net.taler.wallet.kotlin.exchange.SelectedDenomination @@ -286,12 +287,6 @@ data class WithdrawalDetailsForUri( val possibleExchanges: List<ExchangeListItem> ) -data class ExchangeListItem( - val exchangeBaseUrl: String, - val currency: String, - val paytoUris: List<String> -) - data class WithdrawalDetails( /** * Did the user accept the current version of the exchange's terms of service? diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/WalletApiTest.kt b/src/commonTest/kotlin/net/taler/wallet/kotlin/WalletApiTest.kt index 9498792..7971be5 100644 --- a/src/commonTest/kotlin/net/taler/wallet/kotlin/WalletApiTest.kt +++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/WalletApiTest.kt @@ -19,12 +19,19 @@ package net.taler.wallet.kotlin import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue -@Ignore // Live-Test +@Ignore // Live-Test which fails when test environment changes or is not available class WalletApiTest { private val api = WalletApi() + private val exchangeBaseUrl = "https://exchange.test.taler.net/" + private val withdrawUri = + "taler://withdraw/bank.test.taler.net/8adabbf8-fe61-4d31-a2d1-89350b5be8fa" + private val paytoUris = listOf("payto://x-taler-bank/bank.test.taler.net/Exchange") + private val tosEtag = "0" + @Test fun testGetVersions() { api.getVersions() @@ -32,15 +39,55 @@ class WalletApiTest { @Test fun testGetWithdrawalDetails() = runCoroutine { - val detailsForUri = - api.getWithdrawalDetailsForUri("taler://withdraw/bank.test.taler.net/8adabbf8-fe61-4d31-a2d1-89350b5be8fa") + val detailsForUri = api.getWithdrawalDetailsForUri(withdrawUri) assertEquals(Amount("TESTKUDOS", 5, 0), detailsForUri.amount) - assertEquals("https://exchange.test.taler.net/", detailsForUri.defaultExchangeBaseUrl) + assertEquals(exchangeBaseUrl, detailsForUri.defaultExchangeBaseUrl) - val detailsForAmount = - api.getWithdrawalDetailsForAmount(detailsForUri.defaultExchangeBaseUrl!!, detailsForUri.amount) + val detailsForAmount = api.getWithdrawalDetailsForAmount( + detailsForUri.defaultExchangeBaseUrl!!, + detailsForUri.amount + ) assertEquals(Amount("TESTKUDOS", 5, 0), detailsForAmount.amountRaw) assertEquals(Amount("TESTKUDOS", 4, 80000000), detailsForAmount.amountEffective) + assertEquals(false, detailsForAmount.tosAccepted) + } + + @Test + fun testExchangeManagement() = runCoroutine { + // initially we have no exchanges + assertTrue(api.listExchanges().isEmpty()) + + // test exchange gets added correctly + val exchange = api.addExchange(exchangeBaseUrl) + assertEquals(exchangeBaseUrl, exchange.exchangeBaseUrl) + assertEquals("TESTKUDOS", exchange.currency) + assertEquals(paytoUris, exchange.paytoUris) + + // added exchange got added to list + val exchanges = api.listExchanges() + assertEquals(1, exchanges.size) + assertEquals(exchange, exchanges[0]) + + // ToS are not accepted + val tosResult = api.getExchangeTos(exchangeBaseUrl) + assertEquals(null, tosResult.acceptedEtag) + assertEquals(tosEtag, tosResult.currentEtag) + assertTrue(tosResult.tos.length > 100) + + // withdrawal details also show ToS as not accepted + val withdrawalDetails = + api.getWithdrawalDetailsForAmount(exchangeBaseUrl, Amount.zero("TESTKUDOS")) + assertEquals(false, withdrawalDetails.tosAccepted) + + // accept ToS + api.setExchangeTosAccepted(exchangeBaseUrl, tosResult.currentEtag) + val newTosResult = api.getExchangeTos(exchangeBaseUrl) + assertEquals(newTosResult.currentEtag, newTosResult.acceptedEtag) + + // withdrawal details now show ToS as accepted as well + val newWithdrawalDetails = + api.getWithdrawalDetailsForAmount(exchangeBaseUrl, Amount.zero("TESTKUDOS")) + assertEquals(true, newWithdrawalDetails.tosAccepted) } } |