gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] 02/02: implement per-account transaction listing in sandbox


From: gnunet
Subject: [libeufin] 02/02: implement per-account transaction listing in sandbox
Date: Wed, 13 Jan 2021 22:13:54 +0100

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

dold pushed a commit to branch master
in repository libeufin.

commit 2cd810f110e64c7024ce21866f1c705e1c2f032c
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Jan 13 22:13:47 2021 +0100

    implement per-account transaction listing in sandbox
---
 cli/bin/libeufin-cli                               |  65 +++++++++-
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |   4 +-
 .../main/kotlin/tech/libeufin/sandbox/Helpers.kt   |  11 ++
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  |   3 +-
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 137 ++++++++++++++++++++-
 5 files changed, 210 insertions(+), 10 deletions(-)

diff --git a/cli/bin/libeufin-cli b/cli/bin/libeufin-cli
index ed79436..803a60a 100755
--- a/cli/bin/libeufin-cli
+++ b/cli/bin/libeufin-cli
@@ -334,9 +334,9 @@ def show(obj):
     print(resp.content.decode("utf-8"))
 
 @accounts.command(help="prepare payment debiting 'account-name'")
-@click.option("--credit-iban", help="IBAN that will receive the payment", 
required=True)
-@click.option("--credit-bic", help="BIC that will receive the payment", 
required=False)
-@click.option("--credit-name", help="Legal name that will receive the 
payment", required=True)
+@click.option("--creditor-iban", help="IBAN that will receive the payment", 
required=True)
+@click.option("--creditor-bic", help="BIC that will receive the payment", 
required=False)
+@click.option("--creditor-name", help="Legal name that will receive the 
payment", required=True)
 @click.option("--payment-amount", help="Amount to be paid (<currency>:X.Y)", 
required=True)
 @click.option("--payment-subject", help="Subject of this payment", 
required=True)
 @click.argument("account-name")
@@ -448,18 +448,30 @@ def new_facade(obj, facade_name, connection_name, 
account_name):
     print(resp.content.decode("utf-8"))
 
 
-
 @sandbox.group("ebicshost", help="manage EBICS hosts")
 @click.pass_context
 def sandbox_ebicshost(ctx):
     pass
 
+@sandbox.command("check", help="check sandbox status")
+@click.pass_obj
+def check_sandbox_status(obj):
+    sandbox_base_url = obj.require_sandbox_base_url()
+    url = urljoin(sandbox_base_url, "/config")
+    try:
+        resp = get(url)
+    except Exception:
+        print("Could not reach sandbox")
+        exit(1)
+    print(resp.content.decode("utf-8"))
+
+
 @sandbox_ebicshost.command("create", help="Create an EBICS host")
 @click.option("--host-id", help="EBICS host ID", required=True, prompt=True)
 @click.pass_obj
 def make_ebics_host(obj, host_id):
     sandbox_base_url = obj.require_sandbox_base_url()
-    url = urljoin(sandbox_base_url, "/admin/ebics/host")
+    url = urljoin(sandbox_base_url, "/admin/ebics/hosts")
     try:
         resp = post(url, json=dict(hostID=host_id, ebicsVersion="2.5"))
     except Exception:
@@ -517,6 +529,7 @@ def sandbox_ebicsbankaccount(ctx):
     pass
 
 @sandbox_ebicsbankaccount.command("create", help="Create a bank account 
associated to an EBICS subscriber.")
+@click.option("--currency", help="currency", prompt=True)
 @click.option("--iban", help="IBAN", required=True)
 @click.option("--bic", help="BIC", required=True)
 @click.option("--person-name", help="bank account owner name", required=True)
@@ -525,11 +538,12 @@ def sandbox_ebicsbankaccount(ctx):
 @click.option("--ebics-host-id", help="host ID of the Ebics subscriber", 
required=True)
 @click.option("--ebics-partner-id", help="partner ID of the Ebics subscriber", 
required=True)
 @click.pass_obj
-def associate_bank_account(obj, iban, bic, person_name, account_name,
+def associate_bank_account(obj, currency, iban, bic, person_name, account_name,
                            ebics_user_id, ebics_host_id, ebics_partner_id):
     sandbox_base_url = obj.require_sandbox_base_url()
     url = urljoin(sandbox_base_url, "/admin/ebics/bank-accounts")
     body = dict(
+        currency=currency,
         subscriber=dict(userID=ebics_user_id, partnerID=ebics_partner_id, 
hostID=ebics_host_id),
         iban=iban, bic=bic, name=person_name, label=account_name
     )
@@ -546,6 +560,45 @@ def associate_bank_account(obj, iban, bic, person_name, 
account_name,
 def sandbox_bankaccount(ctx):
     pass
 
+@sandbox_bankaccount.command("list", help="List accounts")
+@click.pass_obj
+def bankaccount_list(obj):
+    sandbox_base_url = obj.require_sandbox_base_url()
+    url = urljoin(sandbox_base_url, f"/admin/bank-accounts")
+    try:
+        resp = get(url)
+    except Exception:
+        print("Could not reach sandbox")
+        exit(1)
+    print(resp.content.decode("utf-8"))
+
+@sandbox_bankaccount.command("transactions", help="List transactions")
+@click.argument("account-label")
+@click.pass_obj
+def bankaccount_list(obj, account_label):
+    sandbox_base_url = obj.require_sandbox_base_url()
+    url = urljoin(sandbox_base_url, 
f"/admin/bank-accounts/{account_label}/transactions")
+    try:
+        resp = get(url)
+    except Exception:
+        print("Could not reach sandbox")
+        exit(1)
+    print(resp.content.decode("utf-8"))
+
+@sandbox_bankaccount.command("generate-transactions", help="Generate test 
transactions")
+@click.argument("account-label")
+@click.pass_obj
+def bankaccount_generate_transactions(obj, account_label):
+    sandbox_base_url = obj.require_sandbox_base_url()
+    url = urljoin(sandbox_base_url, 
f"/admin/bank-accounts/{account_label}/generate-transactions")
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach sandbox")
+        exit(1)
+    print(resp.content.decode("utf-8"))
+
+
 @sandbox_bankaccount.command(help="book a payment in the sandbox")
 @click.option("--creditor-iban", help="IBAN receiving the payment", 
prompt=True)
 @click.option("--creditor-bic", help="BIC receiving the payment", prompt=True)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 82124f9..9ea9a0e 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -273,8 +273,9 @@ object BankAccountsTable : IntIdTable() {
     val iban = text("iban")
     val bic = text("bic")
     val name = text("name")
-    val label = text("label")
+    val label = text("label").uniqueIndex("accountLabelIndex")
     val subscriber = reference("subscriber", EbicsSubscribersTable)
+    val currency = text("currency")
 }
 
 class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
@@ -285,6 +286,7 @@ class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
     var name by BankAccountsTable.name
     var label by BankAccountsTable.label
     var subscriber by EbicsSubscriberEntity referencedOn 
BankAccountsTable.subscriber
+    var currency by BankAccountsTable.currency
 }
 
 object BankAccountStatementsTable : IntIdTable() {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 0ea1927..bc9c908 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -53,6 +53,17 @@ fun getBankAccountFromIban(iban: String): BankAccountEntity {
     )
 }
 
+fun getBankAccountFromLabel(label: String): BankAccountEntity {
+    return transaction {
+        BankAccountEntity.find(
+            BankAccountsTable.label eq label
+        )
+    }.firstOrNull() ?: throw SandboxError(
+        HttpStatusCode.NotFound,
+        "Did not find a bank account for label ${label}"
+    )
+}
+
 fun getBankAccountFromSubscriber(subscriber: EbicsSubscriberEntity): 
BankAccountEntity {
     return transaction {
         BankAccountEntity.find(BankAccountsTable.subscriber eq subscriber.id)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index ad46123..69804e7 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -69,7 +69,8 @@ data class BankAccountRequest(
     val iban: String,
     val bic: String,
     val name: String,
-    val label: String
+    val label: String,
+    val currency: String
 )
 
 data class DateRange(
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 7152169..404ac70 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -77,6 +77,7 @@ import 
tech.libeufin.sandbox.BankAccountTransactionsTable.direction
 import tech.libeufin.util.*
 import tech.libeufin.util.ebics_h004.EbicsResponse
 import tech.libeufin.util.ebics_h004.EbicsTypes
+import java.util.*
 import kotlin.random.Random
 
 const val DEFAULT_DB_CONNECTION = "jdbc:sqlite:/tmp/libeufin-sandbox.sqlite3"
@@ -86,6 +87,7 @@ class BadInputData(inputData: String?) : Exception("Customer 
provided invalid in
 class UnacceptableFractional(badNumber: BigDecimal) : Exception(
     "Unacceptable fractional part ${badNumber}"
 )
+
 lateinit var LOGGER: Logger
 
 data class SandboxError(val statusCode: HttpStatusCode, val reason: String) : 
Exception()
@@ -102,6 +104,7 @@ class ResetTables : CliktCommand("Drop all the tables from 
the database") {
             helpFormatter = CliktHelpFormatter(showDefaultValues = true)
         }
     }
+
     private val dbConnString by option().default(DEFAULT_DB_CONNECTION)
     override fun run() {
         execThrowableOrTerminate {
@@ -117,6 +120,7 @@ class Serve : CliktCommand("Run sandbox HTTP server") {
             helpFormatter = CliktHelpFormatter(showDefaultValues = true)
         }
     }
+
     private val dbConnString by option().default(DEFAULT_DB_CONNECTION)
     private val logLevel by option()
     private val port by option().int().default(5000)
@@ -160,6 +164,17 @@ data class EbicsHostPublicInfo(
     val authenticationPublicKey: RSAPublicKey
 )
 
+data class BankAccountInfo(
+    val label: String,
+    val name: String,
+    val iban: String,
+    val bic: String
+)
+
+data class BankAccountsListReponse(
+    val accounts: List<BankAccountInfo>
+)
+
 inline fun <reified T> Document.toObject(): T {
     val jc = JAXBContext.newInstance(T::class.java)
     val m = jc.createUnmarshaller()
@@ -171,6 +186,12 @@ fun BigDecimal.signToString(): String {
     // minus sign is added by default already.
 }
 
+fun ensureNonNull(param: String?): String {
+    return param ?: throw SandboxError(
+        HttpStatusCode.BadRequest, "Bad ID given: $param"
+    )
+}
+
 fun main(args: Array<String>) {
     SandboxCommand()
         .subcommands(Serve(), ResetTables())
@@ -259,6 +280,14 @@ fun serverMain(dbName: String, port: Int) {
             get("/") {
                 call.respondText("Hello, this is Sandbox\n", 
ContentType.Text.Plain)
             }
+            get("/config") {
+                call.respond(object {
+                    val name = "libeufin-sandbox"
+
+                    // FIXME: use actual version here!
+                    val version = "0.0.0-dev.0"
+                })
+            }
             // only reason for a post is to hide the iban (to some degree.)
             post("/admin/payments/camt") {
                 val body = call.receive<CamtParams>()
@@ -268,6 +297,7 @@ fun serverMain(dbName: String, port: Int) {
                 call.respondText(camt53, ContentType.Text.Xml, 
HttpStatusCode.OK)
                 return@post
             }
+            // FIXME:  This returns *all* payments for all accounts.  Is that 
really useful/required?
             get("/admin/payments") {
                 val ret = PaymentsResponse()
                 transaction {
@@ -342,11 +372,114 @@ fun serverMain(dbName: String, port: Int) {
                         bic = body.bic
                         name = body.name
                         label = body.label
+                        currency = body.currency.toUpperCase(Locale.ROOT)
                     }
                 }
                 call.respondText("Bank account created")
                 return@post
             }
+            get("/admin/bank-accounts") {
+                val accounts = mutableListOf<BankAccountInfo>()
+                val accountsResp = BankAccountsListReponse(
+                    accounts = accounts
+                )
+                transaction {
+                    BankAccountEntity.all().forEach {
+                        accounts.add(
+                            BankAccountInfo(
+                                label = it.label,
+                                name = it.name,
+                                bic = it.bic,
+                                iban = it.iban
+                            )
+                        )
+                    }
+                }
+                call.respond(accounts)
+            }
+            get("/admin/bank-accounts/{label}/transactions") {
+                val ret = PaymentsResponse()
+                transaction {
+                    val accountLabel = ensureNonNull(call.parameters["label"])
+                    transaction {
+                        val account = getBankAccountFromLabel(accountLabel)
+                        BankAccountTransactionsTable.select { 
BankAccountTransactionsTable.account eq account.id }
+                            .forEach {
+                                ret.payments.add(
+                                    RawPayment(
+                                        creditorIban = it[creditorIban],
+                                        debitorIban = it[debitorIban],
+                                        subject = 
it[BankAccountTransactionsTable.subject],
+                                        date = it[date].toHttpDateString(),
+                                        amount = it[amount],
+                                        creditorBic = it[creditorBic],
+                                        creditorName = it[creditorName],
+                                        debitorBic = it[debitorBic],
+                                        debitorName = it[debitorName],
+                                        currency = it[currency],
+                                        direction = it[direction]
+                                    )
+                                )
+                            }
+                    }
+                }
+                call.respond(
+                    object {
+                        val payments = ret
+                    }
+                )
+            }
+            post("/admin/bank-accounts/{label}/generate-transactions") {
+                transaction {
+                    val accountLabel = ensureNonNull(call.parameters["label"])
+                    val account = getBankAccountFromLabel(accountLabel)
+
+                    run {
+                        val random = Random.nextLong()
+                        val amount = Random.nextLong(5, 25)
+
+                        BankAccountTransactionsTable.insert {
+                            it[creditorIban] = account.iban
+                            it[creditorBic] = account.bic
+                            it[creditorName] = account.name
+                            it[debitorIban] = "DE64500105178797276788"
+                            it[debitorBic] = "FOBADEM001"
+                            it[debitorName] = "Max Mustermann"
+                            it[subject] = "sample transaction $random"
+                            it[BankAccountTransactionsTable.amount] = 
amount.toString()
+                            it[currency] = account.currency
+                            it[date] = Instant.now().toEpochMilli()
+                            it[pmtInfId] = random.toString()
+                            it[msgId] = random.toString()
+                            it[BankAccountTransactionsTable.account] = 
account.id
+                            it[direction] = "CRDT"
+                        }
+                    }
+
+                    run {
+                        val random = Random.nextLong()
+                        val amount = Random.nextLong(5, 25)
+
+                        BankAccountTransactionsTable.insert {
+                            it[debitorIban] = account.iban
+                            it[debitorBic] = account.bic
+                            it[debitorName] = account.name
+                            it[creditorIban] = "DE64500105178797276788"
+                            it[creditorBic] = "FOBADEM001"
+                            it[creditorName] = "Max Mustermann"
+                            it[subject] = "sample transaction $random"
+                            it[BankAccountTransactionsTable.amount] = 
amount.toString()
+                            it[currency] = account.currency
+                            it[date] = Instant.now().toEpochMilli()
+                            it[pmtInfId] = random.toString()
+                            it[msgId] = random.toString()
+                            it[BankAccountTransactionsTable.account] = 
account.id
+                            it[direction] = "DBIT"
+                        }
+                    }
+                }
+                call.respond(object {})
+            }
             /**
              * Creates a new Ebics subscriber.
              */
@@ -372,7 +505,7 @@ fun serverMain(dbName: String, port: Int) {
              * Shows all the Ebics subscribers' details.
              */
             get("/admin/ebics/subscribers") {
-                var ret = AdminGetSubscribers()
+                val ret = AdminGetSubscribers()
                 transaction {
                     EbicsSubscriberEntity.all().forEach {
                         ret.subscribers.add(
@@ -431,4 +564,4 @@ fun serverMain(dbName: String, port: Int) {
     }
     LOGGER.info("Up and running")
     server.start(wait = true)
-}
+}
\ No newline at end of file

-- 
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]