Started fints4k REST API

This commit is contained in:
dankito 2020-10-18 14:56:54 +02:00
parent 5195f2d0c3
commit 5569594a65
27 changed files with 740 additions and 0 deletions

View File

@ -0,0 +1,5 @@
*
!build/*-runner
!build/*-runner.jar
!build/lib/*
!build/quarkus-app/*

View File

@ -0,0 +1,62 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id "org.jetbrains.kotlin.plugin.allopen" version "1.3.72"
id 'io.quarkus'
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
// TODO: why can't Gradle find fints4k project? .jars have temporarily to be copied to libs folder - which are not committed to repo of course - till this issue is fixed
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "net.dankito.utils:java-utils:$javaUtilsVersion"
implementation "io.ktor:ktor-client-okhttp:$ktorVersion"
implementation "org.slf4j:slf4j-api:$slf4jVersion"
implementation enforcedPlatform("io.quarkus:quarkus-universe-bom:$quarkusVersion")
implementation 'io.quarkus:quarkus-kotlin'
implementation 'io.quarkus:quarkus-resteasy'
implementation 'io.quarkus:quarkus-resteasy-jackson'
testImplementation 'io.quarkus:quarkus-junit5'
testImplementation 'io.rest-assured:kotlin-extensions'
}
quarkus {
setOutputDirectory("$projectDir/build/classes/kotlin/main")
}
quarkusDev {
setSourceDir("$projectDir/src/main/kotlin")
}
allOpen {
annotation("javax.ws.rs.Path")
annotation("javax.enterprise.context.ApplicationScoped")
annotation("io.quarkus.test.junit.QuarkusTest")
}
def javaVersion = JavaVersion.VERSION_11
java {
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}
compileKotlin {
kotlinOptions.jvmTarget = javaVersion
kotlinOptions.javaParameters = true
}
compileTestKotlin {
kotlinOptions.jvmTarget = javaVersion
}
test {
systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager"
}

View File

@ -0,0 +1,57 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# mvn package -Dquarkus.package.type=fast-jar
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.fast-jar -t codinux/fints4k-fast-jar .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 codinux/fints4k-fast-jar
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" codinux/fints4k-fast-jar
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
&& microdnf update \
&& microdnf clean all \
&& mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments \
&& curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
&& chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=1001 build/quarkus-app/lib/ /deployments/lib/
COPY --chown=1001 build/quarkus-app/*.jar /deployments/
COPY --chown=1001 build/quarkus-app/app/ /deployments/app/
COPY --chown=1001 build/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080
USER 1001
ENTRYPOINT [ "/deployments/run-java.sh" ]

View File

@ -0,0 +1,54 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# mvn package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t codinux/fints4k-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 codinux/fints4k-jvm
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" codinux/fints4k-jvm
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
&& microdnf update \
&& microdnf clean all \
&& mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments \
&& curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
&& chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
COPY build/lib/* /deployments/lib/
COPY build/*-runner.jar /deployments/app.jar
EXPOSE 8080
USER 1001
ENTRYPOINT [ "/deployments/run-java.sh" ]

View File

@ -0,0 +1,27 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
#
# Before building the container image run:
#
# mvn package -Pnative -Dquarkus.native.container-build=true
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.native -t codinux/fints4k .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 codinux/fints4k
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root build/*-runner /work/application
EXPOSE 8080
USER 1001
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

View File

@ -0,0 +1,50 @@
package net.dankito.banking.fints.rest
import net.dankito.banking.fints.rest.model.dto.request.AddAccountRequestDto
import net.dankito.banking.fints.rest.mapper.DtoMapper
import net.dankito.banking.fints.rest.model.dto.request.GetAccountsTransactionsRequestDto
import net.dankito.banking.fints.rest.model.dto.response.GetAccountsTransactionsResponseDto
import net.dankito.banking.fints.rest.service.fints4kService
import javax.inject.Inject
import javax.ws.rs.*
import javax.ws.rs.core.MediaType
@Path("/")
class fints4kResource {
@Inject
protected val service = fints4kService()
protected val mapper = DtoMapper()
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("addaccount")
fun addAccount(
request: AddAccountRequestDto,
@DefaultValue("false") @QueryParam("showRawResponse") showRawResponse: Boolean
): Any {
val clientResponse = service.getAddAccountResponse(request)
if (showRawResponse) {
return clientResponse
}
return mapper.map(clientResponse)
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("transactions")
fun getAccountTransactions(request: GetAccountsTransactionsRequestDto): GetAccountsTransactionsResponseDto {
val accountsTransactions = service.getAccountTransactions(request)
return mapper.mapTransactions(accountsTransactions)
}
}

View File

@ -0,0 +1,137 @@
package net.dankito.banking.fints.rest.mapper
import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.response.client.AddAccountResponse
import net.dankito.banking.fints.response.client.FinTsClientResponse
import net.dankito.banking.fints.response.client.GetTransactionsResponse
import net.dankito.banking.fints.rest.model.dto.response.*
import java.math.BigDecimal
open class DtoMapper {
open fun map(response: AddAccountResponse): AddAccountResponseDto {
return AddAccountResponseDto(
response.successful,
mapErrorMessage(response),
map(response.bank),
map(response.retrievedData)
)
}
protected open fun map(bank: BankData): BankResponseDto {
return BankResponseDto(
bank.bankCode,
bank.userName,
bank.finTs3ServerAddress,
bank.bic,
bank.bankName,
bank.userId,
bank.customerName,
mapTanMethods(bank.tanMethodsAvailableForUser),
if (bank.isTanMethodSelected) map(bank.selectedTanMethod) else null,
bank.tanMedia,
bank.supportedHbciVersions.map { it.name.replace("Hbci_", "HBCI ").replace("FinTs_", "FinTS ").replace('_', '.') }
)
}
open fun mapTransactions(accountsTransactions: List<GetTransactionsResponse>): GetAccountsTransactionsResponseDto {
return GetAccountsTransactionsResponseDto(accountsTransactions.map { map(it) })
}
open fun map(accountTransactions: GetTransactionsResponse): GetAccountTransactionsResponseDto {
val retrievedData = accountTransactions.retrievedData.first()
val balance = mapNullable(retrievedData.balance)
val bookedTransactions = map(retrievedData.bookedTransactions)
return GetAccountTransactionsResponseDto(
retrievedData.accountData.accountIdentifier,
retrievedData.accountData.productName,
accountTransactions.successful,
mapErrorMessage(accountTransactions),
balance,
bookedTransactions,
listOf()
)
}
protected open fun map(accountData: List<RetrievedAccountData>): List<BankAccountResponseDto> {
return accountData.map { map(it) }
}
protected open fun map(accountData: RetrievedAccountData): BankAccountResponseDto {
return BankAccountResponseDto(
accountData.accountData.accountIdentifier,
accountData.accountData.subAccountAttribute,
accountData.accountData.iban,
accountData.accountData.userName,
accountData.accountData.accountType,
accountData.accountData.currency,
accountData.accountData.accountHolderName,
accountData.accountData.productName,
accountData.accountData.supportsRetrievingBalance,
accountData.accountData.supportsRetrievingAccountTransactions,
accountData.accountData.supportsTransferringMoney,
accountData.accountData.supportsRealTimeTransfer,
accountData.successfullyRetrievedData,
mapNullable(accountData.balance),
accountData.retrievedTransactionsFrom,
accountData.retrievedTransactionsTo,
map(accountData.bookedTransactions),
listOf()
)
}
protected open fun map(transactions: Collection<AccountTransaction>): Collection<AccountTransactionResponseDto> {
return transactions.map { map(it) }
}
protected open fun map(transaction: AccountTransaction): AccountTransactionResponseDto {
return AccountTransactionResponseDto(
map(transaction.amount),
transaction.reference,
transaction.otherPartyName,
transaction.otherPartyBankCode,
transaction.otherPartyAccountId,
transaction.bookingText,
transaction.valueDate
)
}
protected open fun mapTanMethods(tanMethods: List<TanMethod>): List<TanMethodResponseDto> {
return tanMethods.map { map(it) }
}
protected open fun map(tanMethod: TanMethod): TanMethodResponseDto {
return TanMethodResponseDto(
tanMethod.displayName,
tanMethod.securityFunction.code,
tanMethod.type,
tanMethod.hhdVersion?.name?.replace("HHD_", "")?.replace('_', '.'),
tanMethod.maxTanInputLength,
tanMethod.allowedTanFormat
)
}
protected open fun map(money: Money): BigDecimal {
return money.bigDecimal
}
protected open fun mapNullable(money: Money?): BigDecimal? {
return money?.let { map(it) }
}
protected open fun mapErrorMessage(response: FinTsClientResponse): String? {
// TODO: evaluate fields like isJobAllowed or tanRequiredButWeWereToldToAbortIfSo and set error message accordingly
return response.errorMessage
?: if (response.errorsToShowToUser.isNotEmpty()) response.errorsToShowToUser.joinToString("\n") else null
}
}

View File

@ -0,0 +1,13 @@
package net.dankito.banking.fints.rest.model
open class BankAccessData(
open val bankCode: String,
open val loginName: String,
open val password: String,
open val finTsServerAddress: String? = null
) {
internal constructor() : this("", "", "") // for object deserializers
}

View File

@ -0,0 +1,13 @@
package net.dankito.banking.fints.rest.model
import net.dankito.banking.fints.model.EnterTanResult
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicReference
open class EnterTanContext(
open val enterTanResult: AtomicReference<EnterTanResult>,
open val countDownLatch: CountDownLatch,
open val tanRequestedTimeStamp: Date = Date()
)

View File

@ -0,0 +1,11 @@
package net.dankito.banking.fints.rest.model
import net.dankito.banking.fints.model.BankData
import net.dankito.banking.fints.model.TanChallenge
open class EnteringTanRequested(
open val tanRequestId: String,
open val bank: BankData,
open val tanChallenge: TanChallenge
)

View File

@ -0,0 +1,6 @@
package net.dankito.banking.fints.rest.model.dto.request
open class AccountRequestDto(
open val identifier: String
)

View File

@ -0,0 +1,4 @@
package net.dankito.banking.fints.rest.model.dto.request
open class AddAccountRequestDto : BankAccessDataRequestDto()

View File

@ -0,0 +1,6 @@
package net.dankito.banking.fints.rest.model.dto.request
import net.dankito.banking.fints.rest.model.BankAccessData
open class BankAccessDataRequestDto : BankAccessData()

View File

@ -0,0 +1,14 @@
package net.dankito.banking.fints.rest.model.dto.request
import net.dankito.banking.fints.rest.model.BankAccessData
import net.dankito.utils.multiplatform.Date
open class GetAccountsTransactionsRequestDto(
open val credentials: BankAccessData,
open val accounts: List<AccountRequestDto>,
open val alsoRetrieveBalance: Boolean = true,
open val fromDate: Date? = null,
open val toDate: Date? = null,
open val abortIfTanIsRequired: Boolean = false
)

View File

@ -0,0 +1,15 @@
package net.dankito.banking.fints.rest.model.dto.response
import net.dankito.utils.multiplatform.Date
import java.math.BigDecimal
open class AccountTransactionResponseDto(
open val amount: BigDecimal,
open val reference: String,
open val otherPartyName: String?,
open val otherPartyBankCode: String?,
open val otherPartyAccountId: String?,
open val bookingText: String?,
open val valueDate: Date
)

View File

@ -0,0 +1,9 @@
package net.dankito.banking.fints.rest.model.dto.response
open class AddAccountResponseDto(
successful: Boolean,
errorMessage: String?,
open val bank: BankResponseDto,
open val accounts: List<BankAccountResponseDto>
) : ResponseDtoBase(successful, errorMessage)

View File

@ -0,0 +1,26 @@
package net.dankito.banking.fints.rest.model.dto.response
import net.dankito.banking.fints.response.segments.AccountType
import net.dankito.utils.multiplatform.Date
import java.math.BigDecimal
open class BankAccountResponseDto(
open val accountIdentifier: String,
open val subAccountAttribute: String?,
open val iban: String?,
open val accountType: AccountType?,
open val currency: String?,
open val accountHolderName: String,
open val productName: String?,
open val supportsRetrievingBalance: Boolean,
open val supportsRetrievingAccountTransactions: Boolean,
open val supportsTransferringMoney: Boolean,
open val supportsInstantPaymentMoneyTransfer: Boolean,
open val successfullyRetrievedData: Boolean,
open val balance: BigDecimal?,
open val retrievedTransactionsFrom: Date?,
open val retrievedTransactionsTo: Date?,
open var bookedTransactions: Collection<AccountTransactionResponseDto>,
open var unbookedTransactions: Collection<Any>
)

View File

@ -0,0 +1,22 @@
package net.dankito.banking.fints.rest.model.dto.response
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
open class BankResponseDto(
open val bankCode: String,
open val userName: String,
open val finTs3ServerAddress: String,
open val bic: String,
open val bankName: String,
open val userId: String,
open val customerName: String,
open val usersTanMethods: List<TanMethodResponseDto>,
open val selectedTanMethod: TanMethodResponseDto?,
open val tanMedia: List<TanMedium>,
open val supportedHbciVersions: List<String>
)

View File

@ -0,0 +1,14 @@
package net.dankito.banking.fints.rest.model.dto.response
import java.math.BigDecimal
open class GetAccountTransactionsResponseDto(
open val identifier: String,
open val productName: String?,
successful: Boolean,
errorMessage: String?,
open val balance: BigDecimal?,
open var bookedTransactions: Collection<AccountTransactionResponseDto>,
open var unbookedTransactions: Collection<Any>
) : ResponseDtoBase(successful, errorMessage)

View File

@ -0,0 +1,6 @@
package net.dankito.banking.fints.rest.model.dto.response
open class GetAccountsTransactionsResponseDto(
open val accounts: List<GetAccountTransactionsResponseDto>
)

View File

@ -0,0 +1,7 @@
package net.dankito.banking.fints.rest.model.dto.response
open class ResponseDtoBase(
open val successful: Boolean,
open val errorMessage: String?
)

View File

@ -0,0 +1,15 @@
package net.dankito.banking.fints.rest.model.dto.response
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat
import net.dankito.banking.fints.model.TanMethodType
open class TanMethodResponseDto(
open val displayName: String,
open val bankInternalMethodCode: String,
open val type: TanMethodType,
open val hhdVersion: String? = null,
open val maxTanInputLength: Int? = null,
open val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric,
open val nameOfTanMediumRequired: Boolean = false
)

View File

@ -0,0 +1,134 @@
package net.dankito.banking.fints.rest.service
import net.dankito.banking.bankfinder.InMemoryBankFinder
import net.dankito.banking.fints.FinTsClient
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.response.BankResponse
import net.dankito.banking.fints.response.client.AddAccountResponse
import net.dankito.banking.fints.response.client.GetTransactionsResponse
import net.dankito.banking.fints.rest.model.BankAccessData
import net.dankito.banking.fints.rest.model.EnterTanContext
import net.dankito.banking.fints.rest.model.EnteringTanRequested
import net.dankito.banking.fints.rest.model.dto.request.AccountRequestDto
import net.dankito.banking.fints.rest.model.dto.request.GetAccountsTransactionsRequestDto
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicReference
import javax.enterprise.context.ApplicationScoped
@ApplicationScoped
class fints4kService {
protected val bankFinder = InMemoryBankFinder()
protected val tanRequests = mutableMapOf<String, EnterTanContext>()
fun getAddAccountResponse(accessData: BankAccessData): AddAccountResponse {
val (bank, errorMessage) = mapToBankData(accessData)
if (errorMessage != null) {
return AddAccountResponse(BankResponse(false, errorMessage = errorMessage), bank)
}
return getAccountData(bank)
}
// TODO: as in most cases we really just want the account data, so just retrieve these without balances and transactions
protected fun getAccountData(bank: BankData): AddAccountResponse {
return getAsyncResponse { client, responseRetrieved ->
client.addAccountAsync(bank) { response ->
responseRetrieved(response)
}
}
}
fun getAccountTransactions(dto: GetAccountsTransactionsRequestDto): List<GetTransactionsResponse> {
val (bank, errorMessage) = mapToBankData(dto.credentials)
if (errorMessage != null) {
return listOf(GetTransactionsResponse(BankResponse(false, errorMessage = errorMessage)))
}
val accountData = getAccountData(bank)
return dto.accounts.map { accountDto ->
val account = findAccount(accountData, accountDto)
return@map if (account != null) {
val parameter = GetTransactionsParameter(account, dto.alsoRetrieveBalance, dto.fromDate, dto.toDate, abortIfTanIsRequired = dto.abortIfTanIsRequired)
getAccountTransactions(bank, parameter)
}
else {
GetTransactionsResponse(BankResponse(false, errorMessage = "Account with identifier '${accountDto.identifier}' not found. Available accounts: ${accountData.bank.accounts.map { it.accountIdentifier }.joinToString(", ")}"))
}
}
}
fun getAccountTransactions(bank: BankData, parameter: GetTransactionsParameter): GetTransactionsResponse {
return getAsyncResponse { client, responseRetrieved ->
client.getTransactionsAsync(parameter, bank) { response ->
responseRetrieved(response)
}
}
}
protected fun <T> getAsyncResponse(executeRequest: (FinTsClient, ((T) -> Unit)) -> Unit): T {
val result = AtomicReference<T>()
val countDownLatch = CountDownLatch(1)
// val client = FinTsClient(SimpleFinTsClientCallback { supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod? ->
val client = FinTsClient(SimpleFinTsClientCallback({ bank, tanChallenge -> handleEnterTan(bank, tanChallenge, countDownLatch, result) }) { supportedTanMethods, suggestedTanMethod ->
suggestedTanMethod
})
executeRequest(client) { response ->
result.set(response)
countDownLatch.countDown()
}
countDownLatch.await()
return result.get()
}
protected fun <T> handleEnterTan(bank: BankData, tanChallenge: TanChallenge, originatingRequestLatch: CountDownLatch, originatingRequestResult: AtomicReference<T>): EnterTanResult {
val enterTanResult = AtomicReference<EnterTanResult>()
val enterTanLatch = CountDownLatch(1)
val tanRequestId = UUID.randomUUID().toString()
originatingRequestResult.set(EnteringTanRequested(tanRequestId, bank, tanChallenge))
originatingRequestLatch.countDown()
tanRequests.put(tanRequestId, EnterTanContext(enterTanResult, enterTanLatch))
enterTanLatch.await()
return enterTanResult.get()
}
protected fun mapToBankData(accessData: BankAccessData): Pair<BankData, String?> {
val bankSearchResult = bankFinder.findBankByBankCode(accessData.bankCode)
val fintsServerAddress = accessData.finTsServerAddress ?: bankSearchResult.firstOrNull { it.pinTanAddress != null }?.pinTanAddress
val bank = BankData(accessData.bankCode, accessData.loginName, accessData.password, fintsServerAddress ?: "", bankSearchResult.firstOrNull()?.bic ?: "")
if (fintsServerAddress == null) {
val errorMessage = if (bankSearchResult.isEmpty()) "No bank found for bank code '${accessData.bankCode}'" else "Bank '${bankSearchResult.firstOrNull()?.name} does not support FinTS 3.0"
return Pair(bank, errorMessage)
}
return Pair(bank, null)
}
protected fun findAccount(allAccounts: AddAccountResponse, accountDto: AccountRequestDto): AccountData? {
return allAccounts.bank.accounts.firstOrNull { it.accountIdentifier == accountDto.identifier }
}
}

View File

@ -0,0 +1,5 @@
quarkus.http.port=5555
# enable https support in native builds
quarkus.native.enable-https-url-handler=true
quarkus.native.enable-all-security-services=true

View File

@ -0,0 +1,6 @@
package net.dankito.banking.fints.rest
import io.quarkus.test.junit.NativeImageTest
@NativeImageTest
class NativeFints4kIT : ExampleResourceTest()

View File

@ -0,0 +1,20 @@
package net.dankito.banking.fints.rest
import io.quarkus.test.junit.QuarkusTest
import io.restassured.RestAssured.given
import org.hamcrest.CoreMatchers.`is`
import org.junit.jupiter.api.Test
@QuarkusTest
class fints4kResourceTest {
@Test
fun testHelloEndpoint() {
given()
.`when`().get("/hello")
.then()
.statusCode(200)
.body(`is`("hello"))
}
}

View File

@ -59,8 +59,10 @@ project(':RoomBankingPersistence').projectDir = "$rootDir/persistence/database/R
/* REST APIs */ /* REST APIs */
include ':BankFinderRest' include ':BankFinderRest'
include ':fints4kRest'
project(':BankFinderRest').projectDir = "$rootDir/rest/BankFinderRest/" as File project(':BankFinderRest').projectDir = "$rootDir/rest/BankFinderRest/" as File
project(':fints4kRest').projectDir = "$rootDir/rest/fints4kRest/" as File