Made IWebClient and FinTsClient async so that on iOS everything but the web request can run on the main thread -> we don't need to freeze objects to pass them between threads

This commit is contained in:
dankito 2020-07-15 21:40:06 +02:00
parent 27dd8c98cc
commit 8e221f17ce
5 changed files with 375 additions and 327 deletions

View File

@ -74,7 +74,7 @@ open class FinTsClient(
open fun getAnonymousBankInfoAsync(bank: BankData, callback: (FinTsClientResponse) -> Unit) { open fun getAnonymousBankInfoAsync(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
GlobalScope.launch { GlobalScope.launch {
callback(getAnonymousBankInfo(bank)) getAnonymousBankInfo(bank, callback)
} }
} }
@ -84,20 +84,20 @@ open class FinTsClient(
* *
* On success [bank] parameter is updated afterwards. * On success [bank] parameter is updated afterwards.
*/ */
open fun getAnonymousBankInfo(bank: BankData): FinTsClientResponse { open fun getAnonymousBankInfo(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
val dialogContext = DialogContext(bank, CustomerData.Anonymous, product) val dialogContext = DialogContext(bank, CustomerData.Anonymous, product)
val message = messageBuilder.createAnonymousDialogInitMessage(dialogContext) val message = messageBuilder.createAnonymousDialogInitMessage(dialogContext)
val response = getAndHandleResponseForMessage(message, dialogContext) getAndHandleResponseForMessage(message, dialogContext) { response ->
if (response.successful) { if (response.successful) {
updateBankData(bank, response) updateBankData(bank, response)
closeAnonymousDialog(dialogContext, response) closeAnonymousDialog(dialogContext, response)
} }
return FinTsClientResponse(response) callback(FinTsClientResponse(response))
}
} }
protected open fun closeAnonymousDialog(dialogContext: DialogContext, response: Response) { protected open fun closeAnonymousDialog(dialogContext: DialogContext, response: Response) {
@ -113,7 +113,7 @@ open class FinTsClient(
} }
open fun getBankAndCustomerInfoForNewUser(bank: BankData, customer: CustomerData): AddAccountResponse { open fun getBankAndCustomerInfoForNewUser(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) {
// just to ensure settings are in its initial state and that bank sends use bank parameter (BPD), // just to ensure settings are in its initial state and that bank sends use bank parameter (BPD),
// user parameter (UPD) and allowed tan procedures for user (therefore the resetSelectedTanProcedure()) // user parameter (UPD) and allowed tan procedures for user (therefore the resetSelectedTanProcedure())
bank.resetBpdVersion() bank.resetBpdVersion()
@ -128,16 +128,17 @@ open class FinTsClient(
val dialogContext = DialogContext(bank, customer, product) val dialogContext = DialogContext(bank, customer, product)
val initDialogResponse = initDialogAfterSuccessfulChecks(dialogContext) initDialogAfterSuccessfulChecks(dialogContext) { initDialogResponse ->
closeDialog(dialogContext) closeDialog(dialogContext)
// even though it is required by specification some banks don't support retrieving user's TAN procedure by setting TAN procedure to '999' // even though it is required by specification some banks don't support retrieving user's TAN procedure by setting TAN procedure to '999'
if (bankDoesNotSupportRetrievingUsersTanProcedures(initDialogResponse)) { if (bankDoesNotSupportRetrievingUsersTanProcedures(initDialogResponse)) {
return getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank, customer) getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank, customer, callback)
}
else {
callback(AddAccountResponse(initDialogResponse, bank, customer))
}
} }
return AddAccountResponse(initDialogResponse, bank, customer)
} }
protected open fun bankDoesNotSupportRetrievingUsersTanProcedures(response: Response): Boolean { protected open fun bankDoesNotSupportRetrievingUsersTanProcedures(response: Response): Boolean {
@ -147,27 +148,28 @@ open class FinTsClient(
} }
// TODO: this is only a quick fix. Find a better and general solution // TODO: this is only a quick fix. Find a better and general solution
protected open fun getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank: BankData, customer: CustomerData): AddAccountResponse { protected open fun getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) {
val anonymousBankInfoResponse = getAnonymousBankInfo(bank) getAnonymousBankInfo(bank) { anonymousBankInfoResponse ->
if (anonymousBankInfoResponse.isSuccessful == false) { if (anonymousBankInfoResponse.isSuccessful == false) {
return AddAccountResponse(anonymousBankInfoResponse.toResponse(), bank, customer) callback(AddAccountResponse(anonymousBankInfoResponse.toResponse(), bank, customer))
} }
else if (bank.supportedTanProcedures.isEmpty()) { // should only be a theoretical error else if (bank.supportedTanProcedures.isEmpty()) { // should only be a theoretical error
return AddAccountResponse(Response(true, callback(AddAccountResponse(Response(true,
exception = Exception("Die TAN Verfahren der Bank konnten nicht ermittelt werden")), bank, customer) // TODO: translate exception = Exception("Die TAN Verfahren der Bank konnten nicht ermittelt werden")), bank, customer)) // TODO: translate
} }
else {
customer.supportedTanProcedures = bank.supportedTanProcedures customer.supportedTanProcedures = bank.supportedTanProcedures
getUsersTanProcedure(customer) getUsersTanProcedure(customer)
val dialogContext = DialogContext(bank, customer, product) val dialogContext = DialogContext(bank, customer, product)
val initDialogResponse = initDialogAfterSuccessfulChecks(dialogContext) initDialogAfterSuccessfulChecks(dialogContext) { initDialogResponse ->
closeDialog(dialogContext) closeDialog(dialogContext)
return AddAccountResponse(initDialogResponse, bank, customer) callback(AddAccountResponse(initDialogResponse, bank, customer))
}
}
}
} }
@ -182,13 +184,12 @@ open class FinTsClient(
* *
* If you change customer system id during a dialog your messages get rejected by bank institute. * If you change customer system id during a dialog your messages get rejected by bank institute.
*/ */
protected open fun synchronizeCustomerSystemId(bank: BankData, customer: CustomerData): FinTsClientResponse { protected open fun synchronizeCustomerSystemId(bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) {
val dialogContext = DialogContext(bank, customer, product) val dialogContext = DialogContext(bank, customer, product)
val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(dialogContext) val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(dialogContext)
val response = getAndHandleResponseForMessage(message, dialogContext) getAndHandleResponseForMessage(message, dialogContext) { response ->
if (response.successful) { if (response.successful) {
updateBankData(bank, response) updateBankData(bank, response)
updateCustomerData(customer, bank, response) updateCustomerData(customer, bank, response)
@ -196,7 +197,8 @@ open class FinTsClient(
closeDialog(dialogContext) closeDialog(dialogContext)
} }
return FinTsClientResponse(response) callback(FinTsClientResponse(response))
}
} }
@ -204,21 +206,21 @@ open class FinTsClient(
callback: (AddAccountResponse) -> Unit) { callback: (AddAccountResponse) -> Unit) {
GlobalScope.launch { GlobalScope.launch {
callback(addAccount(bank, customer)) addAccount(bank, customer, callback)
} }
} }
open fun addAccount(bank: BankData, customer: CustomerData): AddAccountResponse { open fun addAccount(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) {
val originalAreWeThatGentleToCloseDialogs = areWeThatGentleToCloseDialogs val originalAreWeThatGentleToCloseDialogs = areWeThatGentleToCloseDialogs
areWeThatGentleToCloseDialogs = false areWeThatGentleToCloseDialogs = false
/* First dialog: Get user's basic data like her TAN procedures */ /* First dialog: Get user's basic data like her TAN procedures */
val newUserInfoResponse = getBankAndCustomerInfoForNewUser(bank, customer) getBankAndCustomerInfoForNewUser(bank, customer) { newUserInfoResponse ->
if (newUserInfoResponse.isSuccessful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong if (newUserInfoResponse.isSuccessful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
return newUserInfoResponse callback(newUserInfoResponse)
} }
@ -241,7 +243,7 @@ open class FinTsClient(
/* Third dialog: Get customer TAN media list - last step that can and must be done without strong customer authorization */ /* Third dialog: Get customer TAN media list - last step that can and must be done without strong customer authorization */
getTanMediaList(bank, customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) getTanMediaList(bank, customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) {
/* Fourth dialog: Try to retrieve account transactions of last 90 days without TAN */ /* Fourth dialog: Try to retrieve account transactions of last 90 days without TAN */
@ -249,14 +251,31 @@ open class FinTsClient(
// also check if retrieving account transactions of last 90 days without tan is supported (and thereby may retrieve first account transactions) // also check if retrieving account transactions of last 90 days without tan is supported (and thereby may retrieve first account transactions)
val transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>() val transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>()
val balances = mutableMapOf<AccountData, Money>() val balances = mutableMapOf<AccountData, Money>()
val countAccountSupportingRetrievingTransactions = customer.accounts.filter { it.supportsFeature(AccountFeature.RetrieveAccountTransactions) }.size
var countRetrievedAccounts = 0
customer.accounts.forEach { account -> customer.accounts.forEach { account ->
if (account.supportsFeature(AccountFeature.RetrieveAccountTransactions)) { if (account.supportsFeature(AccountFeature.RetrieveAccountTransactions)) {
val response = tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false) tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false) { response ->
transactionsOfLast90DaysResponses.add(response) transactionsOfLast90DaysResponses.add(response)
response.balance?.let { balances.put(account, it) } response.balance?.let { balances.put(account, it) }
countRetrievedAccounts++
if (countRetrievedAccounts == countAccountSupportingRetrievingTransactions) {
addAccountAfterRetrievingTransactions(bank, customer, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure, originalAreWeThatGentleToCloseDialogs,
transactionsOfLast90DaysResponses, balances, callback)
}
}
}
}
}
} }
} }
protected open fun addAccountAfterRetrievingTransactions(bank: BankData, customer: CustomerData, newUserInfoResponse: AddAccountResponse,
didOverwriteUserUnselectedTanProcedure: Boolean, originalAreWeThatGentleToCloseDialogs: Boolean,
transactionsOfLast90DaysResponses: MutableList<GetTransactionsResponse>,
balances: MutableMap<AccountData, Money>, callback: (AddAccountResponse) -> Unit) {
if (didOverwriteUserUnselectedTanProcedure) { if (didOverwriteUserUnselectedTanProcedure) {
customer.resetSelectedTanProcedure() customer.resetSelectedTanProcedure()
} }
@ -267,8 +286,8 @@ open class FinTsClient(
val unbookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.unbookedTransactions } val unbookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.unbookedTransactions }
val bookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.bookedTransactions } val bookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.bookedTransactions }
return AddAccountResponse(newUserInfoResponse.toResponse(), bank, customer, callback(AddAccountResponse(newUserInfoResponse.toResponse(), bank, customer,
supportsRetrievingTransactionsOfLast90DaysWithoutTan, bookedTransactions, unbookedTransactions, balances) supportsRetrievingTransactionsOfLast90DaysWithoutTan, bookedTransactions, unbookedTransactions, balances))
} }
@ -278,19 +297,17 @@ open class FinTsClient(
* *
* Check if bank supports this. * Check if bank supports this.
*/ */
open fun tryGetTransactionsOfLast90DaysWithoutTan(bank: BankData, customer: CustomerData, account: AccountData): GetTransactionsResponse { open fun tryGetTransactionsOfLast90DaysWithoutTan(bank: BankData, customer: CustomerData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
return tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false) tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false, callback)
} }
protected open fun tryGetTransactionsOfLast90DaysWithoutTan(bank: BankData, customer: CustomerData, account: AccountData, protected open fun tryGetTransactionsOfLast90DaysWithoutTan(bank: BankData, customer: CustomerData, account: AccountData,
hasRetrievedTransactionsWithTanJustBefore: Boolean): GetTransactionsResponse { hasRetrievedTransactionsWithTanJustBefore: Boolean, callback: (GetTransactionsResponse) -> Unit) {
val now = Date() val now = Date()
val ninetyDaysAgo = Date(now.millisSinceEpoch - NinetyDaysMillis) val ninetyDaysAgo = Date(now.millisSinceEpoch - NinetyDaysMillis)
val response = getTransactions(GetTransactionsParameter(account.supportsFeature(AccountFeature.RetrieveBalance), ninetyDaysAgo, abortIfTanIsRequired = true), bank, customer, account) getTransactions(GetTransactionsParameter(account.supportsFeature(AccountFeature.RetrieveBalance), ninetyDaysAgo, abortIfTanIsRequired = true), bank, customer, account) { response ->
account.triedToRetrieveTransactionsOfLast90DaysWithoutTan = true account.triedToRetrieveTransactionsOfLast90DaysWithoutTan = true
if (response.isSuccessful) { if (response.isSuccessful) {
@ -300,44 +317,47 @@ open class FinTsClient(
} }
} }
return response callback(response)
}
} }
open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData, open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData,
customer: CustomerData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) { customer: CustomerData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
GlobalScope.launch { GlobalScope.launch {
callback(getTransactions(parameter, bank, customer, account)) getTransactions(parameter, bank, customer, account, callback)
} }
} }
open fun getTransactions(parameter: GetTransactionsParameter, bank: BankData, open fun getTransactions(parameter: GetTransactionsParameter, bank: BankData,
customer: CustomerData, account: AccountData): GetTransactionsResponse { customer: CustomerData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
val dialogContext = DialogContext(bank, customer, product) val dialogContext = DialogContext(bank, customer, product)
val initDialogResponse = initDialog(dialogContext) initDialog(dialogContext) { initDialogResponse ->
if (initDialogResponse.successful == false) { if (initDialogResponse.successful == false) {
return GetTransactionsResponse(initDialogResponse) callback(GetTransactionsResponse(initDialogResponse))
} }
else {
mayGetBalance(parameter, account, dialogContext) { balanceResponse ->
var balance: Money? = null
if (parameter.alsoRetrieveBalance && account.supportsFeature(AccountFeature.RetrieveBalance)) {
val balanceResponse = getBalanceAfterDialogInit(account, dialogContext)
if (balanceResponse.successful == false && balanceResponse.couldCreateMessage == true) { // don't break here if required HKSAL message is not implemented if (balanceResponse.successful == false && balanceResponse.couldCreateMessage == true) { // don't break here if required HKSAL message is not implemented
closeDialog(dialogContext) closeDialog(dialogContext)
return GetTransactionsResponse(balanceResponse) callback(GetTransactionsResponse(balanceResponse))
}
else {
getTransactionsAfterInitAndGetBalance(parameter, account, dialogContext, balanceResponse, callback)
}
}
} }
balanceResponse.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
balance = Money(it.balance, it.currency)
} }
} }
protected open fun getTransactionsAfterInitAndGetBalance(parameter: GetTransactionsParameter, account: AccountData, dialogContext: DialogContext,
balanceResponse: Response, callback: (GetTransactionsResponse) -> Unit) {
val balance: Money? = balanceResponse.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
Money(it.balance, it.currency)
}
val message = messageBuilder.createGetTransactionsMessage(parameter, account, dialogContext) val message = messageBuilder.createGetTransactionsMessage(parameter, account, dialogContext)
@ -357,28 +377,34 @@ open class FinTsClient(
} }
} }
val response = getAndHandleResponseForMessage(message, dialogContext) getAndHandleResponseForMessage(message, dialogContext) { response ->
closeDialog(dialogContext) closeDialog(dialogContext)
// just retrieved all transactions -> check if retrieving that ones of last 90 days is possible without entering TAN // just retrieved all transactions -> check if retrieving that ones of last 90 days is possible without entering TAN
if (account.supportsRetrievingTransactionsOfLast90DaysWithoutTan == null && if (account.supportsRetrievingTransactionsOfLast90DaysWithoutTan == null &&
response.successful && bookedTransactions.isNotEmpty() && parameter.fromDate == null) { response.successful && bookedTransactions.isNotEmpty() && parameter.fromDate == null) {
tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, true) tryGetTransactionsOfLast90DaysWithoutTan(dialogContext.bank, dialogContext.customer, account, true) { }
} }
return GetTransactionsResponse(response, callback(GetTransactionsResponse(
response,
bookedTransactions, bookedTransactions,
listOf(), // TODO: implement parsing MT942 listOf(), // TODO: implement parsing MT942
balance) balance
)
)
}
} }
protected open fun getBalanceAfterDialogInit(account: AccountData, dialogContext: DialogContext): Response { protected open fun mayGetBalance(parameter: GetTransactionsParameter, account: AccountData, dialogContext: DialogContext, callback: (Response) -> Unit) {
if (parameter.alsoRetrieveBalance && account.supportsFeature(AccountFeature.RetrieveBalance)) {
val message = messageBuilder.createGetBalanceMessage(account, dialogContext) val message = messageBuilder.createGetBalanceMessage(account, dialogContext)
return getAndHandleResponseForMessage(message, dialogContext) getAndHandleResponseForMessage(message, dialogContext) { response ->
callback(response)
}
}
} }
@ -388,17 +414,16 @@ open class FinTsClient(
callback: (GetTanMediaListResponse) -> Unit) { callback: (GetTanMediaListResponse) -> Unit) {
GlobalScope.launch { GlobalScope.launch {
callback(getTanMediaList(bank, customer)) getTanMediaList(bank, customer, tanMediaKind, tanMediumClass, callback)
} }
} }
open fun getTanMediaList(bank: BankData, customer: CustomerData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, open fun getTanMediaList(bank: BankData, customer: CustomerData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): GetTanMediaListResponse { tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) {
val response = sendMessageAndHandleResponse(bank, customer) { dialogContext -> sendMessageAndHandleResponse(bank, customer, true, { dialogContext ->
messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass) messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass)
} }) { response ->
// TAN media list (= TAN generator list) is only returned for users with chipTAN TAN procedures // TAN media list (= TAN generator list) is only returned for users with chipTAN TAN procedures
val tanMediaList = if (response.successful == false ) null val tanMediaList = if (response.successful == false ) null
else response.getFirstSegmentById<TanMediaList>(InstituteSegmentId.TanMediaList) else response.getFirstSegmentById<TanMediaList>(InstituteSegmentId.TanMediaList)
@ -407,31 +432,32 @@ open class FinTsClient(
customer.tanMedia = it.tanMedia customer.tanMedia = it.tanMedia
} }
return GetTanMediaListResponse(response, tanMediaList) callback(GetTanMediaListResponse(response, tanMediaList))
}
} }
open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData): FinTsClientResponse { open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) {
var enteredAtc: EnterTanGeneratorAtcResult? = null var enteredAtc: EnterTanGeneratorAtcResult? = null
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) { if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
enteredAtc = callback.enterTanGeneratorAtc(customer, newActiveTanMedium) enteredAtc = this.callback.enterTanGeneratorAtc(customer, newActiveTanMedium)
if (enteredAtc.hasAtcBeenEntered == false) { if (enteredAtc.hasAtcBeenEntered == false) {
val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate
return FinTsClientResponse(Response(false, exception = Exception(message))) callback(FinTsClientResponse(Response(false, exception = Exception(message))))
return
} }
} }
val response = sendMessageAndHandleResponse(bank, customer, false) { dialogContext -> sendMessageAndHandleResponse(bank, customer, false, { dialogContext ->
messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext,
enteredAtc?.tan, enteredAtc?.atc) enteredAtc?.tan, enteredAtc?.atc)
}) { response ->
callback(FinTsClientResponse(response))
} }
return FinTsClientResponse(response)
} }
@ -439,66 +465,69 @@ open class FinTsClient(
customer: CustomerData, account: AccountData, callback: (FinTsClientResponse) -> Unit) { customer: CustomerData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
GlobalScope.launch { GlobalScope.launch {
callback(doBankTransfer(bankTransferData, bank, customer, account)) doBankTransfer(bankTransferData, bank, customer, account, callback)
} }
} }
open fun doBankTransfer(bankTransferData: BankTransferData, bank: BankData, open fun doBankTransfer(bankTransferData: BankTransferData, bank: BankData,
customer: CustomerData, account: AccountData): FinTsClientResponse { customer: CustomerData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
val response = sendMessageAndHandleResponse(bank, customer) { dialogContext -> sendMessageAndHandleResponse(bank, customer, true, { dialogContext ->
messageBuilder.createBankTransferMessage(bankTransferData, account, dialogContext) messageBuilder.createBankTransferMessage(bankTransferData, account, dialogContext)
}) { response ->
callback(FinTsClientResponse(response))
} }
return FinTsClientResponse(response)
} }
protected open fun sendMessageAndHandleResponse(bank: BankData, customer: CustomerData, messageMayRequiresTan: Boolean = true, protected open fun sendMessageAndHandleResponse(bank: BankData, customer: CustomerData, messageMayRequiresTan: Boolean = true,
createMessage: (DialogContext) -> MessageBuilderResult): Response { createMessage: (DialogContext) -> MessageBuilderResult, callback: (Response) -> Unit) {
val dialogContext = DialogContext(bank, customer, product) val dialogContext = DialogContext(bank, customer, product)
val initDialogResponse = initDialog(dialogContext) initDialog(dialogContext) { initDialogResponse ->
if (initDialogResponse.successful == false) { if (initDialogResponse.successful == false) {
return initDialogResponse callback(initDialogResponse)
} }
else {
val message = createMessage(dialogContext) val message = createMessage(dialogContext)
val response = getAndHandleResponseForMessage(message, dialogContext) getAndHandleResponseForMessage(message, dialogContext) { response ->
closeDialog(dialogContext) closeDialog(dialogContext)
callback(response)
return response }
}
}
} }
protected open fun initDialog(dialogContext: DialogContext): Response { protected open fun initDialog(dialogContext: DialogContext, callback: (Response) -> Unit) {
// we first need to retrieve supported tan procedures and jobs before we can do anything // we first need to retrieve supported tan procedures and jobs before we can do anything
val retrieveBasicBankDataResponse = ensureBasicBankDataRetrieved(dialogContext.bank, dialogContext.customer) ensureBasicBankDataRetrieved(dialogContext.bank, dialogContext.customer) { retrieveBasicBankDataResponse ->
if (retrieveBasicBankDataResponse.successful == false) { if (retrieveBasicBankDataResponse.successful == false) {
return retrieveBasicBankDataResponse callback(retrieveBasicBankDataResponse)
} }
else {
// as in the next step we have to supply user's tan procedure, ensure user selected his or her // as in the next step we have to supply user's tan procedure, ensure user selected his or her
val tanProcedureSelectedResponse = ensureTanProcedureIsSelected(dialogContext.bank, dialogContext.customer) ensureTanProcedureIsSelected(dialogContext.bank, dialogContext.customer) { tanProcedureSelectedResponse ->
if (tanProcedureSelectedResponse.successful == false) { if (tanProcedureSelectedResponse.successful == false) {
return tanProcedureSelectedResponse callback(tanProcedureSelectedResponse)
}
else {
initDialogAfterSuccessfulChecks(dialogContext, callback)
}
}
}
}
} }
return initDialogAfterSuccessfulChecks(dialogContext) protected open fun initDialogAfterSuccessfulChecks(dialogContext: DialogContext, callback: (Response) -> Unit) {
}
protected open fun initDialogAfterSuccessfulChecks(dialogContext: DialogContext): Response {
val message = messageBuilder.createInitDialogMessage(dialogContext) val message = messageBuilder.createInitDialogMessage(dialogContext)
val response = GetUserTanProceduresResponse(getAndHandleResponseForMessage(message, dialogContext)) getAndHandleResponseForMessage(message, dialogContext) { unmappedResponse ->
val response = GetUserTanProceduresResponse(unmappedResponse)
dialogContext.response = response dialogContext.response = response
if (response.successful) { if (response.successful) {
@ -506,7 +535,8 @@ open class FinTsClient(
updateCustomerData(dialogContext.customer, dialogContext.bank, response) updateCustomerData(dialogContext.customer, dialogContext.bank, response)
} }
return response callback(response)
}
} }
protected open fun closeDialog(dialogContext: DialogContext) { protected open fun closeDialog(dialogContext: DialogContext) {
@ -522,35 +552,45 @@ open class FinTsClient(
} }
protected open fun ensureBasicBankDataRetrieved(bank: BankData, customer: CustomerData): Response { protected open fun ensureBasicBankDataRetrieved(bank: BankData, customer: CustomerData, callback: (Response) -> Unit) {
if (bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) { if (bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) {
val getBankInfoResponse = getBankAndCustomerInfoForNewUser(bank, customer) getBankAndCustomerInfoForNewUser(bank, customer) { getBankInfoResponse ->
if (getBankInfoResponse.isSuccessful == false || bank.supportedTanProcedures.isEmpty() if (getBankInfoResponse.isSuccessful == false || bank.supportedTanProcedures.isEmpty()
|| bank.supportedJobs.isEmpty()) { || bank.supportedJobs.isEmpty()) {
return Response(false, exception = callback(Response(false, exception =
Exception("Could not retrieve basic bank data like supported tan procedures or supported jobs")) // TODO: translate // TODO: add as messageToShowToUser Exception("Could not retrieve basic bank data like supported tan procedures or supported jobs"))) // TODO: translate // TODO: add as messageToShowToUser
}
else {
callback(Response(true))
}
}
}
else {
callback(Response(true))
} }
} }
return Response(true) protected open fun ensureTanProcedureIsSelected(bank: BankData, customer: CustomerData, callback: (Response) -> Unit) {
}
protected open fun ensureTanProcedureIsSelected(bank: BankData, customer: CustomerData): Response {
if (customer.isTanProcedureSelected == false) { if (customer.isTanProcedureSelected == false) {
if (customer.supportedTanProcedures.isEmpty()) { if (customer.supportedTanProcedures.isEmpty()) {
getBankAndCustomerInfoForNewUser(bank, customer) getBankAndCustomerInfoForNewUser(bank, customer) {
}
if (customer.supportedTanProcedures.isEmpty()) { // could not retrieve supported tan procedures for user if (customer.supportedTanProcedures.isEmpty()) { // could not retrieve supported tan procedures for user
return Response(false, noTanProcedureSelected = true) callback(Response(false, noTanProcedureSelected = true))
} }
else {
getUsersTanProcedure(customer) getUsersTanProcedure(customer)
callback(Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected))
}
}
}
else {
callback(Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected))
}
}
else {
callback(Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected))
} }
return Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected)
} }
protected open fun getUsersTanProcedure(customer: CustomerData) { protected open fun getUsersTanProcedure(customer: CustomerData) {
@ -573,12 +613,13 @@ open class FinTsClient(
} }
protected open fun getAndHandleResponseForMessage(message: MessageBuilderResult, dialogContext: DialogContext): Response { protected open fun getAndHandleResponseForMessage(message: MessageBuilderResult, dialogContext: DialogContext, callback: (Response) -> Unit) {
val response = if (message.createdMessage == null) Response(false, messageCreationError = message) if (message.createdMessage == null) {
else getAndHandleResponseForMessage(message.createdMessage, dialogContext) callback(Response(false, messageCreationError = message))
}
val handledResponse = handleMayRequiresTan(response, dialogContext) else {
getAndHandleResponseForMessage(message.createdMessage, dialogContext) { response ->
handleMayRequiresTan(response, dialogContext) { handledResponse ->
// if there's a Aufsetzpunkt (continuationId) set, then response is not complete yet, there's more information to fetch by sending this Aufsetzpunkt // 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 -> 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 (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
@ -586,21 +627,28 @@ open class FinTsClient(
dialogContext.chunkedResponseHandler?.invoke(handledResponse) dialogContext.chunkedResponseHandler?.invoke(handledResponse)
} }
handledResponse.followUpResponse = getFollowUpMessageForContinuationId(handledResponse, continuationId, message, dialogContext) getFollowUpMessageForContinuationId(handledResponse, continuationId, message, dialogContext) { followUpResponse ->
handledResponse.followUpResponse = followUpResponse
handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null
}
}
?: run { dialogContext.chunkedResponseHandler?.invoke(handledResponse) }
return handledResponse callback(handledResponse)
}
}
}
?: run {
dialogContext.chunkedResponseHandler?.invoke(handledResponse)
callback(handledResponse)
}
}
}
}
} }
protected open fun getAndHandleResponseForMessage(requestBody: String, dialogContext: DialogContext): Response { protected open fun getAndHandleResponseForMessage(requestBody: String, dialogContext: DialogContext, callback: (Response) -> Unit) {
addMessageLog(requestBody, MessageLogEntryType.Sent, dialogContext) addMessageLog(requestBody, MessageLogEntryType.Sent, dialogContext)
val webResponse = getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) { webResponse ->
val response = handleResponse(webResponse, dialogContext) val response = handleResponse(webResponse, dialogContext)
dialogContext.response = response dialogContext.response = response
@ -608,20 +656,21 @@ open class FinTsClient(
response.messageHeader?.let { header -> dialogContext.dialogId = header.dialogId } response.messageHeader?.let { header -> dialogContext.dialogId = header.dialogId }
dialogContext.didBankCloseDialog = response.didBankCloseDialog dialogContext.didBankCloseDialog = response.didBankCloseDialog
return response callback(response)
}
} }
protected open fun getResponseForMessage(requestBody: String, finTs3ServerAddress: String): WebClientResponse { protected open fun getResponseForMessage(requestBody: String, finTs3ServerAddress: String, callback: (WebClientResponse) -> Unit) {
val encodedRequestBody = base64Service.encode(requestBody) val encodedRequestBody = base64Service.encode(requestBody)
return webClient.post(finTs3ServerAddress, encodedRequestBody, "application/octet-stream") webClient.post(finTs3ServerAddress, encodedRequestBody, "application/octet-stream", IWebClient.DefaultUserAgent, callback)
} }
protected open fun fireAndForgetMessage(message: MessageBuilderResult, dialogContext: DialogContext) { protected open fun fireAndForgetMessage(message: MessageBuilderResult, dialogContext: DialogContext) {
message.createdMessage?.let { requestBody -> message.createdMessage?.let { requestBody ->
addMessageLog(requestBody, MessageLogEntryType.Sent, dialogContext) addMessageLog(requestBody, MessageLogEntryType.Sent, dialogContext)
getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) { }
// if really needed add received response to message log here // if really needed add received response to message log here
} }
@ -658,13 +707,12 @@ open class FinTsClient(
protected open fun getFollowUpMessageForContinuationId(response: Response, continuationId: String, message: MessageBuilderResult, protected open fun getFollowUpMessageForContinuationId(response: Response, continuationId: String, message: MessageBuilderResult,
dialogContext: DialogContext): Response? { dialogContext: DialogContext, callback: (Response?) -> Unit) {
messageBuilder.rebuildMessageWithContinuationId(message, continuationId, dialogContext)?.let { followUpMessage -> messageBuilder.rebuildMessageWithContinuationId(message, continuationId, dialogContext)?.let { followUpMessage ->
return getAndHandleResponseForMessage(followUpMessage, dialogContext) getAndHandleResponseForMessage(followUpMessage, dialogContext, callback)
} }
?: run { callback(null) }
return null
} }
@ -717,26 +765,29 @@ open class FinTsClient(
} }
protected open fun handleMayRequiresTan(response: Response, dialogContext: DialogContext): Response { // TODO: use response from DialogContext protected open fun handleMayRequiresTan(response: Response, dialogContext: DialogContext, callback: (Response) -> Unit) { // TODO: use response from DialogContext
if (response.isStrongAuthenticationRequired) { if (response.isStrongAuthenticationRequired) {
if (dialogContext.abortIfTanIsRequired) { if (dialogContext.abortIfTanIsRequired) {
response.tanRequiredButWeWereToldToAbortIfSo = true response.tanRequiredButWeWereToldToAbortIfSo = true
return response callback(response)
return
} }
response.tanResponse?.let { tanResponse -> response.tanResponse?.let { tanResponse ->
val customer = dialogContext.customer val customer = dialogContext.customer
val enteredTanResult = callback.enterTan(customer, createTanChallenge(tanResponse, customer)) val enteredTanResult = this.callback.enterTan(customer, createTanChallenge(tanResponse, customer))
if (enteredTanResult.changeTanProcedureTo != null) { if (enteredTanResult.changeTanProcedureTo != null) {
return handleUserAsksToChangeTanProcedureAndResendLastMessage(enteredTanResult.changeTanProcedureTo, handleUserAsksToChangeTanProcedureAndResendLastMessage(enteredTanResult.changeTanProcedureTo,
dialogContext) dialogContext, callback)
return
} }
else if (enteredTanResult.changeTanMediumTo is TanGeneratorTanMedium) { else if (enteredTanResult.changeTanMediumTo is TanGeneratorTanMedium) {
return handleUserAsksToChangeTanMediumAndResendLastMessage(enteredTanResult.changeTanMediumTo, handleUserAsksToChangeTanMediumAndResendLastMessage(enteredTanResult.changeTanMediumTo,
dialogContext, enteredTanResult.changeTanMediumResultCallback) dialogContext, enteredTanResult.changeTanMediumResultCallback, callback)
return
} }
else if (enteredTanResult.enteredTan == null) { else if (enteredTanResult.enteredTan == null) {
// i tried to send a HKTAN with cancelJob = true but then i saw there are no tan procedures that support cancellation (at least not at my bank) // i tried to send a HKTAN with cancelJob = true but then i saw there are no tan procedures that support cancellation (at least not at my bank)
@ -744,7 +795,8 @@ open class FinTsClient(
response.tanRequiredButUserDidNotEnterOne = true response.tanRequiredButUserDidNotEnterOne = true
} }
else { else {
return sendTanToBank(enteredTanResult.enteredTan, tanResponse, dialogContext) sendTanToBank(enteredTanResult.enteredTan, tanResponse, dialogContext, callback)
return
} }
} }
} }
@ -756,7 +808,7 @@ open class FinTsClient(
// TODO: also check '9931 Sperrung des Kontos nach %1 Fehlversuchen' -> if %1 == 3 synchronize TAN generator // TODO: also check '9931 Sperrung des Kontos nach %1 Fehlversuchen' -> if %1 == 3 synchronize TAN generator
// as it's quite unrealistic that user entered TAN wrong three times, in most cases TAN generator is not synchronized // as it's quite unrealistic that user entered TAN wrong three times, in most cases TAN generator is not synchronized
return response callback(response)
} }
protected open fun createTanChallenge(tanResponse: TanResponse, customer: CustomerData): TanChallenge { protected open fun createTanChallenge(tanResponse: TanResponse, customer: CustomerData): TanChallenge {
@ -778,14 +830,14 @@ open class FinTsClient(
} }
} }
protected open fun sendTanToBank(enteredTan: String, tanResponse: TanResponse, dialogContext: DialogContext): Response { protected open fun sendTanToBank(enteredTan: String, tanResponse: TanResponse, dialogContext: DialogContext, callback: (Response) -> Unit) {
val message = messageBuilder.createSendEnteredTanMessage(enteredTan, tanResponse, dialogContext) val message = messageBuilder.createSendEnteredTanMessage(enteredTan, tanResponse, dialogContext)
return getAndHandleResponseForMessage(message, dialogContext) getAndHandleResponseForMessage(message, dialogContext, callback)
} }
protected open fun handleUserAsksToChangeTanProcedureAndResendLastMessage(changeTanProcedureTo: TanProcedure, dialogContext: DialogContext): Response { protected open fun handleUserAsksToChangeTanProcedureAndResendLastMessage(changeTanProcedureTo: TanProcedure, dialogContext: DialogContext, callback: (Response) -> Unit) {
dialogContext.customer.selectedTanProcedure = changeTanProcedureTo dialogContext.customer.selectedTanProcedure = changeTanProcedureTo
@ -794,53 +846,55 @@ open class FinTsClient(
lastCreatedMessage?.let { closeDialog(dialogContext) } lastCreatedMessage?.let { closeDialog(dialogContext) }
return resendMessageInNewDialog(lastCreatedMessage, dialogContext) resendMessageInNewDialog(lastCreatedMessage, dialogContext, callback)
} }
protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(changeTanMediumTo: TanGeneratorTanMedium, protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(changeTanMediumTo: TanGeneratorTanMedium,
dialogContext: DialogContext, dialogContext: DialogContext,
changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?): Response { changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?,
callback: (Response) -> Unit) {
val lastCreatedMessage = dialogContext.currentMessage val lastCreatedMessage = dialogContext.currentMessage
lastCreatedMessage?.let { closeDialog(dialogContext) } lastCreatedMessage?.let { closeDialog(dialogContext) }
val changeTanMediumResponse = changeTanMedium(changeTanMediumTo, dialogContext.bank, dialogContext.customer) changeTanMedium(changeTanMediumTo, dialogContext.bank, dialogContext.customer) { changeTanMediumResponse ->
changeTanMediumResultCallback?.invoke(changeTanMediumResponse) changeTanMediumResultCallback?.invoke(changeTanMediumResponse)
if (changeTanMediumResponse.isSuccessful == false || lastCreatedMessage == null) { if (changeTanMediumResponse.isSuccessful == false || lastCreatedMessage == null) {
return changeTanMediumResponse.toResponse() callback(changeTanMediumResponse.toResponse())
}
else {
resendMessageInNewDialog(lastCreatedMessage, dialogContext, callback)
}
}
} }
return resendMessageInNewDialog(lastCreatedMessage, dialogContext) protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext, callback: (Response) -> Unit) {
}
protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext): Response {
lastCreatedMessage?.let { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message lastCreatedMessage?.let { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message
val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.customer, previousDialogContext.product, chunkedResponseHandler = previousDialogContext.chunkedResponseHandler) val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.customer, previousDialogContext.product, chunkedResponseHandler = previousDialogContext.chunkedResponseHandler)
val initDialogResponse = initDialog(newDialogContext) initDialog(newDialogContext) { initDialogResponse ->
if (initDialogResponse.successful == false) { if (initDialogResponse.successful == false) {
return initDialogResponse callback(initDialogResponse)
} }
else {
val newMessage = messageBuilder.rebuildMessage(lastCreatedMessage, newDialogContext) val newMessage = messageBuilder.rebuildMessage(lastCreatedMessage, newDialogContext)
val response = getAndHandleResponseForMessage(newMessage, newDialogContext) getAndHandleResponseForMessage(newMessage, newDialogContext) { response ->
closeDialog(newDialogContext) closeDialog(newDialogContext)
return response callback(response)
}
}
}
} }
val errorMessage = "There's no last action (like retrieve account transactions, transfer money, ...) to re-send with new TAN procedure. Probably an internal programming error." // TODO: translate val errorMessage = "There's no last action (like retrieve account transactions, transfer money, ...) to re-send with new TAN procedure. Probably an internal programming error." // TODO: translate
return Response(false, exception = Exception(errorMessage)) // should never come to this callback(Response(false, exception = Exception(errorMessage))) // should never come to this
} }

