libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 307db11a19cd3e8e6319f6570f001cc84303e715
parent 626c4bb8c8c3efcad8537acd132afd8462c5e9e1
Author: Florian Dold <florian.dold@gmail.com>
Date:   Sun, 14 Jun 2020 20:32:39 +0530

clearer structure for EBICS params and fetch spec (latter is WIP)

Diffstat:
M.idea/dictionaries/dold.xml | 2++
Mnexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 8++------
Mnexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt | 126++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 36+++++++++++++++++-------------------
4 files changed, 88 insertions(+), 84 deletions(-)

diff --git a/.idea/dictionaries/dold.xml b/.idea/dictionaries/dold.xml @@ -4,7 +4,9 @@ <w>affero</w> <w>combinators</w> <w>ebics</w> + <w>libeufin</w> <w>payto</w> + <w>sqlite</w> </words> </dictionary> </component> \ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -202,18 +202,14 @@ suspend fun fetchEbicsC5x( historyType: String, client: HttpClient, bankConnectionId: String, - start: String?, // dashed date YYYY-MM(01-12)-DD(01-31) - end: String?, // dashed date YYYY-MM(01-12)-DD(01-31) + orderParams: EbicsOrderParams, subscriberDetails: EbicsClientSubscriberDetails ) { - val orderParamsJson = EbicsStandardOrderParamsJson( - EbicsDateRangeJson(start, end) - ) val response = doEbicsDownloadTransaction( client, subscriberDetails, historyType, - orderParamsJson.toOrderParams() + orderParams ) when (historyType) { "C52" -> { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt @@ -22,6 +22,7 @@ package tech.libeufin.nexus import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonTypeName +import com.fasterxml.jackson.annotation.JsonValue import com.fasterxml.jackson.databind.JsonNode import tech.libeufin.util.* import java.time.LocalDate @@ -31,45 +32,51 @@ data class EbicsBackupRequestJson( val passphrase: String ) -data class NexusErrorJson( - val message: String +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "paramType" ) +@JsonSubTypes( + JsonSubTypes.Type(value = EbicsStandardOrderParamsDateJson::class, name = "standard-date-range"), + JsonSubTypes.Type(value = EbicsStandardOrderParamsEmptyJson::class, name = "standard-empty"), + JsonSubTypes.Type(value = EbicsGenericOrderParamsJson::class, name = "generic") +) +abstract class EbicsOrderParamsJson { + abstract fun toOrderParams(): EbicsOrderParams +} + +@JsonTypeName("generic") +class EbicsGenericOrderParamsJson( + val params: Map<String, String> +) : EbicsOrderParamsJson() { + override fun toOrderParams(): EbicsOrderParams { + return EbicsGenericOrderParams(params) + } +} + +@JsonTypeName("standard-empty") +class EbicsStandardOrderParamsEmptyJson : EbicsOrderParamsJson() { + override fun toOrderParams(): EbicsOrderParams { + return EbicsStandardOrderParams(null) + } +} -data class EbicsStandardOrderParamsJson(val dateRange: EbicsDateRangeJson?) { - fun toOrderParams(): EbicsOrderParams { - var dateRange: EbicsDateRange? = if (this.dateRange != null) { +@JsonTypeName("standard-date-range") +class EbicsStandardOrderParamsDateJson( + val start: String, + val end: String +) : EbicsOrderParamsJson() { + override fun toOrderParams(): EbicsOrderParams { + val dateRange: EbicsDateRange? = EbicsDateRange( - LocalDate.parse(this.dateRange.start ?: "1970-01-31"), - LocalDate.parse(this.dateRange.end ?: LocalDateTime.now().toDashedDate()) + LocalDate.parse(this.start), + LocalDate.parse(this.end) ) - } else { - null - } return EbicsStandardOrderParams(dateRange) } } -data class EbicsDateRangeJson( - /** ISO 8601 calendar dates: YEAR-MONTH(01-12)-DAY(1-31) */ - val start: String?, - val end: String? -) - -data class EbicsPubKeyInfo( - val authPub: String, - val encPub: String, - val sigPub: String -) - -data class ProtocolAndVersionJson( - val protocol: String, - val version: String -) - -data class EbicsHevResponseJson( - val versions: List<ProtocolAndVersionJson> -) - data class EbicsErrorDetailJson( val type: String, val ebicsReturnCode: String @@ -79,34 +86,6 @@ data class EbicsErrorJson( val error: EbicsErrorDetailJson ) -/** Instructs the nexus to CREATE a new Ebics subscriber. - * Note that the nexus user to which the subscriber must be - * associated is extracted from other HTTP details. - * - * This same structure can be user to SHOW one Ebics subscriber - * existing at the nexus. - */ -data class EbicsSubscriber( - val ebicsURL: String, - val hostID: String, - val partnerID: String, - val userID: String, - val systemID: String? -) - -data class RawPayments( - var payments: MutableList<RawPayment> = mutableListOf() -) - -/************************************************* - * API types (used as requests/responses types) * - *************************************************/ -data class BankTransport( - val transport: String, - val backup: Any? = null, - val data: Any? -) - data class BankConnectionInfo( val name: String, val type: String @@ -138,6 +117,35 @@ data class EbicsKeysBackupJson( val sigBlob: String ) +enum class FetchLevel(@get:JsonValue val jsonName: String) { + REPORT("report"), STATEMENT("statement"), ALL("all"); +} + +/** + * Instructions on what range to fetch from the bank, + * and which source(s) to use. + * + * Intended to be convenient to specify. + */ +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "rangeType" +) +@JsonSubTypes( + JsonSubTypes.Type(value = FetchSpecLatestJson::class, name = "latest"), + JsonSubTypes.Type(value = FetchSpecAllJson::class, name = "all"), + JsonSubTypes.Type(value = FetchSpecPreviousDaysJson::class, name = "previousDays") +) +abstract class FetchSpecJson( + val level: FetchLevel, + val bankConnection: String? +) + +class FetchSpecLatestJson(level: FetchLevel, bankConnection: String) : FetchSpecJson(level, bankConnection) +class FetchSpecAllJson(level: FetchLevel, bankConnection: String) : FetchSpecJson(level, bankConnection) +class FetchSpecPreviousDaysJson(level: FetchLevel, bankConnection: String, val number: Int) : + FetchSpecJson(level, bankConnection) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -79,7 +79,7 @@ import java.util.zip.InflaterInputStream import javax.crypto.EncryptedPrivateKeyInfo data class NexusError(val statusCode: HttpStatusCode, val reason: String) : - Exception("${reason} (HTTP status $statusCode)") + Exception("$reason (HTTP status $statusCode)") val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus") @@ -314,8 +314,7 @@ suspend fun downloadTalerFacadesTransactions(httpClient: HttpClient, type: Strin client = httpClient, type = type, userId = it.first, - accountid = it.second, - ct = CollectedTransaction(null, null, null) + accountid = it.second ) } } @@ -332,12 +331,11 @@ fun ApplicationCall.expectUrlParameter(name: String): String { ?: throw EbicsProtocolError(HttpStatusCode.BadRequest, "Parameter '$name' not provided in URI") } -suspend fun fetchTransactionsInternal( +private suspend fun fetchTransactionsInternal( client: HttpClient, type: String, // C53 or C52 userId: String, - accountid: String, - ct: CollectedTransaction + accountid: String ) { val res = transaction { val acct = NexusBankAccountEntity.findById(accountid) @@ -363,12 +361,13 @@ suspend fun fetchTransactionsInternal( } when (res.connectionType) { "ebics" -> { + // FIXME(dold): Support fetching not only the latest transactions. + // It's not clear what's the nicest way to support this. fetchEbicsC5x( type, client, res.connectionName, - ct.start, - ct.end, + EbicsStandardOrderParams(), res.subscriberDetails ) ingestBankMessagesIntoAccount(res.connectionName, accountid) @@ -687,8 +686,7 @@ fun serverMain(dbName: String) { client, "C53", user.id.value, - accountid, - ct + accountid ) call.respondText("Collection performed") return@post @@ -935,10 +933,10 @@ fun serverMain(dbName: String) { } post("/bank-connections/{connid}/ebics/fetch-c53") { - val paramsJson = if (call.request.hasBody()) { - call.receive<EbicsDateRangeJson>() + val ebicsOrderParams = if (call.request.hasBody()) { + call.receive<EbicsOrderParamsJson>().toOrderParams() } else { - null + EbicsStandardOrderParams() } val ret = transaction { val user = authenticateRequest(call.request) @@ -952,15 +950,15 @@ fun serverMain(dbName: String) { } } - fetchEbicsC5x("C53", client, ret.connId, paramsJson?.start, paramsJson?.end, ret.subscriber) + fetchEbicsC5x("C53", client, ret.connId, ebicsOrderParams, ret.subscriber) call.respond(object {}) } post("/bank-connections/{connid}/ebics/fetch-c52") { - val paramsJson = if (call.request.hasBody()) { - call.receive<EbicsDateRangeJson>() + val ebicsOrderParams = if (call.request.hasBody()) { + call.receive<EbicsOrderParamsJson>().toOrderParams() } else { - null + EbicsStandardOrderParams() } val ret = transaction { val user = authenticateRequest(call.request) @@ -974,7 +972,7 @@ fun serverMain(dbName: String) { } } - fetchEbicsC5x("C52", client, ret.connId, paramsJson?.start, paramsJson?.end, ret.subscriber) + fetchEbicsC5x("C52", client, ret.connId, ebicsOrderParams, ret.subscriber) call.respond(object {}) } @@ -1093,7 +1091,7 @@ fun serverMain(dbName: String) { if (orderType.length != 3) { throw NexusError(HttpStatusCode.BadRequest, "ebics order type must be three characters") } - val paramsJson = call.receiveOrNull<EbicsStandardOrderParamsJson>() + val paramsJson = call.receiveOrNull<EbicsStandardOrderParamsDateJson>() val orderParams = if (paramsJson == null) { EbicsStandardOrderParams() } else {