[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.