View File

@ -38,8 +38,8 @@ open class FinTsClientForCustomer(
client.addAccountAsync(bank, customer, callback) client.addAccountAsync(bank, customer, callback)
} }
open fun addAccount(): AddAccountResponse { open fun addAccount(callback: (AddAccountResponse) -> Unit) {
return client.addAccount(bank, customer) client.addAccount(bank, customer, callback)
} }
@ -47,8 +47,8 @@ open class FinTsClientForCustomer(
client.getTransactionsAsync(parameter, bank, customer, account, callback) client.getTransactionsAsync(parameter, bank, customer, account, callback)
} }
open fun getTransactions(parameter: GetTransactionsParameter, account: AccountData): GetTransactionsResponse { open fun getTransactions(parameter: GetTransactionsParameter, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
return client.getTransactions(parameter, bank, customer, account) client.getTransactions(parameter, bank, customer, account, callback)
} }
@ -56,8 +56,8 @@ open class FinTsClientForCustomer(
client.doBankTransferAsync(bankTransferData, bank, customer, account, callback) client.doBankTransferAsync(bankTransferData, bank, customer, account, callback)
} }
open fun doBankTransfer(bankTransferData: BankTransferData, account: AccountData): FinTsClientResponse { open fun doBankTransfer(bankTransferData: BankTransferData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
return client.doBankTransfer(bankTransferData, bank, customer, account) client.doBankTransfer(bankTransferData, bank, customer, account, callback)
} }
} }

