gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] 02/02: Introducing account-less Access API's endpoints.


From: gnunet
Subject: [libeufin] 02/02: Introducing account-less Access API's endpoints.
Date: Tue, 04 Apr 2023 21:08:35 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

commit 4b8314699995083a48515b578ebed5da828c477f
Author: MS <ms@taler.net>
AuthorDate: Tue Apr 4 21:07:58 2023 +0200

    Introducing account-less Access API's endpoints.
---
 nexus/src/test/kotlin/SandboxAccessApiTest.kt      |  72 +++++++++++
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 144 ++++++++++++---------
 2 files changed, 158 insertions(+), 58 deletions(-)

diff --git a/nexus/src/test/kotlin/SandboxAccessApiTest.kt 
b/nexus/src/test/kotlin/SandboxAccessApiTest.kt
index 4c0232c9..ca4f2f08 100644
--- a/nexus/src/test/kotlin/SandboxAccessApiTest.kt
+++ b/nexus/src/test/kotlin/SandboxAccessApiTest.kt
@@ -5,15 +5,87 @@ import io.ktor.client.request.*
 import io.ktor.client.statement.*
 import io.ktor.http.*
 import io.ktor.server.testing.*
+import io.ktor.util.*
 import kotlinx.coroutines.runBlocking
 import org.jetbrains.exposed.sql.and
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.junit.Test
+import tech.libeufin.nexus.bankaccount.getBankAccount
 import tech.libeufin.sandbox.*
 
 class SandboxAccessApiTest {
     val mapper = ObjectMapper()
 
+    /**
+     * Testing that ..access-api/withdrawals/{wopid} and
+     * ..access-api/accounts/{account_name}/withdrawals/{wopid}
+     * are handled in the same way.
+     */
+    @Test
+    fun doubleUriStyle() {
+        // Creating one withdrawal operation.
+        withTestDatabase {
+            prepSandboxDb()
+            val wo: TalerWithdrawalEntity = transaction {
+                TalerWithdrawalEntity.new {
+                    this.amount = "TESTKUDOS:3.3"
+                    walletBankAccount = getBankAccountFromLabel("foo")
+                    selectedExchangePayto = 
"payto://iban/SANDBOXX/${BAR_USER_IBAN}"
+                    reservePub = "not used"
+                    selectionDone = true
+                }
+            }
+            testApplication {
+                application(sandboxApp)
+                // Showing withdrawal info.
+                val get_with_account = 
client.get("/demobanks/default/access-api/accounts/foo/withdrawals/${wo.wopid}")
 {
+                    expectSuccess = true
+                }
+                val get_without_account = 
client.get("/demobanks/default/access-api/withdrawals/${wo.wopid}") {
+                    expectSuccess = true
+                }
+                assert(get_without_account.bodyAsText() == 
get_with_account.bodyAsText())
+                assert(get_with_account.bodyAsText() == 
get_without_account.bodyAsText())
+                // Confirming a withdrawal.
+                val confirm_with_account = 
client.post("/demobanks/default/access-api/accounts/foo/withdrawals/${wo.wopid}/confirm")
 {
+                    expectSuccess = true
+                }
+                val confirm_without_account = 
client.post("/demobanks/default/access-api/withdrawals/${wo.wopid}/confirm") {
+                    expectSuccess = true
+                }
+                assert(confirm_with_account.status.value == 
confirm_without_account.status.value)
+                assert(confirm_with_account.bodyAsText() == 
confirm_without_account.bodyAsText())
+                // Aborting one withdrawal.
+                var wo_to_abort = transaction {
+                    TalerWithdrawalEntity.new {
+                        this.amount = "TESTKUDOS:3.3"
+                        walletBankAccount = getBankAccountFromLabel("foo")
+                        selectedExchangePayto = 
"payto://iban/SANDBOXX/${BAR_USER_IBAN}"
+                        reservePub = "not used"
+                        selectionDone = true
+                    }
+                }
+                val abort_with_account = 
client.post("/demobanks/default/access-api/accounts/foo/withdrawals/${wo_to_abort.wopid}/abort")
 {
+                    expectSuccess = true
+                }
+                wo_to_abort = transaction {
+                    TalerWithdrawalEntity.new {
+                        this.amount = "TESTKUDOS:3.3"
+                        walletBankAccount = getBankAccountFromLabel("foo")
+                        selectedExchangePayto = 
"payto://iban/SANDBOXX/${BAR_USER_IBAN}"
+                        reservePub = "not used"
+                        selectionDone = true
+                    }
+                }
+                val abort_without_account = 
client.post("/demobanks/default/access-api/withdrawals/${wo_to_abort.wopid}/abort")
 {
+                    expectSuccess = true
+                }
+                assert(abort_with_account.status.value == 
abort_without_account.status.value)
+                // Not checking the content as they abort two different 
operations.
+            }
+        }
+    }
+
     // Move funds between accounts.
     @Test
     fun wireTransfer() {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 9c8f8121..4aedf41c 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -487,6 +487,73 @@ fun setJsonHandler(ctx: ObjectMapper) {
     )
 }
 
+private suspend fun getWithdrawal(call: ApplicationCall) {
+    val op = getWithdrawalOperation(call.expectUriComponent("withdrawal_id"))
+    if (!op.selectionDone && op.reservePub != null) throw internalServerError(
+        "Unselected withdrawal has a reserve public key",
+        LibeufinErrorCode.LIBEUFIN_EC_INCONSISTENT_STATE
+    )
+    call.respond(object {
+        val amount = op.amount
+        val aborted = op.aborted
+        val confirmation_done = op.confirmationDone
+        val selection_done = op.selectionDone
+        val selected_reserve_pub = op.reservePub
+        val selected_exchange_account = op.selectedExchangePayto
+    })
+}
+
+private suspend fun confirmWithdrawal(call: ApplicationCall) {
+    val withdrawalId = call.expectUriComponent("withdrawal_id")
+    transaction {
+        val wo = getWithdrawalOperation(withdrawalId)
+        if (wo.aborted) throw SandboxError(
+            HttpStatusCode.Conflict,
+            "Cannot confirm an aborted withdrawal."
+        )
+        if (!wo.selectionDone) throw SandboxError(
+            HttpStatusCode.UnprocessableEntity,
+            "Cannot confirm a unselected withdrawal: " +
+                    "specify exchange and reserve public key via Integration 
API first."
+        )
+        /**
+         * The wallet chose not to select any exchange, use the default.
+         */
+        val demobank = ensureDemobank(call)
+        if (wo.selectedExchangePayto == null) {
+            wo.selectedExchangePayto = demobank.config.suggestedExchangePayto
+        }
+        val exchangeBankAccount = getBankAccountFromPayto(
+            wo.selectedExchangePayto ?: throw internalServerError(
+                "Cannot withdraw without an exchange."
+            )
+        )
+        if (!wo.confirmationDone) {
+            wireTransfer(
+                debitAccount = wo.walletBankAccount,
+                creditAccount = exchangeBankAccount,
+                amount = wo.amount,
+                subject = wo.reservePub ?: throw internalServerError(
+                    "Cannot transfer funds without reserve public key."
+                ),
+                // provide the currency.
+                demobank = ensureDemobank(call)
+            )
+            wo.confirmationDone = true
+        }
+        wo.confirmationDone
+    }
+    call.respond(object {})
+}
+
+private suspend fun abortWithdrawal(call: ApplicationCall) {
+    val withdrawalId = call.expectUriComponent("withdrawal_id")
+    val operation = getWithdrawalOperation(withdrawalId)
+    if (operation.confirmationDone) throw conflict("Cannot abort paid 
withdrawal.")
+    transaction { operation.aborted = true }
+    call.respond(object {})
+}
+
 val sandboxApp: Application.() -> Unit = {
     install(CallLogging) {
         this.level = Level.DEBUG
@@ -1283,19 +1350,12 @@ val sandboxApp: Application.() -> Unit = {
                 }
                 // Information about one withdrawal.
                 get("/accounts/{account_name}/withdrawals/{withdrawal_id}") {
-                    val op = 
getWithdrawalOperation(call.expectUriComponent("withdrawal_id"))
-                    if (!op.selectionDone && op.reservePub != null) throw 
internalServerError(
-                        "Unselected withdrawal has a reserve public key",
-                        LibeufinErrorCode.LIBEUFIN_EC_INCONSISTENT_STATE
-                    )
-                    call.respond(object {
-                        val amount = op.amount
-                        val aborted = op.aborted
-                        val confirmation_done = op.confirmationDone
-                        val selection_done = op.selectionDone
-                        val selected_reserve_pub = op.reservePub
-                        val selected_exchange_account = 
op.selectedExchangePayto
-                    })
+                    getWithdrawal(call)
+                    return@get
+                }
+                // account-less style:
+                get("/withdrawals/{withdrawal_id}") {
+                    getWithdrawal(call)
                     return@get
                 }
                 // Create a new withdrawal operation.
@@ -1372,54 +1432,22 @@ val sandboxApp: Application.() -> Unit = {
                 }
                 // Confirm a withdrawal: no basic auth, because the ID should 
be unguessable.
                 
post("/accounts/{account_name}/withdrawals/{withdrawal_id}/confirm") {
-                    val withdrawalId = call.expectUriComponent("withdrawal_id")
-                    transaction {
-                        val wo = getWithdrawalOperation(withdrawalId)
-                        if (wo.aborted) throw SandboxError(
-                            HttpStatusCode.Conflict,
-                            "Cannot confirm an aborted withdrawal."
-                        )
-                        if (!wo.selectionDone) throw SandboxError(
-                            HttpStatusCode.UnprocessableEntity,
-                            "Cannot confirm a unselected withdrawal: " +
-                                    "specify exchange and reserve public key 
via Integration API first."
-                        )
-                        /**
-                         * The wallet chose not to select any exchange, use 
the default.
-                         */
-                        val demobank = ensureDemobank(call)
-                        if (wo.selectedExchangePayto == null) {
-                            wo.selectedExchangePayto = 
demobank.config.suggestedExchangePayto
-                        }
-                        val exchangeBankAccount = getBankAccountFromPayto(
-                            wo.selectedExchangePayto ?: throw 
internalServerError(
-                                "Cannot withdraw without an exchange."
-                            )
-                        )
-                        if (!wo.confirmationDone) {
-                            wireTransfer(
-                                debitAccount = wo.walletBankAccount,
-                                creditAccount = exchangeBankAccount,
-                                amount = wo.amount,
-                                subject = wo.reservePub ?: throw 
internalServerError(
-                                    "Cannot transfer funds without reserve 
public key."
-                                ),
-                                // provide the currency.
-                                demobank = ensureDemobank(call)
-                            )
-                            wo.confirmationDone = true
-                        }
-                        wo.confirmationDone
-                    }
-                    call.respond(object {})
+                    confirmWithdrawal(call)
                     return@post
                 }
+                // account-less style:
+                post("/withdrawals/{withdrawal_id}/confirm") {
+                    confirmWithdrawal(call)
+                    return@post
+                }
+                // Aborting withdrawals:
                 
post("/accounts/{account_name}/withdrawals/{withdrawal_id}/abort") {
-                    val withdrawalId = call.expectUriComponent("withdrawal_id")
-                    val operation = getWithdrawalOperation(withdrawalId)
-                    if (operation.confirmationDone) throw conflict("Cannot 
abort paid withdrawal.")
-                    transaction { operation.aborted = true }
-                    call.respond(object {})
+                    abortWithdrawal(call)
+                    return@post
+                }
+                // account-less style:
+                post("/withdrawals/{withdrawal_id}/abort") {
+                    abortWithdrawal(call)
                     return@post
                 }
                 // Bank account basic information.

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]