Added FinTsClientConfiguration to centralize configuration of FinTsClient

This commit is contained in:
dankito 2024-08-20 10:43:52 +02:00
parent 8f079d1404
commit 6f5eabf144
7 changed files with 85 additions and 62 deletions

View File

@ -7,6 +7,7 @@ import net.dankito.banking.client.model.response.ErrorCode
import net.dankito.banking.client.model.response.GetAccountDataResponse
import net.dankito.banking.client.model.response.TransferMoneyResponse
import net.dankito.banking.fints.callback.FinTsClientCallback
import net.dankito.banking.fints.config.FinTsClientConfiguration
import net.dankito.banking.fints.mapper.FinTsModelMapper
import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.response.client.FinTsClientResponse
@ -14,16 +15,11 @@ import net.dankito.banking.fints.response.client.GetAccountInfoResponse
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
import net.dankito.banking.fints.response.segments.AccountType
import net.dankito.banking.fints.util.BicFinder
import net.dankito.banking.fints.util.FinTsServerAddressFinder
import net.dankito.banking.fints.webclient.IWebClient
import kotlin.jvm.JvmOverloads
open class FinTsClient @JvmOverloads constructor(
open var callback: FinTsClientCallback,
protected open val jobExecutor: FinTsJobExecutor = FinTsJobExecutor(),
protected open val finTsServerAddressFinder: FinTsServerAddressFinder = FinTsServerAddressFinder(),
protected open val product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically
open class FinTsClient(
protected open val config: FinTsClientConfiguration,
open var callback: FinTsClientCallback
) {
companion object { // TODO: use the English names
@ -31,7 +27,7 @@ open class FinTsClient @JvmOverloads constructor(
}
constructor(callback: FinTsClientCallback, webClient: IWebClient) : this(callback, FinTsJobExecutor(RequestExecutor(webClient = webClient))) // Swift does not support default parameter values -> create constructor overloads
constructor(callback: FinTsClientCallback) : this(FinTsClientConfiguration(), callback)
protected open val mapper = FinTsModelMapper()
@ -44,7 +40,7 @@ open class FinTsClient @JvmOverloads constructor(
}
open suspend fun getAccountDataAsync(param: GetAccountDataParameter): GetAccountDataResponse {
val finTsServerAddress = finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
val finTsServerAddress = config.finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
if (finTsServerAddress.isNullOrBlank()) {
return GetAccountDataResponse(ErrorCode.BankDoesNotSupportFinTs3, "Either bank does not support FinTS 3.0 or we don't know its FinTS server address", null, listOf())
}
@ -88,9 +84,9 @@ open class FinTsClient @JvmOverloads constructor(
}
protected open suspend fun getAccountData(param: GetAccountDataParameter, bank: BankData, account: AccountData): GetAccountTransactionsResponse {
val context = JobContext(JobContextType.GetTransactions, this.callback, product, bank, account)
val context = JobContext(JobContextType.GetTransactions, this.callback, config, bank, account)
return jobExecutor.getTransactionsAsync(context, mapper.toGetAccountTransactionsParameter(param, bank, account))
return config.jobExecutor.getTransactionsAsync(context, mapper.toGetAccountTransactionsParameter(param, bank, account))
}
@ -100,7 +96,7 @@ open class FinTsClient @JvmOverloads constructor(
}
open suspend fun transferMoneyAsync(param: TransferMoneyParameter): TransferMoneyResponse {
val finTsServerAddress = finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
val finTsServerAddress = config.finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
if (finTsServerAddress.isNullOrBlank()) {
return TransferMoneyResponse(ErrorCode.BankDoesNotSupportFinTs3, "Either bank does not FinTS 3.0 or we don't know its FinTS server address", listOf(), null)
}
@ -146,9 +142,9 @@ open class FinTsClient @JvmOverloads constructor(
accountToUse = selectedAccount
}
val context = JobContext(JobContextType.TransferMoney, this.callback, product, bank, accountToUse)
val context = JobContext(JobContextType.TransferMoney, this.callback, config, bank, accountToUse)
val response = jobExecutor.transferMoneyAsync(context, BankTransferData(param.recipientName, param.recipientAccountIdentifier, recipientBankIdentifier,
val response = config.jobExecutor.transferMoneyAsync(context, BankTransferData(param.recipientName, param.recipientAccountIdentifier, recipientBankIdentifier,
param.amount, param.reference, param.instantPayment))
return TransferMoneyResponse(mapper.mapErrorCode(response), mapper.mapErrorMessages(response), mapper.mergeMessageLog(previousJobResponse, response), bank)
@ -173,11 +169,11 @@ open class FinTsClient @JvmOverloads constructor(
// return GetAccountInfoResponse(it)
}
val context = JobContext(JobContextType.GetAccountInfo, this.callback, product, bank)
val context = JobContext(JobContextType.GetAccountInfo, this.callback, config, bank)
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
val newUserInfoResponse = jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, param.preferredTanMethods, param.preferredTanMedium)
val newUserInfoResponse = config.jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, param.preferredTanMethods, param.preferredTanMedium)
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
return GetAccountInfoResponse(context, newUserInfoResponse)
@ -186,7 +182,7 @@ open class FinTsClient @JvmOverloads constructor(
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
strong customer authorization TAN media is required */
val getAccountsResponse = jobExecutor.getAccounts(context)
val getAccountsResponse = config.jobExecutor.getAccounts(context)
return GetAccountInfoResponse(context, getAccountsResponse)
}

View File

@ -4,6 +4,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.datetime.*
import net.dankito.banking.fints.callback.FinTsClientCallback
import net.dankito.banking.fints.config.FinTsClientConfiguration
import net.dankito.banking.fints.extensions.minusDays
import net.dankito.banking.fints.extensions.todayAtEuropeBerlin
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.*
@ -18,14 +19,11 @@ import net.dankito.banking.fints.webclient.IWebClient
* [addAccountAsync] gets user's TAN methods, user's TAN media, user's bank accounts and may even current balance and account transactions of last 90 days.
*/
open class FinTsClientDeprecated(
open var callback: FinTsClientCallback,
protected open val jobExecutor: FinTsJobExecutor = FinTsJobExecutor(),
protected open val product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically
protected val config: FinTsClientConfiguration,
open var callback: FinTsClientCallback
) {
constructor(callback: FinTsClientCallback) : this(callback, FinTsJobExecutor()) // Swift does not support default parameter values -> create constructor overloads
constructor(callback: FinTsClientCallback, webClient: IWebClient) : this(callback, FinTsJobExecutor(RequestExecutor(webClient = webClient)))
constructor(callback: FinTsClientCallback) : this(FinTsClientConfiguration(), callback)
/**
@ -48,20 +46,20 @@ open class FinTsClientDeprecated(
* On success [bank] parameter is updated afterwards.
*/
open suspend fun getAnonymousBankInfo(bank: BankData): FinTsClientResponse {
val context = JobContext(JobContextType.AnonymousBankInfo, this.callback, product, bank)
val context = JobContext(JobContextType.AnonymousBankInfo, this.callback, config, bank)
val response = jobExecutor.getAnonymousBankInfo(context)
val response = config.jobExecutor.getAnonymousBankInfo(context)
return FinTsClientResponse(context, response)
}
open suspend fun addAccountAsync(parameter: AddAccountParameter): AddAccountResponse {
val bank = parameter.bank
val context = JobContext(JobContextType.AddAccount, this.callback, product, bank)
val context = JobContext(JobContextType.AddAccount, this.callback, config, bank)
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
val newUserInfoResponse = jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, parameter.preferredTanMethods, parameter.preferredTanMedium)
val newUserInfoResponse = config.jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, parameter.preferredTanMethods, parameter.preferredTanMedium)
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
return AddAccountResponse(context, newUserInfoResponse)
@ -77,7 +75,7 @@ open class FinTsClientDeprecated(
/* Third dialog: Now we can initialize our first dialog with strong customer authorization. Use it to get UPD and customer's accounts */
val getAccountsResponse = jobExecutor.getAccounts(context)
val getAccountsResponse = config.jobExecutor.getAccounts(context)
if (getAccountsResponse.successful == false) {
return AddAccountResponse(context, getAccountsResponse)
@ -138,33 +136,33 @@ open class FinTsClientDeprecated(
open suspend fun getAccountTransactionsAsync(parameter: GetAccountTransactionsParameter): GetAccountTransactionsResponse {
val context = JobContext(JobContextType.GetTransactions, this.callback, product, parameter.bank, parameter.account)
val context = JobContext(JobContextType.GetTransactions, this.callback, config, parameter.bank, parameter.account)
return jobExecutor.getTransactionsAsync(context, parameter)
return config.jobExecutor.getTransactionsAsync(context, parameter)
}
open suspend fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): GetTanMediaListResponse {
val context = JobContext(JobContextType.GetTanMedia, this.callback, product, bank)
val context = JobContext(JobContextType.GetTanMedia, this.callback, config, bank)
return jobExecutor.getTanMediaList(context, tanMediaKind, tanMediumClass)
return config.jobExecutor.getTanMediaList(context, tanMediaKind, tanMediumClass)
}
open suspend fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData): FinTsClientResponse {
val context = JobContext(JobContextType.ChangeTanMedium, this.callback, product, bank)
val context = JobContext(JobContextType.ChangeTanMedium, this.callback, config, bank)
val response = jobExecutor.changeTanMedium(context, newActiveTanMedium)
val response = config.jobExecutor.changeTanMedium(context, newActiveTanMedium)
return FinTsClientResponse(context, response)
}
open suspend fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, account: AccountData): FinTsClientResponse {
val context = JobContext(JobContextType.TransferMoney, this.callback, product, bank, account)
val context = JobContext(JobContextType.TransferMoney, this.callback, config, bank, account)
return jobExecutor.transferMoneyAsync(context, bankTransferData)
return config.jobExecutor.transferMoneyAsync(context, bankTransferData)
}
}

View File

@ -1,35 +1,23 @@
package net.dankito.banking.fints
import net.dankito.banking.fints.callback.FinTsClientCallback
import net.dankito.banking.fints.messages.MessageBuilder
import net.dankito.banking.fints.config.FinTsClientConfiguration
import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.model.mapper.ModelMapper
import net.dankito.banking.fints.response.client.AddAccountResponse
import net.dankito.banking.fints.response.client.FinTsClientResponse
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
import net.dankito.banking.fints.util.IBase64Service
import net.dankito.banking.fints.util.PureKotlinBase64Service
import net.dankito.banking.fints.util.TanMethodSelector
import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.fints.webclient.KtorWebClient
open class FinTsClientForCustomer(
val bank: BankData,
callback: FinTsClientCallback,
requestExecutor: RequestExecutor = RequestExecutor(),
messageBuilder: MessageBuilder = MessageBuilder(),
modelMapper: ModelMapper = ModelMapper(messageBuilder),
protected open val tanMethodSelector: TanMethodSelector = TanMethodSelector(),
product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically)
config: FinTsClientConfiguration,
callback: FinTsClientCallback
) {
constructor(bank: BankData, callback: FinTsClientCallback, webClient: IWebClient = KtorWebClient(), base64Service: IBase64Service = PureKotlinBase64Service(),
product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0")) // TODO: get version dynamically)
: this(bank, callback, RequestExecutor(MessageBuilder(), webClient, base64Service), product = product)
constructor(bank: BankData, callback: FinTsClientCallback) : this(bank, FinTsClientConfiguration(), callback)
protected val client = FinTsClientDeprecated(callback, FinTsJobExecutor(requestExecutor, messageBuilder, modelMapper, tanMethodSelector), product)
protected val client = FinTsClientDeprecated(config, callback)
open val messageLogWithoutSensitiveData: List<MessageLogEntry> = mutableListOf()

View File

@ -0,0 +1,26 @@
package net.dankito.banking.fints.config
import net.dankito.banking.fints.FinTsJobExecutor
import net.dankito.banking.fints.RequestExecutor
import net.dankito.banking.fints.log.MessageLogCollector
import net.dankito.banking.fints.messages.MessageBuilder
import net.dankito.banking.fints.model.mapper.ModelMapper
import net.dankito.banking.fints.util.FinTsServerAddressFinder
import net.dankito.banking.fints.util.IBase64Service
import net.dankito.banking.fints.util.PureKotlinBase64Service
import net.dankito.banking.fints.util.TanMethodSelector
import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.fints.webclient.KtorWebClient
class FinTsClientConfiguration(
var options: FinTsClientOptions = FinTsClientOptions(),
messageBuilder: MessageBuilder = MessageBuilder(),
webClient: IWebClient = KtorWebClient(),
base64Service: IBase64Service = PureKotlinBase64Service(),
requestExecutor: RequestExecutor = RequestExecutor(messageBuilder, webClient, base64Service),
modelMapper: ModelMapper = ModelMapper(messageBuilder),
tanMethodSelector: TanMethodSelector = TanMethodSelector(),
var jobExecutor: FinTsJobExecutor = FinTsJobExecutor(requestExecutor, messageBuilder, modelMapper, tanMethodSelector),
var finTsServerAddressFinder: FinTsServerAddressFinder = FinTsServerAddressFinder(),
var messageLogCollector: MessageLogCollector = MessageLogCollector()
)

View File

@ -0,0 +1,12 @@
package net.dankito.banking.fints.config
import net.dankito.banking.fints.model.ProductData
data class FinTsClientOptions(
val version: String = "1.0.0", // TODO: get version dynamically
val productName: String = "15E53C26816138699C7B6A3E8"
) {
val product: ProductData by lazy { ProductData(productName, version) }
}

View File

@ -2,6 +2,7 @@ package net.dankito.banking.fints.model
import kotlinx.atomicfu.atomic
import net.dankito.banking.fints.callback.FinTsClientCallback
import net.dankito.banking.fints.config.FinTsClientConfiguration
import net.dankito.banking.fints.log.IMessageLogAppender
import net.dankito.banking.fints.log.MessageContext
import net.dankito.banking.fints.log.MessageLogCollector
@ -17,14 +18,13 @@ import kotlin.reflect.KClass
open class JobContext(
open val type: JobContextType,
open val callback: FinTsClientCallback,
product: ProductData,
open val config: FinTsClientConfiguration,
bank: BankData,
/**
* Only set if the current context is for a specific account (like get account's transactions).
*/
open val account: AccountData? = null,
protected open val messageLogCollector: MessageLogCollector = MessageLogCollector()
) : MessageBaseData(bank, product), IMessageLogAppender {
open val account: AccountData? = null
) : MessageBaseData(bank, config.options.product), IMessageLogAppender {
companion object {
private val JobCount = atomic(0) // this variable is accessed from multiple threads, so make it thread safe
@ -38,7 +38,7 @@ open class JobContext(
open val responseParser: ResponseParser = ResponseParser(logAppender = this)
open val messageLogWithoutSensitiveData: List<MessageLogEntry>
get() = messageLogCollector.messageLogWithoutSensitiveData
get() = config.messageLogCollector.messageLogWithoutSensitiveData
open var dialog: DialogContext = DialogContext() // create null value so that variable is not null
@ -72,11 +72,11 @@ open class JobContext(
open fun addMessageLog(type: MessageLogEntryType, message: String) {
messageLogCollector.addMessageLog(type, message, createMessageContext())
config.messageLogCollector.addMessageLog(type, message, createMessageContext())
}
override fun logError(loggingClass: KClass<*>, message: String, e: Exception?) {
messageLogCollector.logError(loggingClass, message, createMessageContext(), e)
config.messageLogCollector.logError(loggingClass, message, createMessageContext(), e)
}
protected open fun createMessageContext(): MessageContext {

View File

@ -2,6 +2,7 @@ package net.dankito.banking.fints
import kotlinx.datetime.LocalDate
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
import net.dankito.banking.fints.config.FinTsClientConfiguration
import net.dankito.banking.fints.extensions.randomWithSeed
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
@ -55,6 +56,8 @@ abstract class FinTsTestBase {
const val Time = 182752
val ClientConfig = FinTsClientConfiguration()
init {
Bank.changeTanMediumParameters = ChangeTanMediaParameters(JobParameters("", 1, 1, 1, ":0:0"), false, false, false, false, false, listOf())
@ -72,7 +75,7 @@ abstract class FinTsTestBase {
protected open fun createContext(bank: BankData = Bank, dialogId: String = DialogContext.InitialDialogId): JobContext {
val context = JobContext(JobContextType.AnonymousBankInfo, SimpleFinTsClientCallback(), Product, bank)
val context = JobContext(JobContextType.AnonymousBankInfo, SimpleFinTsClientCallback(), ClientConfig, bank)
context.startNewDialog(dialogId = dialogId)
return context