View File

@ -20,6 +20,6 @@ interface IWebClient {
} }
fun post(url: String, body: String, contentType: String = "application/octet-stream", userAgent: String = DefaultUserAgent) : WebClientResponse fun post(url: String, body: String, contentType: String = "application/octet-stream", userAgent: String = DefaultUserAgent, callback: (WebClientResponse) -> Unit)
} }

View File

@ -34,9 +34,8 @@ open class KtorWebClient : IWebClient {
} }
override fun post(url: String, body: String, contentType: String, userAgent: String): WebClientResponse { override fun post(url: String, body: String, contentType: String, userAgent: String, callback: (WebClientResponse) -> Unit) {
try { GlobalScope.async {
val job = GlobalScope.async {
try { try {
val clientResponse = client.post<HttpResponse>(url) { val clientResponse = client.post<HttpResponse>(url) {
this.body = TextContent(body, contentType = ContentType.Application.OctetStream) this.body = TextContent(body, contentType = ContentType.Application.OctetStream)
@ -44,20 +43,13 @@ open class KtorWebClient : IWebClient {
val responseBody = clientResponse.readText() val responseBody = clientResponse.readText()
WebClientResponse(clientResponse.status.value == 200, clientResponse.status.value, body = responseBody) callback(WebClientResponse(clientResponse.status.value == 200, clientResponse.status.value, body = responseBody))
} catch (e: Exception) { } catch (e: Exception) {
log.error(e) { "Could not send request to url '$url'" } log.error(e) { "Could not send request to url '$url'" }
WebClientResponse(false, error = e) callback(WebClientResponse(false, error = e))
} }
} }
while (job.isCompleted == false) { } // let's warm the CPU to get suspend function synchronous (runBlocking is not available in common projects)
return job.getCompleted()
} catch (e: Exception) {
return WebClientResponse(false, error = e)
}
} }
} }

View File

@ -87,7 +87,7 @@ open class FinTsClientTestBase {
fun getAnonymousBankInfo() { fun getAnonymousBankInfo() {
// when // when
val result = underTest.getAnonymousBankInfo(BankDataAnonymous) underTest.getAnonymousBankInfo(BankDataAnonymous) { result ->
// then // then
expect(result.isSuccessful).isTrue() expect(result.isSuccessful).isTrue()
@ -97,6 +97,7 @@ open class FinTsClientTestBase {
expect(BankDataAnonymous.supportedLanguages).isNotEmpty() expect(BankDataAnonymous.supportedLanguages).isNotEmpty()
expect(BankDataAnonymous.name).isNotEmpty() expect(BankDataAnonymous.name).isNotEmpty()
} }
}
@Test @Test
@ -166,7 +167,7 @@ open class FinTsClientTestBase {
// this test is only senseful for accounts using chipTAN / TAN generator as TAN procedure // this test is only senseful for accounts using chipTAN / TAN generator as TAN procedure
underTest.getAnonymousBankInfo(Bank) underTest.getAnonymousBankInfo(Bank) { }
val supportsRetrievingTanMedia = Bank.supportedJobs.firstOrNull { it.jobName == "HKTAB" } != null val supportsRetrievingTanMedia = Bank.supportedJobs.firstOrNull { it.jobName == "HKTAB" } != null
@ -178,8 +179,7 @@ open class FinTsClientTestBase {
// when // when
val result = underTest.getTanMediaList(Bank, Customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) underTest.getTanMediaList(Bank, Customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) { result ->
// then // then
expect(result.isSuccessful).isTrue() expect(result.isSuccessful).isTrue()
@ -190,6 +190,7 @@ open class FinTsClientTestBase {
expect(Customer.tanMedia).isNotEmpty() expect(Customer.tanMedia).isNotEmpty()
} }
}
@Ignore // only works with banks that don't support HKTAB version 5 @Ignore // only works with banks that don't support HKTAB version 5
@Test @Test
@ -197,7 +198,7 @@ open class FinTsClientTestBase {
// when // when
expect { expect {
underTest.getTanMediaList(Bank, Customer, TanMedienArtVersion.Alle, TanMediumKlasse.BilateralVereinbart) underTest.getTanMediaList(Bank, Customer, TanMedienArtVersion.Alle, TanMediumKlasse.BilateralVereinbart) { }
}.toThrow<UnsupportedOperationException>() }.toThrow<UnsupportedOperationException>()
@ -226,10 +227,11 @@ open class FinTsClientTestBase {
// when // when
val result = underTest.doBankTransfer(BankTransferData, Bank, Customer, account) underTest.doBankTransfer(BankTransferData, Bank, Customer, account) { result ->
// then // then
expect(result.isSuccessful).isTrue() expect(result.isSuccessful).isTrue()
} }
}
} }