libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 44f2a6c1236f6cb3f557afba572ccc5ae2381b78
parent befcf2bafe6fd9e248befd980344b8e58847fb7a
Author: MS <ms@taler.net>
Date:   Mon,  2 Oct 2023 09:25:07 +0200

Implementing GET /accounts

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt | 8++++++++
Mbank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt | 24++++++++++++++++++++++++
Mbank/src/main/kotlin/tech/libeufin/bank/Database.kt | 4+++-
Mbank/src/test/kotlin/LibeuFinApiTest.kt | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt @@ -380,6 +380,14 @@ data class AccountMinimalData( ) /** + * Response type of GET /accounts. + */ +@Serializable +data class ListBankAccountsResponse( + val accounts: MutableList<AccountMinimalData> = mutableListOf() +) + +/** * GET /accounts/$USERNAME response. */ @Serializable diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt @@ -199,6 +199,30 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: BankApplicationContext) { return@post } + get("/accounts") { + val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw unauthorized() + if (c.login != "admin") throw forbidden("Only admin allowed.") + // Get optional param. + val maybeFilter: String? = call.request.queryParameters["filter_name"] + logger.debug("Filtering on '${maybeFilter}'") + val queryParam = if (maybeFilter != null) { + "%${maybeFilter}%" + } else "%" + val dbRes = db.accountsGetForAdmin(queryParam) + if (dbRes.isEmpty()) { + call.respond(HttpStatusCode.NoContent) + return@get + } + call.respond( + ListBankAccountsResponse().apply { + dbRes.forEach { element -> + this.accounts.add(element) + } + } + ) + return@get + } + get("/accounts/{USERNAME}") { val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw unauthorized("Login failed") val resourceName = call.maybeUriComponent("USERNAME") ?: throw badRequest( diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt @@ -362,7 +362,9 @@ class Database(private val dbConfig: String, private val bankCurrency: String) { /** * Gets a minimal set of account data, as outlined in the GET /accounts - * endpoint. + * endpoint. The nameFilter parameter will be passed AS IS to the SQL + * LIKE operator. If it's null, it defaults to the "%" wildcard, meaning + * that it returns ALL the existing accounts. */ fun accountsGetForAdmin(nameFilter: String = "%"): List<AccountMinimalData> { reconnect() diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt b/bank/src/test/kotlin/LibeuFinApiTest.kt @@ -516,4 +516,66 @@ class LibeuFinApiTest { assert(resp.status == HttpStatusCode.Created) } } + + /** + * Tests the GET /accounts endpoint. + */ + @Test + fun getAccountsList() { + val db = initDb() + val ctx = getTestContext() + val adminCustomer = Customer( + "admin", + CryptoUtil.hashpw("pass"), + "CFO" + ) + assert(db.customerCreate(adminCustomer) != null) + testApplication { + application { + corebankWebApp(db, ctx) + } + // No users registered, expect no data. + client.get("/accounts") { + basicAuth("admin", "pass") + expectSuccess = true + }.apply { + assert(this.status == HttpStatusCode.NoContent) + } + // foo account + db.customerCreate(customerFoo).apply { + assert(this != null) + db.bankAccountCreate(genBankAccount(this!!)) != null + } + // bar account + db.customerCreate(customerBar).apply { + assert(this != null) + db.bankAccountCreate(genBankAccount(this!!)) != null + } + // Two users registered, requesting all of them. + client.get("/accounts") { + basicAuth("admin", "pass") + expectSuccess = true + }.apply { + println(this.bodyAsText()) + assert(this.status == HttpStatusCode.OK) + val obj = Json.decodeFromString<ListBankAccountsResponse>(this.bodyAsText()) + assert(obj.accounts.size == 2) + // Order unreliable, just checking they're different. + assert(obj.accounts[0].username != obj.accounts[1].username) + } + // Filtering on bar. + client.get("/accounts?filter_name=ar") { + basicAuth("admin", "pass") + expectSuccess = true + }.apply { + assert(this.status == HttpStatusCode.OK) + val obj = Json.decodeFromString<ListBankAccountsResponse>(this.bodyAsText()) + assert(obj.accounts.size == 1) { + println("Wrong size of filtered query: ${obj.accounts.size}") + } + assert(obj.accounts[0].username == "bar") + } + } + } + }