Added BankingClient
This commit is contained in:
parent
d89a55e8d7
commit
0c9d1ab91c
|
@ -0,0 +1,106 @@
|
|||
@file:OptIn(ExperimentalWasmDsl::class)
|
||||
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
}
|
||||
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(8)
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
|
||||
testLogging {
|
||||
showExceptions = true
|
||||
showStandardStreams = true
|
||||
events("passed", "skipped", "failed")
|
||||
// exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
js {
|
||||
moduleName = "banking-client"
|
||||
binaries.executable()
|
||||
|
||||
browser {
|
||||
testTask {
|
||||
useKarma {
|
||||
useChromeHeadless()
|
||||
useFirefoxHeadless()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "20s" // Mocha times out after 2 s, which is too short for bufferExceeded() test
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wasmJs()
|
||||
|
||||
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
macosX64()
|
||||
macosArm64()
|
||||
watchosArm64()
|
||||
watchosSimulatorArm64()
|
||||
tvosArm64()
|
||||
tvosSimulatorArm64()
|
||||
|
||||
applyDefaultHierarchyTemplate()
|
||||
|
||||
|
||||
|
||||
val coroutinesVersion: String by project
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":BankingClientModel"))
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
||||
jvmTest { }
|
||||
|
||||
jsMain {
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
||||
jsTest { }
|
||||
|
||||
nativeMain { }
|
||||
nativeTest { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ext["customArtifactId"] = "banking-client"
|
||||
|
||||
apply(from = "../gradle/scripts/publish-codinux.gradle.kts")
|
|
@ -0,0 +1,19 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.AccountCredentials
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
|
||||
interface BankingClient {
|
||||
|
||||
suspend fun getAccountDataAsync(bankCode: String, loginName: String, password: String) =
|
||||
getAccountDataAsync(AccountCredentials(bankCode, loginName, password))
|
||||
|
||||
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||
suspend fun getAccountDataAsync(credentials: AccountCredentials) =
|
||||
getAccountDataAsync(credentials, GetAccountDataOptions())
|
||||
|
||||
suspend fun getAccountDataAsync(credentials: AccountCredentials, options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
|
||||
interface BankingClientForCustomer {
|
||||
|
||||
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||
suspend fun getAccountDataAsync() = getAccountDataAsync(GetAccountDataOptions())
|
||||
|
||||
suspend fun getAccountDataAsync(options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.AccountCredentials
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
|
||||
abstract class BankingClientForCustomerBase(
|
||||
protected val credentials: AccountCredentials,
|
||||
protected val client: BankingClient
|
||||
) : BankingClientForCustomer {
|
||||
|
||||
override suspend fun getAccountDataAsync(options: GetAccountDataOptions) =
|
||||
client.getAccountDataAsync(credentials, options)
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.AccountCredentials
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
|
||||
interface BlockingBankingClient {
|
||||
|
||||
suspend fun getAccountData(bankCode: String, loginName: String, password: String) =
|
||||
getAccountData(AccountCredentials(bankCode, loginName, password))
|
||||
|
||||
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||
fun getAccountData(credentials: AccountCredentials) =
|
||||
getAccountData(credentials, GetAccountDataOptions())
|
||||
|
||||
fun getAccountData(credentials: AccountCredentials, options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
|
||||
interface BlockingBankingClientForCustomer {
|
||||
|
||||
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||
fun getAccountData() = getAccountData(GetAccountDataOptions())
|
||||
|
||||
fun getAccountData(options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.AccountCredentials
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
|
||||
abstract class BlockingBankingClientForCustomerBase(
|
||||
protected val credentials: AccountCredentials,
|
||||
protected val client: BlockingBankingClient
|
||||
) : BlockingBankingClientForCustomer {
|
||||
|
||||
override fun getAccountData(options: GetAccountDataOptions) =
|
||||
client.getAccountData(credentials, options)
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package net.codinux.banking.client.model.options
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
data class GetAccountDataOptions(
|
||||
val retrieveBalance: Boolean = true,
|
||||
val retrieveTransactions: RetrieveTransactions = RetrieveTransactions.OfLast90Days,
|
||||
val retrieveTransactionsFrom: LocalDate? = null,
|
||||
val retrieveTransactionsTo: LocalDate? = null,
|
||||
val abortIfTanIsRequired: Boolean = false
|
||||
)
|
|
@ -0,0 +1,18 @@
|
|||
package net.codinux.banking.client.model.options
|
||||
|
||||
enum class RetrieveTransactions {
|
||||
No,
|
||||
|
||||
All,
|
||||
|
||||
/**
|
||||
* Some banks support that according to PSD2 account transactions of last 90 days may be retrieved without
|
||||
* a TAN (= no strong customer authorization needed). So try this options if you don't want to enter a TAN.
|
||||
*/
|
||||
OfLast90Days,
|
||||
|
||||
/**
|
||||
* Retrieves account transactions in the boundaries of [GetAccountDataOptions.retrieveTransactionsFrom] to [GetAccountDataOptions.retrieveTransactionsTo].
|
||||
*/
|
||||
AccordingToRetrieveFromAndTo
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
|
||||
@NoArgConstructor
|
||||
class Error(
|
||||
val type: ErrorType,
|
||||
|
||||
/**
|
||||
* A banking client internal error like an error occurred during response parsing.
|
||||
*/
|
||||
val internalError: String? = null,
|
||||
/**
|
||||
* Error messages as received from bank
|
||||
*/
|
||||
val errorMessagesFromBank: List<String> = emptyList(),
|
||||
) {
|
||||
override fun toString() = "$type: ${internalError ?: errorMessagesFromBank.joinToString()}"
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
enum class ErrorType {
|
||||
BankDoesNotSupportFinTs3,
|
||||
|
||||
NetworkError,
|
||||
|
||||
InternalError,
|
||||
|
||||
BankReturnedError,
|
||||
|
||||
WrongCredentials,
|
||||
|
||||
AccountLocked,
|
||||
|
||||
JobNotSupported,
|
||||
|
||||
UserCancelledAction,
|
||||
|
||||
TanRequiredButShouldAbortIfRequiresTan,
|
||||
|
||||
NoneOfTheAccountsSupportsRetrievingData,
|
||||
|
||||
DidNotRetrieveAllAccountData,
|
||||
|
||||
CanNotDetermineBicForIban,
|
||||
|
||||
NoAccountSupportsMoneyTransfer,
|
||||
|
||||
MoreThanOneAccountSupportsMoneyTransfer,
|
||||
|
||||
UnknownError
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
import net.codinux.banking.client.model.AccountTransaction
|
||||
import net.codinux.banking.client.model.CustomerAccount
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
|
||||
@NoArgConstructor
|
||||
class GetAccountDataResponse(
|
||||
val customer: CustomerAccount
|
||||
) {
|
||||
|
||||
val bookedTransactions: List<AccountTransaction>
|
||||
get() = customer.accounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate }
|
||||
|
||||
override fun toString() = customer.toString()
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
|
||||
@NoArgConstructor
|
||||
class Response<T> (
|
||||
val type: ResponseType,
|
||||
val data: T? = null,
|
||||
val error: Error? = null,
|
||||
val tanRequired: TanRequired? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun <T> success(data: T): Response<T> =
|
||||
Response(ResponseType.Success, data)
|
||||
|
||||
fun <T> error(errorType: ErrorType, internalError: String? = null, errorMessagesFromBank: List<String> = emptyList()): Response<T> =
|
||||
Response(ResponseType.Error, null, Error(errorType, internalError, errorMessagesFromBank))
|
||||
|
||||
fun <T> bankReturnedError(errorMessagesFromBank: List<String>): Response<T> =
|
||||
Response.error(ErrorType.BankReturnedError, null, errorMessagesFromBank)
|
||||
}
|
||||
|
||||
|
||||
override fun toString() = when (type) {
|
||||
ResponseType.Success -> "Success: $data"
|
||||
ResponseType.Error -> "Error: $error"
|
||||
ResponseType.TanRequired -> "TanRequired: $tanRequired"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
enum class ResponseType {
|
||||
Success,
|
||||
|
||||
Error,
|
||||
|
||||
TanRequired
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
|
||||
@NoArgConstructor
|
||||
class TanRequired (
|
||||
val tanRequestId: String,
|
||||
// TODO: add TAN model
|
||||
// val tanChallenge: TanChallenge
|
||||
) {
|
||||
|
||||
}
|
|
@ -27,12 +27,14 @@ allprojects {
|
|||
|
||||
tasks.register("publishAllToMavenLocal") {
|
||||
dependsOn(
|
||||
":BankingClientModel:publishToMavenLocal"
|
||||
":BankingClientModel:publishToMavenLocal",
|
||||
":BankingClient:publishToMavenLocal"
|
||||
)
|
||||
}
|
||||
|
||||
tasks.register("publishAll") {
|
||||
dependsOn(
|
||||
":BankingClientModel:publish"
|
||||
":BankingClientModel:publish",
|
||||
":BankingClient:publish"
|
||||
)
|
||||
}
|
|
@ -21,6 +21,8 @@ plugins {
|
|||
}
|
||||
|
||||
|
||||
rootProject.name = "BankingClient"
|
||||
rootProject.name = "BankingClientProject"
|
||||
|
||||
|
||||
include("BankingClientModel")
|
||||
include("BankingClient")
|
||||
|
|
Loading…
Reference in New Issue