libeufin

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

commit 303848df352f3515666f55f44b68d73315083b06
parent da2aa56eaf8ad545746eb7c8520b665980127523
Author: Antoine A <>
Date:   Mon, 11 Dec 2023 12:26:41 +0000

Reuse common unknown account error

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt | 35+++++++----------------------------
Mbank/src/main/kotlin/tech/libeufin/bank/Error.kt | 33+++++++++++++++++++++++----------
Mbank/src/main/kotlin/tech/libeufin/bank/Main.kt | 2+-
Mbank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt | 10++--------
Mbank/src/main/kotlin/tech/libeufin/bank/helpers.kt | 5+----
5 files changed, 34 insertions(+), 51 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt @@ -245,10 +245,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { ) when (db.account.delete(username)) { - AccountDeletionResult.UnknownAccount -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + AccountDeletionResult.UnknownAccount -> throw unknownAccount(username) AccountDeletionResult.BalanceNotZero -> throw conflict( "Account balance is not zero.", TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO @@ -262,10 +259,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { val req = call.receive<AccountReconfiguration>() when (patchAccount(db, ctx, req, username, isAdmin)) { AccountPatchResult.Success -> call.respond(HttpStatusCode.NoContent) - AccountPatchResult.UnknownAccount -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + AccountPatchResult.UnknownAccount -> throw unknownAccount(username) AccountPatchResult.NonAdminName -> throw conflict( "non-admin user cannot change their legal name", TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME @@ -290,10 +284,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { } when (db.account.reconfigPassword(username, req.new_password, req.old_password)) { AccountPatchAuthResult.Success -> call.respond(HttpStatusCode.NoContent) - AccountPatchAuthResult.UnknownAccount -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + AccountPatchAuthResult.UnknownAccount -> throw unknownAccount(username) AccountPatchAuthResult.OldPasswordMismatch -> throw conflict( "old password does not match", TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD @@ -323,10 +314,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { } auth(db, TokenScope.readonly, allowAdmin = true) { get("/accounts/{USERNAME}") { - val account = db.account.get(username) ?: throw notFound( - "Account '$username' not found.", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + val account = db.account.get(username) ?: throw unknownAccount(username) call.respond(account) } } @@ -370,10 +358,7 @@ private fun Routing.coreBankTransactionsApi(db: Database, ctx: BankConfig) { timestamp = Instant.now(), ) when (res) { - is BankTransactionResult.UnknownDebtor -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + is BankTransactionResult.UnknownDebtor -> throw unknownAccount(username) is BankTransactionResult.BothPartySame -> throw conflict( "Wire transfer attempted with credit and debit party being the same bank account", TalerErrorCode.BANK_SAME_ACCOUNT @@ -399,10 +384,7 @@ private fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankConfig) { ctx.checkRegionalCurrency(req.amount) val opId = UUID.randomUUID() when (db.withdrawal.create(username, opId, req.amount)) { - WithdrawalCreationResult.UnknownAccount -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + WithdrawalCreationResult.UnknownAccount -> throw unknownAccount(username) WithdrawalCreationResult.AccountIsExchange -> throw conflict( "Exchange account cannot perform withdrawal operation", TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE @@ -543,10 +525,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio validityPeriod = TAN_VALIDITY_PERIOD ) when (res) { - is CashoutCreationResult.AccountNotFound -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + is CashoutCreationResult.AccountNotFound -> throw unknownAccount(username) is CashoutCreationResult.BadConversion -> throw conflict( "Wrong currency conversion", TalerErrorCode.BANK_BAD_CONVERSION diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Error.kt b/bank/src/main/kotlin/tech/libeufin/bank/Error.kt @@ -30,7 +30,7 @@ import tech.libeufin.util.* * and that is meant to be caught by Ktor and responded to the * client. */ -class LibeufinBankException( +class LibeufinException( // Status code that Ktor will set for the response. val httpStatus: HttpStatusCode, // Error detail object, after Taler API. @@ -61,7 +61,7 @@ suspend fun ApplicationCall.err( error: TalerErrorCode ) { err( - LibeufinBankException( + LibeufinException( httpStatus = status, talerError = TalerError( code = error.code, err = error, hint = hint ) @@ -70,7 +70,7 @@ suspend fun ApplicationCall.err( } suspend fun ApplicationCall.err( - err: LibeufinBankException + err: LibeufinException ) { attributes.put(LOG_MSG, "${err.talerError.err.name} ${err.talerError.hint}") respond( @@ -85,40 +85,44 @@ fun libeufinError( hint: String?, error: TalerErrorCode, detail: String? = null -): LibeufinBankException = LibeufinBankException( +): LibeufinException = LibeufinException( httpStatus = status, talerError = TalerError( code = error.code, err = error, hint = hint, detail = detail ) ) +/* ----- HTTP error ----- */ + fun forbidden( hint: String = "No rights on the resource", error: TalerErrorCode = TalerErrorCode.END -): LibeufinBankException = libeufinError(HttpStatusCode.Forbidden, hint, error) +): LibeufinException = libeufinError(HttpStatusCode.Forbidden, hint, error) fun unauthorized( hint: String, error: TalerErrorCode = TalerErrorCode.GENERIC_UNAUTHORIZED -): LibeufinBankException = libeufinError(HttpStatusCode.Unauthorized, hint, error) +): LibeufinException = libeufinError(HttpStatusCode.Unauthorized, hint, error) -fun internalServerError(hint: String?): LibeufinBankException +fun internalServerError(hint: String?): LibeufinException = libeufinError(HttpStatusCode.InternalServerError, hint, TalerErrorCode.GENERIC_INTERNAL_INVARIANT_FAILURE) fun notFound( hint: String, error: TalerErrorCode -): LibeufinBankException = libeufinError(HttpStatusCode.NotFound, hint, error) +): LibeufinException = libeufinError(HttpStatusCode.NotFound, hint, error) fun conflict( hint: String, error: TalerErrorCode -): LibeufinBankException = libeufinError(HttpStatusCode.Conflict, hint, error) +): LibeufinException = libeufinError(HttpStatusCode.Conflict, hint, error) fun badRequest( hint: String? = null, error: TalerErrorCode = TalerErrorCode.GENERIC_JSON_INVALID, detail: String? = null -): LibeufinBankException = libeufinError(HttpStatusCode.BadRequest, hint, error, detail) +): LibeufinException = libeufinError(HttpStatusCode.BadRequest, hint, error, detail) + +/* ----- Currency checks ----- */ fun BankConfig.checkRegionalCurrency(amount: TalerAmount) { if (amount.currency != regionalCurrency) throw badRequest( @@ -132,4 +136,13 @@ fun BankConfig.checkFiatCurrency(amount: TalerAmount) { "Wrong currency: expected fiat currency $fiatCurrency got ${amount.currency}", TalerErrorCode.GENERIC_CURRENCY_MISMATCH ) +} + +/* ----- Common errors ----- */ + +fun unknownAccount(id: String): LibeufinException { + return notFound( + "Account '$id' not found", + TalerErrorCode.BANK_UNKNOWN_ACCOUNT + ) } \ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -141,7 +141,7 @@ fun Application.corebankWebApp(db: Database, ctx: BankConfig) { install(StatusPages) { exception<Exception> { call, cause -> when (cause) { - is LibeufinBankException -> call.err(cause) + is LibeufinException -> call.err(cause) is SQLException -> { when (cause.sqlState) { PSQLState.SERIALIZATION_FAILURE.state -> call.err( diff --git a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt @@ -52,10 +52,7 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { now = Instant.now() ) when (res) { - is TransferResult.UnknownExchange -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + is TransferResult.UnknownExchange -> throw unknownAccount(username) is TransferResult.NotAnExchange -> throw conflict( "$username is not an exchange account.", TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE @@ -125,10 +122,7 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { now = timestamp ) when (res) { - is AddIncomingResult.UnknownExchange -> throw notFound( - "Account '$username' not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + is AddIncomingResult.UnknownExchange -> throw unknownAccount(username) is AddIncomingResult.NotAnExchange -> throw conflict( "$username is not an exchange account.", TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt @@ -49,10 +49,7 @@ fun ApplicationCall.expectUriComponent(componentName: String) = /** Retrieve the bank account info for the selected username*/ suspend fun ApplicationCall.bankInfo(db: Database): BankInfo - = db.account.bankInfo(username) ?: throw notFound( - "Bank account for customer $username not found", - TalerErrorCode.BANK_UNKNOWN_ACCOUNT - ) + = db.account.bankInfo(username) ?: throw unknownAccount(username) // Generates a new Payto-URI with IBAN scheme. fun genIbanPaytoUri(): String = "payto://iban/${getIban()}"