commit 44f2a6c1236f6cb3f557afba572ccc5ae2381b78
parent befcf2bafe6fd9e248befd980344b8e58847fb7a
Author: MS <ms@taler.net>
Date: Mon, 2 Oct 2023 09:25:07 +0200
Implementing GET /accounts
Diffstat:
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")
+ }
+ }
+ }
+
}