Implemented reusing FinTsClient and therefore don't have to fetch basic data before all operations

This commit is contained in:
dankito 2021-04-18 01:00:35 +02:00
parent fbfcbb1d2c
commit 1b3ec8f7c1
1 changed files with 42 additions and 16 deletions

View File

@ -1,7 +1,7 @@
package net.dankito.banking.fints.rest.service package net.dankito.banking.fints.rest.service
import net.dankito.banking.bankfinder.InMemoryBankFinder import net.dankito.banking.bankfinder.InMemoryBankFinder
import net.dankito.banking.fints.FinTsClient import net.dankito.banking.fints.FinTsClientForCustomer
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
import net.dankito.banking.fints.model.* import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.response.BankResponse import net.dankito.banking.fints.response.BankResponse
@ -9,10 +9,10 @@ import net.dankito.banking.fints.response.client.AddAccountResponse
import net.dankito.banking.fints.response.client.GetTransactionsResponse import net.dankito.banking.fints.response.client.GetTransactionsResponse
import net.dankito.banking.fints.rest.model.BankAccessData import net.dankito.banking.fints.rest.model.BankAccessData
import net.dankito.banking.fints.rest.model.EnterTanContext import net.dankito.banking.fints.rest.model.EnterTanContext
import net.dankito.banking.fints.rest.model.EnteringTanRequested
import net.dankito.banking.fints.rest.model.dto.request.AccountRequestDto import net.dankito.banking.fints.rest.model.dto.request.AccountRequestDto
import net.dankito.banking.fints.rest.model.dto.request.GetAccountsTransactionsRequestDto import net.dankito.banking.fints.rest.model.dto.request.GetAccountsTransactionsRequestDto
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import javax.enterprise.context.ApplicationScoped import javax.enterprise.context.ApplicationScoped
@ -23,6 +23,8 @@ class fints4kService {
protected val bankFinder = InMemoryBankFinder() protected val bankFinder = InMemoryBankFinder()
protected val clientCache = ConcurrentHashMap<String, FinTsClientForCustomer>()
protected val tanRequests = mutableMapOf<String, EnterTanContext>() protected val tanRequests = mutableMapOf<String, EnterTanContext>()
@ -38,7 +40,7 @@ class fints4kService {
// TODO: as in most cases we really just want the account data, so just retrieve these without balances and transactions // TODO: as in most cases we really just want the account data, so just retrieve these without balances and transactions
protected fun getAccountData(bank: BankData): AddAccountResponse { protected fun getAccountData(bank: BankData): AddAccountResponse {
return getAsyncResponse { client, responseRetrieved -> return getAsyncResponse(bank) { client, responseRetrieved ->
client.addAccountAsync(AddAccountParameter(bank)) { response -> client.addAccountAsync(AddAccountParameter(bank)) { response ->
responseRetrieved(response) responseRetrieved(response)
} }
@ -53,38 +55,34 @@ class fints4kService {
return listOf(GetTransactionsResponse(BankResponse(false, errorMessage = errorMessage))) return listOf(GetTransactionsResponse(BankResponse(false, errorMessage = errorMessage)))
} }
val accountData = getAccountData(bank)
return dto.accounts.map { accountDto -> return dto.accounts.map { accountDto ->
val account = findAccount(accountData, accountDto) val account = findAccount(dto.credentials, accountDto)
return@map if (account != null) { return@map if (account != null) {
val parameter = GetTransactionsParameter(account, dto.alsoRetrieveBalance, dto.fromDate, dto.toDate, abortIfTanIsRequired = dto.abortIfTanIsRequired) val parameter = GetTransactionsParameter(account, dto.alsoRetrieveBalance, dto.fromDate, dto.toDate, abortIfTanIsRequired = dto.abortIfTanIsRequired)
getAccountTransactions(bank, parameter) getAccountTransactions(bank, parameter)
} }
else { else {
GetTransactionsResponse(BankResponse(false, errorMessage = "Account with identifier '${accountDto.identifier}' not found. Available accounts: ${accountData.bank.accounts.map { it.accountIdentifier }.joinToString(", ")}")) GetTransactionsResponse(BankResponse(false, errorMessage = "Account with identifier '${accountDto.identifier}' not found. Available accounts: " +
"${getCachedClient(dto.credentials)?.bank?.accounts?.map { it.accountIdentifier }?.joinToString(", ")}"))
} }
} }
} }
fun getAccountTransactions(bank: BankData, parameter: GetTransactionsParameter): GetTransactionsResponse { fun getAccountTransactions(bank: BankData, parameter: GetTransactionsParameter): GetTransactionsResponse {
return getAsyncResponse { client, responseRetrieved -> return getAsyncResponse(bank) { client, responseRetrieved ->
client.getTransactionsAsync(parameter, bank) { response -> client.getTransactionsAsync(parameter) { response ->
responseRetrieved(response) responseRetrieved(response)
} }
} }
} }
protected fun <T> getAsyncResponse(executeRequest: (FinTsClient, ((T) -> Unit)) -> Unit): T { protected fun <T> getAsyncResponse(bank: BankData, executeRequest: (FinTsClientForCustomer, ((T) -> Unit)) -> Unit): T {
val result = AtomicReference<T>() val result = AtomicReference<T>()
val countDownLatch = CountDownLatch(1) val countDownLatch = CountDownLatch(1)
// val client = FinTsClient(SimpleFinTsClientCallback { supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod? -> val client = getClient(bank, result, countDownLatch)
val client = FinTsClient(SimpleFinTsClientCallback({ bank, tanChallenge -> handleEnterTan(bank, tanChallenge, countDownLatch, result) }) { supportedTanMethods, suggestedTanMethod ->
suggestedTanMethod
})
executeRequest(client) { response -> executeRequest(client) { response ->
result.set(response) result.set(response)
@ -96,6 +94,23 @@ class fints4kService {
return result.get() return result.get()
} }
private fun <T> getClient(bank: BankData, result: AtomicReference<T>, countDownLatch: CountDownLatch): FinTsClientForCustomer {
val cacheKey = getCacheKey(bank.bankCode, bank.customerId)
clientCache[cacheKey]?.let {
return it
}
// val client = FinTsClient(SimpleFinTsClientCallback { supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod? ->
val client = FinTsClientForCustomer(bank, SimpleFinTsClientCallback({ bank, tanChallenge -> handleEnterTan(bank, tanChallenge, countDownLatch, result) }) { supportedTanMethods, suggestedTanMethod ->
suggestedTanMethod
})
clientCache[cacheKey] = client
return client
}
protected fun <T> handleEnterTan(bank: BankData, tanChallenge: TanChallenge, originatingRequestLatch: CountDownLatch, originatingRequestResult: AtomicReference<T>): EnterTanResult { protected fun <T> handleEnterTan(bank: BankData, tanChallenge: TanChallenge, originatingRequestLatch: CountDownLatch, originatingRequestResult: AtomicReference<T>): EnterTanResult {
val enterTanResult = AtomicReference<EnterTanResult>() val enterTanResult = AtomicReference<EnterTanResult>()
val enterTanLatch = CountDownLatch(1) val enterTanLatch = CountDownLatch(1)
@ -131,8 +146,19 @@ class fints4kService {
return Pair(bank, null) return Pair(bank, null)
} }
protected fun findAccount(allAccounts: AddAccountResponse, accountDto: AccountRequestDto): AccountData? { protected fun findAccount(credentials: BankAccessData, accountDto: AccountRequestDto): AccountData? {
return allAccounts.bank.accounts.firstOrNull { it.accountIdentifier == accountDto.identifier } return getCachedClient(credentials)?.bank?.accounts?.firstOrNull { it.accountIdentifier == accountDto.identifier }
}
private fun getCachedClient(credentials: BankAccessData): FinTsClientForCustomer? {
val cacheKey = getCacheKey(credentials.bankCode, credentials.loginName)
return clientCache[cacheKey]
}
private fun getCacheKey(bankCode: String, loginName: String): String {
return bankCode + "_" + loginName
} }
} }