From 0a9b31b39321d2e1709135c30119277b20ea7dec Mon Sep 17 00:00:00 2001 From: dankito Date: Fri, 12 Nov 2021 20:36:49 +0100 Subject: [PATCH] Created JobContext to have a cross dialog context per job and to be able to set FinTsClientCallback (and later on other objects) on a job basis --- .../net/dankito/banking/fints/FinTsClient.kt | 59 ++-- .../banking/fints/FinTsClientForCustomer.kt | 4 +- .../dankito/banking/fints/FinTsJobExecutor.kt | 314 +++++++++--------- .../dankito/banking/fints/RequestExecutor.kt | 61 ++-- .../banking/fints/messages/MessageBuilder.kt | 109 +++--- .../dankito/banking/fints/model/JobContext.kt | 24 ++ .../banking/fints/model/JobContextType.kt | 18 + .../dankito/banking/fints/FinTsTestBase.kt | 10 + .../fints/messages/MessageBuilderTest.kt | 36 +- .../bankdetails/BanksFinTsDetailsRetriever.kt | 14 +- 10 files changed, 367 insertions(+), 282 deletions(-) create mode 100644 fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt create mode 100644 fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContextType.kt diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt index a7895776..74abb52d 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt @@ -9,14 +9,16 @@ import net.dankito.banking.fints.response.BankResponse import net.dankito.banking.fints.response.client.* import net.dankito.banking.fints.response.segments.* import net.dankito.utils.multiplatform.Date +import kotlin.jvm.JvmOverloads /** * This is the high level FinTS client that groups single low level jobs of [FinTsJobExecutor] to senseful units e.g. * [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 FinTsClient( - protected open val jobExecutor: FinTsJobExecutor // TODO: recreate when callback is set to avoid multithreading issues - but use its configured instances like RequestExecutor +open class FinTsClient @JvmOverloads constructor( + open var callback: FinTsClientCallback, + protected open val jobExecutor: FinTsJobExecutor = FinTsJobExecutor() ) { companion object { @@ -24,16 +26,9 @@ open class FinTsClient( } - constructor(callback: FinTsClientCallback) : this(FinTsJobExecutor(callback)) - - open val messageLogWithoutSensitiveData: List get() = jobExecutor.messageLogWithoutSensitiveData - open fun setCallback(callback: FinTsClientCallback) { - jobExecutor.callback = callback - } - /** * Retrieves information about bank (e.g. supported HBCI versions, FinTS server address, @@ -55,7 +50,7 @@ open class FinTsClient( * On success [bank] parameter is updated afterwards. */ open fun getAnonymousBankInfo(bank: BankData, callback: (FinTsClientResponse) -> Unit) { - jobExecutor.getAnonymousBankInfo(bank) { response -> + jobExecutor.getAnonymousBankInfo(JobContext(JobContextType.AnonymousBankInfo, this.callback, bank)) { response -> callback(FinTsClientResponse(response)) } } @@ -63,10 +58,11 @@ open class FinTsClient( open fun addAccountAsync(parameter: AddAccountParameter, callback: (AddAccountResponse) -> Unit) { val bank = parameter.bank + val context = JobContext(JobContextType.AddAccount, this.callback, bank) /* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */ - jobExecutor.retrieveBasicDataLikeUsersTanMethods(bank, parameter.preferredTanMethods, parameter.preferredTanMedium) { newUserInfoResponse -> + jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, parameter.preferredTanMethods, parameter.preferredTanMedium) { newUserInfoResponse -> if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong callback(AddAccountResponse(newUserInfoResponse, bank)) @@ -75,37 +71,38 @@ open class FinTsClient( /* Second dialog: some banks require that in order to initialize a dialog with strong customer authorization TAN media is required */ - addAccountGetAccountsAndTransactions(parameter, bank, callback) + addAccountGetAccountsAndTransactions(context, parameter, callback) } } - protected open fun addAccountGetAccountsAndTransactions(parameter: AddAccountParameter, bank: BankData, + protected open fun addAccountGetAccountsAndTransactions(context: JobContext, parameter: AddAccountParameter, callback: (AddAccountResponse) -> Unit) { /* Third dialog: Now we can initialize our first dialog with strong customer authorization. Use it to get UPD and customer's accounts */ - jobExecutor.getAccounts(bank) { getAccountsResponse -> + jobExecutor.getAccounts(context) { getAccountsResponse -> if (getAccountsResponse.successful == false) { - callback(AddAccountResponse(getAccountsResponse, bank)) + callback(AddAccountResponse(getAccountsResponse, context.bank)) return@getAccounts } /* Fourth dialog (if requested): Try to retrieve account balances and transactions of last 90 days without TAN */ if (parameter.fetchBalanceAndTransactions) { - addAccountGetAccountBalancesAndTransactions(bank, getAccountsResponse, callback) + addAccountGetAccountBalancesAndTransactions(context, getAccountsResponse, callback) } else { - val retrievedAccountData = bank.accounts.associateBy( { it }, { RetrievedAccountData.balanceAndTransactionsNotRequestedByUser(it) } ) - addAccountDone(bank, getAccountsResponse, retrievedAccountData, callback) + val retrievedAccountData = context.bank.accounts.associateBy( { it }, { RetrievedAccountData.balanceAndTransactionsNotRequestedByUser(it) } ) + addAccountDone(context, getAccountsResponse, retrievedAccountData, callback) } } } - protected open fun addAccountGetAccountBalancesAndTransactions(bank: BankData, getAccountsResponse: BankResponse, + protected open fun addAccountGetAccountBalancesAndTransactions(context: JobContext, getAccountsResponse: BankResponse, callback: (AddAccountResponse) -> Unit) { + val bank = context.bank val retrievedAccountData = bank.accounts.associateBy( { it }, { RetrievedAccountData.unsuccessful(it) } ).toMutableMap() val accountsSupportingRetrievingTransactions = bank.accounts.filter { it.supportsRetrievingBalance || it.supportsRetrievingAccountTransactions } @@ -113,7 +110,7 @@ open class FinTsClient( var countRetrievedAccounts = 0 if (countAccountsSupportingRetrievingTransactions == 0) { - addAccountDone(bank, getAccountsResponse, retrievedAccountData, callback) + addAccountDone(context, getAccountsResponse, retrievedAccountData, callback) return // no necessary just to make it clearer that code below doesn't get called } @@ -127,17 +124,17 @@ open class FinTsClient( countRetrievedAccounts++ if (countRetrievedAccounts == countAccountsSupportingRetrievingTransactions) { - addAccountDone(bank, getAccountsResponse, retrievedAccountData, callback) + addAccountDone(context, getAccountsResponse, retrievedAccountData, callback) } } } } - protected open fun addAccountDone(bank: BankData, getAccountsResponse: BankResponse, + protected open fun addAccountDone(context: JobContext, getAccountsResponse: BankResponse, retrievedAccountData: Map, callback: (AddAccountResponse) -> Unit) { - callback(AddAccountResponse(getAccountsResponse, bank, retrievedAccountData.values.toList())) + callback(AddAccountResponse(getAccountsResponse, context.bank, retrievedAccountData.values.toList())) } @@ -158,26 +155,34 @@ open class FinTsClient( open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData, callback: (GetTransactionsResponse) -> Unit) { - jobExecutor.getTransactionsAsync(parameter, bank, callback) + val context = JobContext(JobContextType.GetTransactions, this.callback, bank, parameter.account) + + jobExecutor.getTransactionsAsync(context, parameter, callback) } open fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) { - jobExecutor.getTanMediaList(bank, tanMediaKind, tanMediumClass, callback) + val context = JobContext(JobContextType.GetTanMedia, this.callback, bank) + + jobExecutor.getTanMediaList(context, tanMediaKind, tanMediumClass, callback) } open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, callback: (FinTsClientResponse) -> Unit) { - jobExecutor.changeTanMedium(newActiveTanMedium, bank) { response -> + val context = JobContext(JobContextType.ChangeTanMedium, this.callback, bank) + + jobExecutor.changeTanMedium(context, newActiveTanMedium) { response -> callback(FinTsClientResponse(response)) } } open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, account: AccountData, callback: (FinTsClientResponse) -> Unit) { - jobExecutor.doBankTransferAsync(bankTransferData, bank, account, callback) + val context = JobContext(JobContextType.TransferMoney, this.callback, bank, account) + + jobExecutor.doBankTransferAsync(context, bankTransferData, callback) } } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClientForCustomer.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClientForCustomer.kt index 238630c9..7185f3eb 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClientForCustomer.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClientForCustomer.kt @@ -32,14 +32,14 @@ open class FinTsClientForCustomer( : this(bank, callback, RequestExecutor(MessageBuilder(), webClient, base64Service)) - protected val client = FinTsClient(FinTsJobExecutor(callback, requestExecutor, messageBuilder, mt940Parser, modelMapper, tanMethodSelector, product)) + protected val client = FinTsClient(callback, FinTsJobExecutor(requestExecutor, messageBuilder, mt940Parser, modelMapper, tanMethodSelector, product)) open val messageLogWithoutSensitiveData: List get() = client.messageLogWithoutSensitiveData open fun setCallback(callback: FinTsClientCallback) { - client.setCallback(callback) + client.callback = callback } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt index d4e908a2..49294bd2 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt @@ -32,7 +32,6 @@ import net.dankito.utils.multiplatform.ObjectReference * In almost all cases you want to use [FinTsClient] which wraps these business transactions to a higher level API. */ open class FinTsJobExecutor( - open var callback: FinTsClientCallback, protected open val requestExecutor: RequestExecutor = RequestExecutor(), protected open val messageBuilder: MessageBuilder = MessageBuilder(), protected open val mt940Parser: IAccountTransactionsParser = Mt940AccountTransactionsParser(), @@ -55,30 +54,31 @@ open class FinTsJobExecutor( } - open fun getAnonymousBankInfo(bank: BankData, callback: (BankResponse) -> Unit) { - val dialogContext = DialogContext(bank, product) + open fun getAnonymousBankInfo(context: JobContext, callback: (BankResponse) -> Unit) { + val dialogContext = DialogContext(context.bank, product) + context.startNewDialog(dialogContext) - val message = messageBuilder.createAnonymousDialogInitMessage(dialogContext) + val message = messageBuilder.createAnonymousDialogInitMessage(context) - getAndHandleResponseForMessage(message, dialogContext) { response -> + getAndHandleResponseForMessage(context, message) { response -> if (response.successful) { - closeAnonymousDialog(dialogContext, response) + closeAnonymousDialog(context, response) } callback(response) } } - protected open fun closeAnonymousDialog(dialogContext: DialogContext, response: BankResponse) { + protected open fun closeAnonymousDialog(context: JobContext, response: BankResponse) { // bank already closed dialog -> there's no need to send dialog end message - if (dialogContext.closeDialog == false || dialogContext.didBankCloseDialog) { + if (context.dialog.closeDialog == false || context.dialog.didBankCloseDialog) { return } - val dialogEndRequestBody = messageBuilder.createAnonymousDialogEndMessage(dialogContext) + val dialogEndRequestBody = messageBuilder.createAnonymousDialogEndMessage(context) - fireAndForgetMessage(dialogEndRequestBody, dialogContext) + fireAndForgetMessage(context, dialogEndRequestBody) } @@ -89,8 +89,10 @@ open class FinTsJobExecutor( * * Be aware this method resets BPD, UPD and selected TAN method! */ - open fun retrieveBasicDataLikeUsersTanMethods(bank: BankData, preferredTanMethods: List? = null, preferredTanMedium: String? = null, + open fun retrieveBasicDataLikeUsersTanMethods(context: JobContext, preferredTanMethods: List? = null, preferredTanMedium: String? = null, closeDialog: Boolean = false, callback: (BankResponse) -> Unit) { + val bank = context.bank + // just to ensure settings are in its initial state and that bank sends us bank parameter (BPD), // user parameter (UPD) and allowed tan methods for user (therefore the resetSelectedTanMethod()) bank.resetBpdVersion() @@ -105,21 +107,22 @@ open class FinTsJobExecutor( // this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN methods val dialogContext = DialogContext(bank, product, closeDialog, versionOfSecurityMethod = VersionDesSicherheitsverfahrens.Version_1) + context.startNewDialog(dialogContext) - val message = messageBuilder.createInitDialogMessage(dialogContext) + val message = messageBuilder.createInitDialogMessage(context) - getAndHandleResponseForMessage(message, dialogContext) { response -> - closeDialog(dialogContext) + getAndHandleResponseForMessage(context, message) { response -> + closeDialog(context) - handleGetUsersTanMethodsResponse(response, dialogContext) { getTanMethodsResponse -> + handleGetUsersTanMethodsResponse(context, response) { getTanMethodsResponse -> if (bank.tanMethodsAvailableForUser.isEmpty()) { // could not retrieve supported tan methods for user callback(getTanMethodsResponse) } else { - getUsersTanMethod(bank, preferredTanMethods) { + getUsersTanMethod(context, preferredTanMethods) { if (bank.isTanMethodSelected == false) { callback(getTanMethodsResponse) } else if (bank.tanMedia.isEmpty() && isJobSupported(bank, CustomerSegmentId.TanMediaList)) { // tan media not retrieved yet - getTanMediaList(bank, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien, preferredTanMedium) { + getTanMediaList(context, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien, preferredTanMedium) { callback(getTanMethodsResponse) // TODO: judge if bank requires selecting TAN media and if though evaluate getTanMediaListResponse } } else { @@ -131,12 +134,12 @@ open class FinTsJobExecutor( } } - protected open fun handleGetUsersTanMethodsResponse(response: BankResponse, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun handleGetUsersTanMethodsResponse(context: JobContext, response: BankResponse, callback: (BankResponse) -> Unit) { val getUsersTanMethodsResponse = GetUserTanMethodsResponse(response) // even though it is required by specification some banks don't support retrieving user's TAN method by setting TAN method to '999' if (bankDoesNotSupportRetrievingUsersTanMethods(getUsersTanMethodsResponse)) { - getBankDataForNewUserViaAnonymousDialog(dialogContext.bank, callback) // TODO: should not be necessary anymore + getBankDataForNewUserViaAnonymousDialog(context, callback) // TODO: should not be necessary anymore } else { callback(getUsersTanMethodsResponse) @@ -150,8 +153,10 @@ open class FinTsJobExecutor( } // TODO: this is only a quick fix. Find a better and general solution - protected open fun getBankDataForNewUserViaAnonymousDialog(bank: BankData, callback: (BankResponse) -> Unit) { - getAnonymousBankInfo(bank) { anonymousBankInfoResponse -> + protected open fun getBankDataForNewUserViaAnonymousDialog(context: JobContext, callback: (BankResponse) -> Unit) { + getAnonymousBankInfo(context) { anonymousBankInfoResponse -> + val bank = context.bank + if (anonymousBankInfoResponse.successful == false) { callback(anonymousBankInfoResponse) } else if (bank.tanMethodsSupportedByBank.isEmpty()) { // should only be a theoretical error @@ -159,12 +164,13 @@ open class FinTsJobExecutor( } else { bank.tanMethodsAvailableForUser = bank.tanMethodsSupportedByBank - getUsersTanMethod(bank) { didSelectTanMethod -> + getUsersTanMethod(context) { didSelectTanMethod -> if (didSelectTanMethod) { val dialogContext = DialogContext(bank, product) + context.startNewDialog(dialogContext) - initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(dialogContext) { initDialogResponse -> - closeDialog(dialogContext) + initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context) { initDialogResponse -> + closeDialog(context) callback(initDialogResponse) } @@ -177,48 +183,50 @@ open class FinTsJobExecutor( } - open fun getAccounts(bank: BankData, callback: (BankResponse) -> Unit) { + open fun getAccounts(context: JobContext, callback: (BankResponse) -> Unit) { - val dialogContext = DialogContext(bank, product, false) + val dialogContext = DialogContext(context.bank, product, false) + context.startNewDialog(dialogContext) - initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(dialogContext) { response -> - closeDialog(dialogContext) + initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context) { response -> + closeDialog(context) callback(response) } } - open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData, callback: (GetTransactionsResponse) -> Unit) { + open fun getTransactionsAsync(context: JobContext, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { - val dialogContext = DialogContext(bank, product) + val dialogContext = DialogContext(context.bank, product) + context.startNewDialog(dialogContext) - initDialogWithStrongCustomerAuthentication(dialogContext) { initDialogResponse -> + initDialogWithStrongCustomerAuthentication(context) { initDialogResponse -> if (initDialogResponse.successful == false) { callback(GetTransactionsResponse(initDialogResponse, RetrievedAccountData.unsuccessfulList(parameter.account))) } else { // we now retrieved the fresh account information from FinTS server, use that one - parameter.account = getUpdatedAccount(bank, parameter.account) + parameter.account = getUpdatedAccount(context, parameter.account) - mayGetBalance(parameter, dialogContext) { balanceResponse -> + mayGetBalance(context, parameter) { balanceResponse -> if (dialogContext.didBankCloseDialog) { callback(GetTransactionsResponse(balanceResponse ?: initDialogResponse, RetrievedAccountData.unsuccessfulList(parameter.account))) } else { - getTransactionsAfterInitAndGetBalance(parameter, dialogContext, balanceResponse, callback) + getTransactionsAfterInitAndGetBalance(context, parameter, balanceResponse, callback) } } } } } - private fun getUpdatedAccount(bank: BankData, account: AccountData): AccountData { - return bank.accounts.firstOrNull { it.accountIdentifier == account.accountIdentifier } ?: account + private fun getUpdatedAccount(context: JobContext, account: AccountData): AccountData { + return context.bank.accounts.firstOrNull { it.accountIdentifier == account.accountIdentifier } ?: account } - protected open fun getTransactionsAfterInitAndGetBalance(parameter: GetTransactionsParameter, dialogContext: DialogContext, + protected open fun getTransactionsAfterInitAndGetBalance(context: JobContext, parameter: GetTransactionsParameter, balanceResponse: BankResponse?, callback: (GetTransactionsResponse) -> Unit) { var balance: Money? = balanceResponse?.getFirstSegmentById(InstituteSegmentId.Balance)?.let { Money(it.balance, it.currency) @@ -226,16 +234,16 @@ open class FinTsJobExecutor( val bookedTransactions = mutableSetOf() val unbookedTransactions = mutableSetOf() - val message = messageBuilder.createGetTransactionsMessage(parameter, dialogContext) + val message = messageBuilder.createGetTransactionsMessage(context, parameter) var remainingMt940String = "" - dialogContext.abortIfTanIsRequired = parameter.abortIfTanIsRequired + context.dialog.abortIfTanIsRequired = parameter.abortIfTanIsRequired - dialogContext.chunkedResponseHandler = { response -> + context.dialog.chunkedResponseHandler = { response -> response.getFirstSegmentById(InstituteSegmentId.AccountTransactionsMt940)?.let { transactionsSegment -> val (chunkTransaction, remainder) = mt940Parser.parseTransactionsChunk(remainingMt940String + transactionsSegment.bookedTransactionsString, - dialogContext.bank, parameter.account) + context.bank, parameter.account) bookedTransactions.addAll(chunkTransaction) remainingMt940String = remainder @@ -249,8 +257,8 @@ open class FinTsJobExecutor( } } - getAndHandleResponseForMessage(message, dialogContext) { response -> - closeDialog(dialogContext) + getAndHandleResponseForMessage(context, message) { response -> + closeDialog(context) val successful = response.tanRequiredButWeWereToldToAbortIfSo || (response.successful && (parameter.alsoRetrieveBalance == false || balance != null)) @@ -267,13 +275,11 @@ open class FinTsJobExecutor( } } - protected open fun mayGetBalance(parameter: GetTransactionsParameter, dialogContext: DialogContext, callback: (BankResponse?) -> Unit) { + protected open fun mayGetBalance(context: JobContext, parameter: GetTransactionsParameter, callback: (BankResponse?) -> Unit) { if (parameter.alsoRetrieveBalance && parameter.account.supportsRetrievingBalance) { - val message = messageBuilder.createGetBalanceMessage(parameter.account, dialogContext) + val message = messageBuilder.createGetBalanceMessage(context, parameter.account) - getAndHandleResponseForMessage(message, dialogContext) { response -> - callback(response) - } + getAndHandleResponseForMessage(context, message, callback) } else { callback(null) @@ -292,14 +298,16 @@ open class FinTsJobExecutor( * * If you change customer system id during a dialog your messages get rejected by bank institute. */ - protected open fun synchronizeCustomerSystemId(bank: BankData, callback: (FinTsClientResponse) -> Unit) { + protected open fun synchronizeCustomerSystemId(context: JobContext, callback: (FinTsClientResponse) -> Unit) { - val dialogContext = DialogContext(bank, product) - val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(dialogContext) + val dialogContext = DialogContext(context.bank, product) + context.startNewDialog(dialogContext) - getAndHandleResponseForMessage(message, dialogContext) { response -> + val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(context) + + getAndHandleResponseForMessage(context, message) { response -> if (response.successful) { - closeDialog(dialogContext) + closeDialog(context) } callback(FinTsClientResponse(response)) @@ -307,18 +315,18 @@ open class FinTsJobExecutor( } - open fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, + open fun getTanMediaList(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) { - getTanMediaList(bank, tanMediaKind, tanMediumClass, null, callback) + getTanMediaList(context, tanMediaKind, tanMediumClass, null, callback) } - protected open fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, + protected open fun getTanMediaList(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, preferredTanMedium: String? = null, callback: (GetTanMediaListResponse) -> Unit) { - sendMessageAndHandleResponse(bank, CustomerSegmentId.TanMediaList, false, { dialogContext -> - messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass) + sendMessageAndHandleResponse(context, CustomerSegmentId.TanMediaList, false, { + messageBuilder.createGetTanMediaListMessage(context, tanMediaKind, tanMediumClass) }) { response -> - handleGetTanMediaListResponse(response, bank, preferredTanMedium, callback) + handleGetTanMediaListResponse(response, context.bank, preferredTanMedium, callback) } } @@ -339,80 +347,79 @@ open class FinTsJobExecutor( } - open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, callback: (BankResponse) -> Unit) { + open fun changeTanMedium(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, callback: (BankResponse) -> Unit) { + val bank = context.bank if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) { - this.callback.enterTanGeneratorAtc(bank, newActiveTanMedium) { enteredAtc -> + context.callback.enterTanGeneratorAtc(bank, newActiveTanMedium) { enteredAtc -> if (enteredAtc.hasAtcBeenEntered == false) { val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate callback(BankResponse(false, internalError = message)) } else { - sendChangeTanMediumMessage(bank, newActiveTanMedium, enteredAtc, callback) + sendChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc, callback) } } } else { - sendChangeTanMediumMessage(bank, newActiveTanMedium, null, callback) + sendChangeTanMediumMessage(context, newActiveTanMedium, null, callback) } } - protected open fun sendChangeTanMediumMessage(bank: BankData, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?, + protected open fun sendChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?, callback: (BankResponse) -> Unit) { - sendMessageAndHandleResponse(bank, null, true, { dialogContext -> - messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, enteredAtc?.tan, enteredAtc?.atc) - }) { response -> - callback(response) - } + sendMessageAndHandleResponse(context, null, true, { + messageBuilder.createChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc?.tan, enteredAtc?.atc) + }, callback) } - open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, account: AccountData, callback: (FinTsClientResponse) -> Unit) { + open fun doBankTransferAsync(context: JobContext, bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) { - sendMessageAndHandleResponse(bank, null, true, { dialogContext -> - val updatedAccount = getUpdatedAccount(bank, account) - messageBuilder.createBankTransferMessage(bankTransferData, updatedAccount, dialogContext) + sendMessageAndHandleResponse(context, null, true, { + val updatedAccount = getUpdatedAccount(context, context.account!!) + messageBuilder.createBankTransferMessage(context, bankTransferData, updatedAccount) }) { response -> callback(FinTsClientResponse(response)) } } - protected open fun getAndHandleResponseForMessage(message: MessageBuilderResult, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { - requestExecutor.getAndHandleResponseForMessage(message, dialogContext, + protected open fun getAndHandleResponseForMessage(context: JobContext, message: MessageBuilderResult, callback: (BankResponse) -> Unit) { + requestExecutor.getAndHandleResponseForMessage(message, context, { tanResponse, bankResponse, tanRequiredCallback -> // if we receive a message that tells us a TAN is required below callback doesn't get called for that message -> update data here // for Hypovereinsbank it's absolutely necessary to update bank data (more specific: PinInfo / HIPINS) after first strong authentication dialog init response // as HIPINS differ in anonymous and in authenticated dialog. Anonymous dialog tells us for HKSAL and HKKAZ no TAN is needed - updateBankAndCustomerDataIfResponseSuccessful(dialogContext, bankResponse) - handleEnteringTanRequired(tanResponse, bankResponse, dialogContext, tanRequiredCallback) + updateBankAndCustomerDataIfResponseSuccessful(context, bankResponse) + handleEnteringTanRequired(context, tanResponse, bankResponse, tanRequiredCallback) }) { response -> // TODO: really update data only on complete successfully response? as it may contain useful information anyway // TODO: extract method for this code part - updateBankAndCustomerDataIfResponseSuccessful(dialogContext, response) + updateBankAndCustomerDataIfResponseSuccessful(context, response) callback(response) } } - protected open fun fireAndForgetMessage(message: MessageBuilderResult, dialogContext: DialogContext) { - requestExecutor.fireAndForgetMessage(message, dialogContext) + protected open fun fireAndForgetMessage(context: JobContext, message: MessageBuilderResult) { + requestExecutor.fireAndForgetMessage(context, message) } - protected open fun handleEnteringTanRequired(tanResponse: TanResponse, response: BankResponse, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { - val bank = dialogContext.bank // TODO: copy required data to TanChallenge + protected open fun handleEnteringTanRequired(context: JobContext, tanResponse: TanResponse, response: BankResponse, callback: (BankResponse) -> Unit) { + val bank = context.bank // TODO: copy required data to TanChallenge val tanChallenge = createTanChallenge(tanResponse, bank) val userDidCancelEnteringTan = ObjectReference(false) - this.callback.enterTan(bank, tanChallenge) { enteredTanResult -> + context.callback.enterTan(bank, tanChallenge) { enteredTanResult -> userDidCancelEnteringTan.value = true - handleEnterTanResult(enteredTanResult, tanResponse, response, dialogContext, callback) + handleEnterTanResult(context, enteredTanResult, tanResponse, response, callback) } - mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(tanChallenge, tanResponse, userDidCancelEnteringTan, dialogContext) + mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context, tanChallenge, tanResponse, userDidCancelEnteringTan) } protected open fun createTanChallenge(tanResponse: TanResponse, bank: BankData): TanChallenge { @@ -435,28 +442,28 @@ open class FinTsJobExecutor( } } - protected open fun mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(tanChallenge: TanChallenge, tanResponse: TanResponse, - userDidCancelEnteringTan: ObjectReference, dialogContext: DialogContext + protected open fun mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, tanResponse: TanResponse, + userDidCancelEnteringTan: ObjectReference ) { - dialogContext.bank.selectedTanMethod.decoupledParameters?.let { decoupledTanMethodParameters -> + context.bank.selectedTanMethod.decoupledParameters?.let { decoupledTanMethodParameters -> if (tanResponse.tanProcess == TanProcess.AppTan && decoupledTanMethodParameters.periodicStateRequestsAllowed) { - automaticallyRetrieveIfUserEnteredDecoupledTan(tanChallenge, userDidCancelEnteringTan, dialogContext) + automaticallyRetrieveIfUserEnteredDecoupledTan(context, tanChallenge, userDidCancelEnteringTan) } } } - protected open fun automaticallyRetrieveIfUserEnteredDecoupledTan(tanChallenge: TanChallenge, userDidCancelEnteringTan: ObjectReference, dialogContext: DialogContext) { + protected open fun automaticallyRetrieveIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, userDidCancelEnteringTan: ObjectReference) { log.info("automaticallyRetrieveIfUserEnteredDecoupledTan() called for $tanChallenge") } - protected open fun handleEnterTanResult(enteredTanResult: EnterTanResult, tanResponse: TanResponse, response: BankResponse, - dialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun handleEnterTanResult(context: JobContext, enteredTanResult: EnterTanResult, tanResponse: TanResponse, + response: BankResponse, callback: (BankResponse) -> Unit) { if (enteredTanResult.changeTanMethodTo != null) { - handleUserAsksToChangeTanMethodAndResendLastMessage(enteredTanResult.changeTanMethodTo, dialogContext, callback) + handleUserAsksToChangeTanMethodAndResendLastMessage(context, enteredTanResult.changeTanMethodTo, callback) } else if (enteredTanResult.changeTanMediumTo is TanGeneratorTanMedium) { - handleUserAsksToChangeTanMediumAndResendLastMessage(enteredTanResult.changeTanMediumTo, dialogContext, + handleUserAsksToChangeTanMediumAndResendLastMessage(context, enteredTanResult.changeTanMediumTo, enteredTanResult.changeTanMediumResultCallback, callback) } else if (enteredTanResult.enteredTan == null) { @@ -467,66 +474,67 @@ open class FinTsJobExecutor( callback(response) } else { - sendTanToBank(enteredTanResult.enteredTan, tanResponse, dialogContext, callback) + sendTanToBank(context, enteredTanResult.enteredTan, tanResponse, callback) } } - protected open fun sendTanToBank(enteredTan: String, tanResponse: TanResponse, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun sendTanToBank(context: JobContext, enteredTan: String, tanResponse: TanResponse, callback: (BankResponse) -> Unit) { - val message = messageBuilder.createSendEnteredTanMessage(enteredTan, tanResponse, dialogContext) + val message = messageBuilder.createSendEnteredTanMessage(context, enteredTan, tanResponse) - getAndHandleResponseForMessage(message, dialogContext, callback) + getAndHandleResponseForMessage(context, message, callback) } - protected open fun handleUserAsksToChangeTanMethodAndResendLastMessage(changeTanMethodTo: TanMethod, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun handleUserAsksToChangeTanMethodAndResendLastMessage(context: JobContext, changeTanMethodTo: TanMethod, callback: (BankResponse) -> Unit) { - dialogContext.bank.selectedTanMethod = changeTanMethodTo + context.bank.selectedTanMethod = changeTanMethodTo - val lastCreatedMessage = dialogContext.currentMessage + val lastCreatedMessage = context.dialog.currentMessage - lastCreatedMessage?.let { closeDialog(dialogContext) } + lastCreatedMessage?.let { closeDialog(context) } - resendMessageInNewDialog(lastCreatedMessage, dialogContext, callback) + resendMessageInNewDialog(context, lastCreatedMessage, callback) } - protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(changeTanMediumTo: TanGeneratorTanMedium, - dialogContext: DialogContext, + protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(context: JobContext, changeTanMediumTo: TanGeneratorTanMedium, changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?, callback: (BankResponse) -> Unit) { - val lastCreatedMessage = dialogContext.currentMessage + val lastCreatedMessage = context.dialog.currentMessage - lastCreatedMessage?.let { closeDialog(dialogContext) } + lastCreatedMessage?.let { closeDialog(context) } - changeTanMedium(changeTanMediumTo, dialogContext.bank) { changeTanMediumResponse -> + changeTanMedium(context, changeTanMediumTo) { changeTanMediumResponse -> changeTanMediumResultCallback?.invoke(FinTsClientResponse(changeTanMediumResponse)) if (changeTanMediumResponse.successful == false || lastCreatedMessage == null) { callback(changeTanMediumResponse) } else { - resendMessageInNewDialog(lastCreatedMessage, dialogContext, callback) + resendMessageInNewDialog(context, lastCreatedMessage, callback) } } } - protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun resendMessageInNewDialog(context: JobContext, lastCreatedMessage: MessageBuilderResult?, callback: (BankResponse) -> Unit) { if (lastCreatedMessage != null) { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message - val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.product, chunkedResponseHandler = previousDialogContext.chunkedResponseHandler) + val previousDialog = context.dialog + val newDialogContext = DialogContext(context.bank, previousDialog.product, chunkedResponseHandler = previousDialog.chunkedResponseHandler) + context.startNewDialog(newDialogContext) - initDialogWithStrongCustomerAuthentication(newDialogContext) { initDialogResponse -> + initDialogWithStrongCustomerAuthentication(context) { initDialogResponse -> if (initDialogResponse.successful == false) { callback(initDialogResponse) } else { - val newMessage = messageBuilder.rebuildMessage(lastCreatedMessage, newDialogContext) + val newMessage = messageBuilder.rebuildMessage(context, lastCreatedMessage) - getAndHandleResponseForMessage(newMessage, newDialogContext) { response -> - closeDialog(newDialogContext) + getAndHandleResponseForMessage(context, newMessage) { response -> + closeDialog(context) callback(response) } @@ -540,91 +548,95 @@ open class FinTsJobExecutor( } - protected open fun sendMessageAndHandleResponse(bank: BankData, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null, - closeDialog: Boolean = true, createMessage: (DialogContext) -> MessageBuilderResult, callback: (BankResponse) -> Unit) { + protected open fun sendMessageAndHandleResponse(context: JobContext, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null, + closeDialog: Boolean = true, createMessage: () -> MessageBuilderResult, callback: (BankResponse) -> Unit) { - val dialogContext = DialogContext(bank, product, closeDialog) + val dialogContext = DialogContext(context.bank, product, closeDialog) + context.startNewDialog(dialogContext) if (segmentForNonStrongCustomerAuthenticationTwoStepTanProcess == null) { - initDialogWithStrongCustomerAuthentication(dialogContext) { initDialogResponse -> - sendMessageAndHandleResponseAfterDialogInitialization(dialogContext, initDialogResponse, createMessage, callback) + initDialogWithStrongCustomerAuthentication(context) { initDialogResponse -> + sendMessageAndHandleResponseAfterDialogInitialization(context, initDialogResponse, createMessage, callback) } } else { - initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(dialogContext, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess) { initDialogResponse -> - sendMessageAndHandleResponseAfterDialogInitialization(dialogContext, initDialogResponse, createMessage, callback) + initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(context, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess) { initDialogResponse -> + sendMessageAndHandleResponseAfterDialogInitialization(context, initDialogResponse, createMessage, callback) } } } - private fun sendMessageAndHandleResponseAfterDialogInitialization(dialogContext: DialogContext, initDialogResponse: BankResponse, createMessage: (DialogContext) -> MessageBuilderResult, callback: (BankResponse) -> Unit) { + private fun sendMessageAndHandleResponseAfterDialogInitialization(context: JobContext, initDialogResponse: BankResponse, + createMessage: () -> MessageBuilderResult, callback: (BankResponse) -> Unit) { if (initDialogResponse.successful == false) { callback(initDialogResponse) } else { - val message = createMessage(dialogContext) + val message = createMessage() - getAndHandleResponseForMessage(message, dialogContext) { response -> - closeDialog(dialogContext) + getAndHandleResponseForMessage(context, message) { response -> + closeDialog(context) callback(response) } } } - protected open fun initDialogWithStrongCustomerAuthentication(dialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun initDialogWithStrongCustomerAuthentication(context: JobContext, callback: (BankResponse) -> Unit) { // we first need to retrieve supported tan methods and jobs before we can do anything - ensureBasicBankDataRetrieved(dialogContext.bank) { retrieveBasicBankDataResponse -> + ensureBasicBankDataRetrieved(context) { retrieveBasicBankDataResponse -> if (retrieveBasicBankDataResponse.successful == false) { callback(retrieveBasicBankDataResponse) } else { // as in the next step we have to supply user's tan method, ensure user selected his or her - ensureTanMethodIsSelected(dialogContext.bank) { tanMethodSelectedResponse -> + ensureTanMethodIsSelected(context) { tanMethodSelectedResponse -> if (tanMethodSelectedResponse.successful == false) { callback(tanMethodSelectedResponse) } else { - initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(dialogContext, callback) + initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context, callback) } } } } } - protected open fun initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(dialogContext: DialogContext, callback: (BankResponse) -> Unit) { + protected open fun initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context: JobContext, callback: (BankResponse) -> Unit) { - val message = messageBuilder.createInitDialogMessage(dialogContext) + val message = messageBuilder.createInitDialogMessage(context) - getAndHandleResponseForMessage(message, dialogContext, callback) + getAndHandleResponseForMessage(context, message, callback) } - protected open fun initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(dialogContext: DialogContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?, + protected open fun initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(context: JobContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?, callback: (BankResponse) -> Unit) { - val message = messageBuilder.createInitDialogMessageWithoutStrongCustomerAuthentication(dialogContext, segmentIdForTwoStepTanProcess) + val message = messageBuilder.createInitDialogMessageWithoutStrongCustomerAuthentication(context, segmentIdForTwoStepTanProcess) - getAndHandleResponseForMessage(message, dialogContext, callback) + getAndHandleResponseForMessage(context, message, callback) } - protected open fun closeDialog(dialogContext: DialogContext) { + protected open fun closeDialog(context: JobContext) { // bank already closed dialog -> there's no need to send dialog end message - if (dialogContext.closeDialog == false || dialogContext.didBankCloseDialog) { + if (context.dialog.closeDialog == false || context.dialog.didBankCloseDialog) { return } - val dialogEndRequestBody = messageBuilder.createDialogEndMessage(dialogContext) + val dialogEndRequestBody = messageBuilder.createDialogEndMessage(context) - fireAndForgetMessage(dialogEndRequestBody, dialogContext) + fireAndForgetMessage(context, dialogEndRequestBody) } - protected open fun ensureBasicBankDataRetrieved(bank: BankData, callback: (BankResponse) -> Unit) { + protected open fun ensureBasicBankDataRetrieved(context: JobContext, callback: (BankResponse) -> Unit) { + val bank = context.bank + if (bank.tanMethodsSupportedByBank.isEmpty() || bank.supportedJobs.isEmpty()) { - retrieveBasicDataLikeUsersTanMethods(bank) { getBankInfoResponse -> + retrieveBasicDataLikeUsersTanMethods(context) { getBankInfoResponse -> if (getBankInfoResponse.successful == false) { callback(getBankInfoResponse) } else if (bank.tanMethodsSupportedByBank.isEmpty() || bank.supportedJobs.isEmpty()) { @@ -640,15 +652,17 @@ open class FinTsJobExecutor( } } - protected open fun ensureTanMethodIsSelected(bank: BankData, callback: (BankResponse) -> Unit) { + protected open fun ensureTanMethodIsSelected(context: JobContext, callback: (BankResponse) -> Unit) { + val bank = context.bank + if (bank.isTanMethodSelected == false) { if (bank.tanMethodsAvailableForUser.isEmpty()) { - retrieveBasicDataLikeUsersTanMethods(bank) { retrieveBasicDataResponse -> + retrieveBasicDataLikeUsersTanMethods(context) { retrieveBasicDataResponse -> callback(retrieveBasicDataResponse) } } else { - getUsersTanMethod(bank) { + getUsersTanMethod(context) { callback(createNoTanMethodSelectedResponse(bank)) } } @@ -665,7 +679,9 @@ open class FinTsJobExecutor( return BankResponse(true, noTanMethodSelected = noTanMethodSelected, internalError = errorMessage) } - open fun getUsersTanMethod(bank: BankData, preferredTanMethods: List? = null, done: (Boolean) -> Unit) { + open fun getUsersTanMethod(context: JobContext, preferredTanMethods: List? = null, done: (Boolean) -> Unit) { + val bank = context.bank + if (bank.tanMethodsAvailableForUser.size == 1) { // user has only one TAN method -> set it and we're done bank.selectedTanMethod = bank.tanMethodsAvailableForUser.first() done(true) @@ -679,7 +695,7 @@ open class FinTsJobExecutor( // we know user's supported tan methods, now ask user which one to select val suggestedTanMethod = tanMethodSelector.getSuggestedTanMethod(bank.tanMethodsAvailableForUser) - callback.askUserForTanMethod(bank.tanMethodsAvailableForUser, suggestedTanMethod) { selectedTanMethod -> + context.callback.askUserForTanMethod(bank.tanMethodsAvailableForUser, suggestedTanMethod) { selectedTanMethod -> if (selectedTanMethod != null) { bank.selectedTanMethod = selectedTanMethod done(true) @@ -696,9 +712,9 @@ open class FinTsJobExecutor( modelMapper.updateBankData(bank, response) } - protected open fun updateBankAndCustomerDataIfResponseSuccessful(dialogContext: DialogContext, response: BankResponse) { + protected open fun updateBankAndCustomerDataIfResponseSuccessful(context: JobContext, response: BankResponse) { if (response.successful) { - updateBankAndCustomerData(dialogContext.bank, response) + updateBankAndCustomerData(context.bank, response) } } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/RequestExecutor.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/RequestExecutor.kt index a9fe9c21..6fd9c400 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/RequestExecutor.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/RequestExecutor.kt @@ -43,7 +43,7 @@ open class RequestExecutor( } - open fun getAndHandleResponseForMessage(message: MessageBuilderResult, dialogContext: DialogContext, + open fun getAndHandleResponseForMessage(message: MessageBuilderResult, context: JobContext, tanRequiredCallback: (TanResponse, BankResponse, callback: (BankResponse) -> Unit) -> Unit, callback: (BankResponse) -> Unit) { if (message.createdMessage == null) { log.error("Could not create FinTS message to be sent to bank. isJobAllowed ${message.isJobAllowed}, isJobVersionSupported = ${message.isJobVersionSupported}," + @@ -51,16 +51,16 @@ open class RequestExecutor( callback(BankResponse(false, messageThatCouldNotBeCreated = message, internalError = "Could not create FinTS message to be sent to bank")) // TODO: translate } else { - getAndHandleResponseForMessage(message.createdMessage, dialogContext) { response -> - handleMayRequiresTan(response, dialogContext, tanRequiredCallback) { handledResponse -> + getAndHandleResponseForMessage(context, message.createdMessage) { response -> + handleMayRequiresTan(context, response, tanRequiredCallback) { handledResponse -> // if there's a Aufsetzpunkt (continuationId) set, then response is not complete yet, there's more information to fetch by sending this Aufsetzpunkt handledResponse.aufsetzpunkt?.let { continuationId -> if (handledResponse.followUpResponse == null) { // for re-sent messages followUpResponse is already set and dialog already closed -> would be overwritten with an error response that dialog is closed if (message.isSendEnteredTanMessage() == false) { // for sending TAN no follow up message can be created -> filter out, otherwise chunkedResponseHandler would get called twice for same response - dialogContext.chunkedResponseHandler?.invoke(handledResponse) + context.dialog.chunkedResponseHandler?.invoke(handledResponse) } - getFollowUpMessageForContinuationId(handledResponse, continuationId, message, dialogContext, tanRequiredCallback) { followUpResponse -> + getFollowUpMessageForContinuationId(context, handledResponse, continuationId, message, tanRequiredCallback) { followUpResponse -> handledResponse.followUpResponse = followUpResponse handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null @@ -75,7 +75,7 @@ open class RequestExecutor( // e.g. response = enter TAN response, but handledResponse is then response after entering TAN, e.g. account transactions // -> chunkedResponseHandler would get called for same handledResponse multiple times if (response == handledResponse) { - dialogContext.chunkedResponseHandler?.invoke(handledResponse) + context.dialog.chunkedResponseHandler?.invoke(handledResponse) } callback(handledResponse) @@ -85,16 +85,17 @@ open class RequestExecutor( } } - protected open fun getAndHandleResponseForMessage(requestBody: String, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { - addMessageLog(requestBody, MessageLogEntryType.Sent, dialogContext) + protected open fun getAndHandleResponseForMessage(context: JobContext, requestBody: String, callback: (BankResponse) -> Unit) { + addMessageLog(context, MessageLogEntryType.Sent, requestBody) - getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) { webResponse -> - val response = handleResponse(webResponse, dialogContext) + getResponseForMessage(requestBody, context.bank.finTs3ServerAddress) { webResponse -> + val response = handleResponse(context, webResponse) - dialogContext.response = response + val dialog = context.dialog + dialog.response = response - response.messageHeader?.let { header -> dialogContext.dialogId = header.dialogId } - dialogContext.didBankCloseDialog = response.didBankCloseDialog + response.messageHeader?.let { header -> dialog.dialogId = header.dialogId } + dialog.didBankCloseDialog = response.didBankCloseDialog callback(response) } @@ -106,17 +107,17 @@ open class RequestExecutor( webClient.post(finTs3ServerAddress, encodedRequestBody, "application/octet-stream", IWebClient.DefaultUserAgent, callback) } - open fun fireAndForgetMessage(message: MessageBuilderResult, dialogContext: DialogContext) { + open fun fireAndForgetMessage(context: JobContext, message: MessageBuilderResult) { message.createdMessage?.let { requestBody -> - addMessageLog(requestBody, MessageLogEntryType.Sent, dialogContext) + addMessageLog(context, MessageLogEntryType.Sent, requestBody) - getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) { } + getResponseForMessage(requestBody, context.bank.finTs3ServerAddress) { } // if really needed add received response to message log here } } - protected open fun handleResponse(webResponse: WebClientResponse, dialogContext: DialogContext): BankResponse { + protected open fun handleResponse(context: JobContext, webResponse: WebClientResponse): BankResponse { val responseBody = webResponse.body if (webResponse.successful && responseBody != null) { @@ -124,18 +125,18 @@ open class RequestExecutor( try { val decodedResponse = decodeBase64Response(responseBody) - addMessageLog(decodedResponse, MessageLogEntryType.Received, dialogContext) + addMessageLog(context, MessageLogEntryType.Received, decodedResponse) return responseParser.parse(decodedResponse) } catch (e: Exception) { - logError("Could not decode responseBody:\r\n'$responseBody'", dialogContext, e) + logError(context, "Could not decode responseBody:\r\n'$responseBody'", e) return BankResponse(false, internalError = e.getAllExceptionMessagesJoined()) } } else { - val bank = dialogContext.bank - logError("Request to $bank (${bank.finTs3ServerAddress}) failed", dialogContext, webResponse.error) + val bank = context.bank + logError(context, "Request to $bank (${bank.finTs3ServerAddress}) failed", webResponse.error) } return BankResponse(false, internalError = webResponse.error?.getAllExceptionMessagesJoined()) @@ -146,23 +147,23 @@ open class RequestExecutor( } - protected open fun getFollowUpMessageForContinuationId(response: BankResponse, continuationId: String, message: MessageBuilderResult, dialogContext: DialogContext, + protected open fun getFollowUpMessageForContinuationId(context: JobContext, response: BankResponse, continuationId: String, message: MessageBuilderResult, tanRequiredCallback: (TanResponse, BankResponse, callback: (BankResponse) -> Unit) -> Unit, callback: (BankResponse?) -> Unit) { - messageBuilder.rebuildMessageWithContinuationId(message, continuationId, dialogContext)?.let { followUpMessage -> - getAndHandleResponseForMessage(followUpMessage, dialogContext, tanRequiredCallback, callback) + messageBuilder.rebuildMessageWithContinuationId(context, message, continuationId)?.let { followUpMessage -> + getAndHandleResponseForMessage(followUpMessage, context, tanRequiredCallback, callback) } ?: run { callback(null) } } - protected open fun handleMayRequiresTan(response: BankResponse, dialogContext: DialogContext, + protected open fun handleMayRequiresTan(context: JobContext, response: BankResponse, tanRequiredCallback: (TanResponse, BankResponse, callback: (BankResponse) -> Unit) -> Unit, callback: (BankResponse) -> Unit) { // TODO: use response from DialogContext if (response.isStrongAuthenticationRequired) { - if (dialogContext.abortIfTanIsRequired) { + if (context.dialog.abortIfTanIsRequired) { response.tanRequiredButWeWereToldToAbortIfSo = true callback(response) @@ -188,12 +189,12 @@ open class RequestExecutor( } - protected open fun addMessageLog(message: String, type: MessageLogEntryType, dialogContext: DialogContext) { - messageLogCollector.addMessageLog(message, type, dialogContext.bank) + protected open fun addMessageLog(context: JobContext, type: MessageLogEntryType, message: String) { + messageLogCollector.addMessageLog(message, type, context.bank) } - protected open fun logError(message: String, dialogContext: DialogContext, e: Exception?) { - messageLogAppender.logError(message, e, log, dialogContext.bank) + protected open fun logError(context: JobContext, message: String, e: Exception?) { + messageLogAppender.logError(message, e, log, context.bank) } } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt index 9ba8f85c..00f29bb9 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt @@ -58,24 +58,24 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg übermittelt werden, wenn das Kreditinstitut dies unterstützt. (PinTan S. 35) */ - open fun createAnonymousDialogInitMessage(dialogContext: DialogContext): MessageBuilderResult { + open fun createAnonymousDialogInitMessage(context: JobContext): MessageBuilderResult { - return createUnsignedMessageBuilderResult(dialogContext, listOf( - IdentifikationsSegment(generator.resetSegmentNumber(1), dialogContext), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext) + return createUnsignedMessageBuilderResult(context.dialog, listOf( + IdentifikationsSegment(generator.resetSegmentNumber(1), context.dialog), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), context.dialog) )) } - open fun createAnonymousDialogEndMessage(dialogContext: DialogContext): MessageBuilderResult { + open fun createAnonymousDialogEndMessage(context: JobContext): MessageBuilderResult { - return createUnsignedMessageBuilderResult(dialogContext, listOf( - Dialogende(generator.resetSegmentNumber(1), dialogContext) + return createUnsignedMessageBuilderResult(context.dialog, listOf( + Dialogende(generator.resetSegmentNumber(1), context.dialog) )) } - open fun createInitDialogMessage(dialogContext: DialogContext): MessageBuilderResult { - return createInitDialogMessage(dialogContext, null) + open fun createInitDialogMessage(context: JobContext): MessageBuilderResult { + return createInitDialogMessage(context, null) } /** @@ -112,61 +112,63 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg (PinTan S. 37/38) */ - open fun createInitDialogMessageWithoutStrongCustomerAuthentication(dialogContext: DialogContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { - return createInitDialogMessage(dialogContext, segmentIdForTwoStepTanProcess) + open fun createInitDialogMessageWithoutStrongCustomerAuthentication(context: JobContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { + return createInitDialogMessage(context, segmentIdForTwoStepTanProcess) } - protected open fun createInitDialogMessage(dialogContext: DialogContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { + protected open fun createInitDialogMessage(context: JobContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { + val dialog = context.dialog val segments = mutableListOf( - IdentifikationsSegment(generator.resetSegmentNumber(2), dialogContext), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext) + IdentifikationsSegment(generator.resetSegmentNumber(2), dialog), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialog) ) if (segmentIdForTwoStepTanProcess != null) { - segments.add(createTwoStepTanSegment(segmentIdForTwoStepTanProcess, dialogContext)) + segments.add(createTwoStepTanSegment(segmentIdForTwoStepTanProcess, dialog)) } - else if (dialogContext.bank.isTanMethodSelected) { - segments.add(createTwoStepTanSegment(CustomerSegmentId.Identification, dialogContext)) + else if (context.bank.isTanMethodSelected) { + segments.add(createTwoStepTanSegment(CustomerSegmentId.Identification, dialog)) } - if (dialogContext.bank.customerSystemId == KundensystemID.Anonymous) { + if (context.bank.customerSystemId == KundensystemID.Anonymous) { segments.add(Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden)) } - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(dialog, segments) } - open fun createSynchronizeCustomerSystemIdMessage(dialogContext: DialogContext): MessageBuilderResult { + open fun createSynchronizeCustomerSystemIdMessage(context: JobContext): MessageBuilderResult { + val dialog = context.dialog - return createSignedMessageBuilderResult(dialogContext, listOf( - IdentifikationsSegment(generator.resetSegmentNumber(2), dialogContext), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext), - createTwoStepTanSegment(CustomerSegmentId.Identification, dialogContext), + return createSignedMessageBuilderResult(dialog, listOf( + IdentifikationsSegment(generator.resetSegmentNumber(2), dialog), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialog), + createTwoStepTanSegment(CustomerSegmentId.Identification, dialog), Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden) )) } - open fun createDialogEndMessage(dialogContext: DialogContext): MessageBuilderResult { + open fun createDialogEndMessage(context: JobContext): MessageBuilderResult { - return createSignedMessageBuilderResult(dialogContext, listOf( - Dialogende(generator.resetSegmentNumber(2), dialogContext) + return createSignedMessageBuilderResult(context.dialog, listOf( + Dialogende(generator.resetSegmentNumber(2), context.dialog) )) } - open fun createGetTransactionsMessage(parameter: GetTransactionsParameter, dialogContext: DialogContext): MessageBuilderResult { + open fun createGetTransactionsMessage(context: JobContext, parameter: GetTransactionsParameter): MessageBuilderResult { val result = supportsGetTransactionsMt940(parameter.account) if (result.isJobVersionSupported) { - return createGetTransactionsMessageMt940(result, parameter, dialogContext) + return createGetTransactionsMessageMt940(result, parameter, context.dialog) } val creditCardResult = supportsGetCreditCardTransactions(parameter.account) if (creditCardResult.isJobVersionSupported) { - return createGetCreditCardTransactionsMessage(result, parameter, dialogContext) + return createGetCreditCardTransactionsMessage(result, parameter, context.dialog) } return result @@ -220,19 +222,19 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - open fun createGetBalanceMessage(account: AccountData, dialogContext: DialogContext): MessageBuilderResult { + open fun createGetBalanceMessage(context: JobContext, account: AccountData): MessageBuilderResult { val result = supportsGetBalanceMessage(account) if (result.isJobVersionSupported) { val balanceJob = if (result.isAllowed(5)) SaldenabfrageVersion5(generator.resetSegmentNumber(2), account) - else SaldenabfrageVersion7(generator.resetSegmentNumber(2), account, dialogContext.bank) + else SaldenabfrageVersion7(generator.resetSegmentNumber(2), account, context.bank) val segments = mutableListOf(balanceJob) - addTanSegmentIfRequired(CustomerSegmentId.Balance, dialogContext, segments) + addTanSegmentIfRequired(CustomerSegmentId.Balance, context.dialog, segments) - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(context.dialog, segments) } return result @@ -247,11 +249,11 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - open fun createGetTanMediaListMessage(dialogContext: DialogContext, + open fun createGetTanMediaListMessage(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): MessageBuilderResult { - val result = getSupportedVersionsOfJobForBank(CustomerSegmentId.TanMediaList, dialogContext.bank, listOf(2, 3, 4, 5)) + val result = getSupportedVersionsOfJobForBank(CustomerSegmentId.TanMediaList, context.bank, listOf(2, 3, 4, 5)) if (result.isJobVersionSupported) { val segments = listOf( @@ -259,31 +261,31 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg generator.resetSegmentNumber(2), tanMediaKind, tanMediumClass) ) - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(context.dialog, segments) } return result } // TODO: no HKTAN needed? - open fun createChangeTanMediumMessage(newActiveTanMedium: TanGeneratorTanMedium, dialogContext: DialogContext, + open fun createChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, tan: String? = null, atc: Int? = null): MessageBuilderResult { - val result = getSupportedVersionsOfJobForBank(CustomerSegmentId.ChangeTanMedium, dialogContext.bank, listOf(1, 2, 3)) + val result = getSupportedVersionsOfJobForBank(CustomerSegmentId.ChangeTanMedium, context.bank, listOf(1, 2, 3)) if (result.isJobVersionSupported) { val segments = listOf( TanGeneratorTanMediumAnOderUmmelden(result.getHighestAllowedVersion!!, generator.resetSegmentNumber(2), - dialogContext.bank, newActiveTanMedium, tan, atc) + context.bank, newActiveTanMedium, tan, atc) ) - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(context.dialog, segments) } return result } - open fun createSendEnteredTanMessage(enteredTan: String, tanResponse: TanResponse, dialogContext: DialogContext): MessageBuilderResult { + open fun createSendEnteredTanMessage(context: JobContext, enteredTan: String, tanResponse: TanResponse): MessageBuilderResult { val tanProcess = if (tanResponse.tanProcess == TanProcess.TanProcess1) TanProcess.TanProcess1 else TanProcess.TanProcess2 @@ -292,23 +294,23 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg tanResponse.jobHashValue, tanResponse.jobReference, false, null, tanResponse.tanMediaIdentifier) ) - return createSignedMessageBuilderResult(createSignedMessage(dialogContext, enteredTan, segments), dialogContext, segments) + return createSignedMessageBuilderResult(createSignedMessage(context.dialog, enteredTan, segments), context.dialog, segments) } - open fun createBankTransferMessage(data: BankTransferData, account: AccountData, dialogContext: DialogContext): MessageBuilderResult { + open fun createBankTransferMessage(context: JobContext, data: BankTransferData, account: AccountData): MessageBuilderResult { val segmentId = if (data.realTimeTransfer) CustomerSegmentId.SepaRealTimeTransfer else CustomerSegmentId.SepaBankTransfer - val (result, urn) = supportsBankTransferAndSepaVersion(dialogContext.bank, account, segmentId) + val (result, urn) = supportsBankTransferAndSepaVersion(context.bank, account, segmentId) if (result.isJobVersionSupported && urn != null) { val segments = mutableListOf(SepaBankTransferBase(segmentId, generator.resetSegmentNumber(2), - urn, dialogContext.bank.customerName, account, dialogContext.bank.bic, data)) + urn, context.bank.customerName, account, context.bank.bic, data)) - addTanSegmentIfRequired(segmentId, dialogContext, segments) + addTanSegmentIfRequired(segmentId, context.dialog, segments) - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(context.dialog, segments) } return result @@ -342,7 +344,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - open fun rebuildMessageWithContinuationId(message: MessageBuilderResult, continuationId: String, dialogContext: DialogContext): MessageBuilderResult? { + open fun rebuildMessageWithContinuationId(context: JobContext, message: MessageBuilderResult, continuationId: String): MessageBuilderResult? { // val copiedSegments = message.messageBodySegments.map { } val aufsetzpunkte = message.messageBodySegments.flatMap { it.dataElementsAndGroups }.filterIsInstance() @@ -354,12 +356,12 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg aufsetzpunkte.forEach { it.resetContinuationId(continuationId) } - return rebuildMessage(message, dialogContext) + return rebuildMessage(context, message) } - open fun rebuildMessage(message: MessageBuilderResult, dialogContext: DialogContext): MessageBuilderResult { + open fun rebuildMessage(context: JobContext, message: MessageBuilderResult): MessageBuilderResult { - return createSignedMessageBuilderResult(dialogContext, message.messageBodySegments) + return createSignedMessageBuilderResult(context.dialog, message.messageBodySegments) } protected open fun createSignedMessageBuilderResult(dialogContext: DialogContext, segments: List): MessageBuilderResult { @@ -518,7 +520,8 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } protected open fun createTwoStepTanSegment(segmentId: CustomerSegmentId, dialogContext: DialogContext): ZweiSchrittTanEinreichung { - return ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, segmentId, tanMediaIdentifier = getTanMediaIdentifierIfRequired(dialogContext)) + return ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, segmentId, + tanMediaIdentifier = getTanMediaIdentifierIfRequired(dialogContext)) } protected open fun getTanMediaIdentifierIfRequired(dialogContext: DialogContext): String? { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt new file mode 100644 index 00000000..aad94f43 --- /dev/null +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt @@ -0,0 +1,24 @@ +package net.dankito.banking.fints.model + +import net.dankito.banking.fints.callback.FinTsClientCallback + + +class JobContext( + val type: JobContextType, + val callback: FinTsClientCallback, + val bank: BankData, + /** + * Only set if the current context is for a specific account (like get account's transactions). + */ + val account: AccountData? = null +) { + + + lateinit var dialog: DialogContext + + + fun startNewDialog(dialog: DialogContext) { + this.dialog = dialog + } + +} \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContextType.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContextType.kt new file mode 100644 index 00000000..e54f02a3 --- /dev/null +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContextType.kt @@ -0,0 +1,18 @@ +package net.dankito.banking.fints.model + + +enum class JobContextType { + + AnonymousBankInfo, + + GetTanMedia, + + ChangeTanMedium, + + AddAccount, + + GetTransactions, + + TransferMoney + +} \ No newline at end of file diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt index a3fb495b..633a71e1 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt @@ -1,5 +1,6 @@ package net.dankito.banking.fints +import net.dankito.banking.fints.callback.SimpleFinTsClientCallback import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache @@ -61,6 +62,15 @@ abstract class FinTsTestBase { } + protected open fun createContext(dialogId: String = DialogContext.InitialDialogId): JobContext { + val dialogContext = DialogContext(Bank, Product, dialogId = dialogId) + + val context = JobContext(JobContextType.AnonymousBankInfo, SimpleFinTsClientCallback(), Bank) + context.startNewDialog(dialogContext) + + return context + } + protected open fun createDialogId(): String { return UUID.random().replace("-", "") } diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt index 8230f9d8..e944fbd9 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt @@ -47,10 +47,10 @@ class MessageBuilderTest : FinTsTestBase() { fun createAnonymousDialogInitMessage() { // given - val dialogContext = DialogContext(Bank, Product) + val context = createContext() // when - val result = underTest.createAnonymousDialogInitMessage(dialogContext).createdMessage + val result = underTest.createAnonymousDialogInitMessage(context).createdMessage // then expect(result).toBe( @@ -66,10 +66,10 @@ class MessageBuilderTest : FinTsTestBase() { // given val dialogId = createDialogId() - val dialogContext = DialogContext(Bank, Product, dialogId = dialogId) + val context = createContext(dialogId) // when - val result = underTest.createAnonymousDialogEndMessage(dialogContext).createdMessage ?: "" + val result = underTest.createAnonymousDialogEndMessage(context).createdMessage ?: "" // then expect(normalizeBinaryData(result)).toBe(normalizeBinaryData( @@ -84,10 +84,10 @@ class MessageBuilderTest : FinTsTestBase() { fun createDialogInitMessage() { // given - val dialogContext = DialogContext(Bank, Product) + val context = createContext() // when - val result = underTest.createSynchronizeCustomerSystemIdMessage(dialogContext).createdMessage ?: "" + val result = underTest.createSynchronizeCustomerSystemIdMessage(context).createdMessage ?: "" // then expect(normalizeBinaryData(result)).toBe(normalizeBinaryData( @@ -108,10 +108,10 @@ class MessageBuilderTest : FinTsTestBase() { // given val dialogId = createDialogId() - val dialogContext = DialogContext(Bank, Product, dialogId = dialogId) + val context = createContext(dialogId) // when - val result = underTest.createDialogEndMessage(dialogContext).createdMessage ?: "" + val result = underTest.createDialogEndMessage(context).createdMessage ?: "" // then expect(normalizeBinaryData(result)).toBe(normalizeBinaryData( @@ -129,10 +129,10 @@ class MessageBuilderTest : FinTsTestBase() { fun createGetTransactionsMessage_JobIsNotAllowed() { // given - val dialogContext = DialogContext(Bank, Product) + val context = createContext() // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(Account), dialogContext) + val result = underTest.createGetTransactionsMessage(context, GetTransactionsParameter(Account)) // then expect(result.isJobAllowed).toBe(false) @@ -147,10 +147,11 @@ class MessageBuilderTest : FinTsTestBase() { Bank.supportedJobs = listOf(getTransactionsJob) val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJobWithPreviousVersion)) Bank.addAccount(account) - val dialogContext = DialogContext(Bank, Product) + + val context = createContext() // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(account), dialogContext) + val result = underTest.createGetTransactionsMessage(context, GetTransactionsParameter(account)) // then expect(result.isJobAllowed).toBe(true) @@ -166,14 +167,15 @@ class MessageBuilderTest : FinTsTestBase() { Bank.pinInfo = PinInfo(getTransactionsJob, null, null, null, null, null, listOf(JobTanConfiguration(CustomerSegmentId.AccountTransactionsMt940.id, true))) val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob)) Bank.addAccount(account) - val dialogContext = DialogContext(Bank, Product) + + val context = createContext() val fromDate = Date(2019, Month.August, 6) val toDate = Date(2019, Month.October, 21) val maxCountEntries = 99 // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(account, false, fromDate, toDate, maxCountEntries), dialogContext) + val result = underTest.createGetTransactionsMessage(context, GetTransactionsParameter(account, false, fromDate, toDate, maxCountEntries)) // then expect(result.createdMessage).notToBeNull() @@ -198,7 +200,8 @@ class MessageBuilderTest : FinTsTestBase() { Bank.pinInfo = PinInfo(getTransactionsJob, null, null, null, null, null, listOf(JobTanConfiguration(CustomerSegmentId.AccountTransactionsMt940.id, true))) val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob)) Bank.addAccount(account) - val dialogContext = DialogContext(Bank, Product) + + val context = createContext() val fromDate = Date(2019, Month.August, 6) val toDate = Date(2019, Month.October, 21) @@ -206,7 +209,8 @@ class MessageBuilderTest : FinTsTestBase() { val continuationId = "9345-10-26-11.52.15.693455" // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(account, false, fromDate, toDate, maxCountEntries, false), dialogContext) // TODO: test Aufsetzpunkt / continuationId + val result = underTest.createGetTransactionsMessage(context, // TODO: test Aufsetzpunkt / continuationId + GetTransactionsParameter(account, false, fromDate, toDate, maxCountEntries, false)) // then expect(result.createdMessage).notToBeNull() diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt b/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt index e2753474..2efc877d 100644 --- a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt +++ b/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt @@ -13,6 +13,7 @@ import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.SmsAb import net.dankito.banking.fints.model.* import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.fints.FinTsJobExecutor +import net.dankito.banking.fints.callback.SimpleFinTsClientCallback import net.dankito.banking.fints.model.mapper.ModelMapper import net.dankito.banking.fints.response.BankResponse import net.dankito.banking.fints.response.segments.SepaAccountInfoParameters @@ -57,10 +58,10 @@ class BanksFinTsDetailsRetriever { } - private val jobExecutor = object : FinTsJobExecutor(NoOpFinTsClientCallback(), modelMapper = modelMapper) { + private val jobExecutor = object : FinTsJobExecutor(modelMapper = modelMapper) { - fun getAndHandleResponseForMessagePublic(message: MessageBuilderResult, dialogContext: DialogContext, callback: (BankResponse) -> Unit) { - getAndHandleResponseForMessage(message, dialogContext, callback) + fun getAndHandleResponseForMessagePublic(context: JobContext, message: MessageBuilderResult, callback: (BankResponse) -> Unit) { + getAndHandleResponseForMessage(context, message, callback) } } @@ -127,12 +128,15 @@ class BanksFinTsDetailsRetriever { private fun getAnonymousBankInfo(bank: BankData): BankResponse { val dialogContext = DialogContext(bank, product) - val requestBody = messageBuilder.createAnonymousDialogInitMessage(dialogContext) + val context = JobContext(JobContextType.AnonymousBankInfo, SimpleFinTsClientCallback(), bank) + context.startNewDialog(dialogContext) + + val requestBody = messageBuilder.createAnonymousDialogInitMessage(context) val anonymousBankInfoResponse = AtomicReference() val countDownLatch = CountDownLatch(1) - jobExecutor.getAndHandleResponseForMessagePublic(requestBody, dialogContext) { + jobExecutor.getAndHandleResponseForMessagePublic(context, requestBody) { anonymousBankInfoResponse.set(it) countDownLatch.countDown() }