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

This commit is contained in:
dankito 2021-11-12 20:36:49 +01:00
parent d5573817ef
commit 0a9b31b393
10 changed files with 367 additions and 282 deletions

View File

@ -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<MessageLogEntry>
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<AccountData, RetrievedAccountData>,
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)
}
}

View File

@ -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<MessageLogEntry>
get() = client.messageLogWithoutSensitiveData
open fun setCallback(callback: FinTsClientCallback) {
client.setCallback(callback)
client.callback = callback
}

View File

@ -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<TanMethodType>? = null, preferredTanMedium: String? = null,
open fun retrieveBasicDataLikeUsersTanMethods(context: JobContext, preferredTanMethods: List<TanMethodType>? = 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<BalanceSegment>(InstituteSegmentId.Balance)?.let {
Money(it.balance, it.currency)
@ -226,16 +234,16 @@ open class FinTsJobExecutor(
val bookedTransactions = mutableSetOf<AccountTransaction>()
val unbookedTransactions = mutableSetOf<Any>()
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<ReceivedAccountTransactions>(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<Boolean>, dialogContext: DialogContext
protected open fun mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, tanResponse: TanResponse,
userDidCancelEnteringTan: ObjectReference<Boolean>
) {
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<Boolean>, dialogContext: DialogContext) {
protected open fun automaticallyRetrieveIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, userDidCancelEnteringTan: ObjectReference<Boolean>) {
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<TanMethodType>? = null, done: (Boolean) -> Unit) {
open fun getUsersTanMethod(context: JobContext, preferredTanMethods: List<TanMethodType>? = 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)
}
}

View File

@ -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)
}
}

View File

@ -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<Segment>(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<Segment>(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<Aufsetzpunkt>()
@ -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<Segment>): 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? {

View File

@ -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
}
}

View File

@ -0,0 +1,18 @@
package net.dankito.banking.fints.model
enum class JobContextType {
AnonymousBankInfo,
GetTanMedia,
ChangeTanMedium,
AddAccount,
GetTransactions,
TransferMoney
}

View File

@ -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("-", "")
}

View File

@ -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()

View File

@ -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<BankResponse>()
val countDownLatch = CountDownLatch(1)
jobExecutor.getAndHandleResponseForMessagePublic(requestBody, dialogContext) {
jobExecutor.getAndHandleResponseForMessagePublic(context, requestBody) {
anonymousBankInfoResponse.set(it)
countDownLatch.countDown()
}