Started ResponseParser

This commit is contained in:
dankl 2019-10-05 20:26:57 +02:00 committed by dankito
parent 1e7aa6f7b9
commit 7f6752fa6d
7 changed files with 276 additions and 0 deletions

View File

@ -0,0 +1,10 @@
package net.dankito.fints.response
import net.dankito.fints.messages.segmente.id.ISegmentId
enum class InstituteSegmentId(override val id: String) : ISegmentId {
Synchronization("HISYN")
}

View File

@ -0,0 +1,45 @@
package net.dankito.fints.response
import net.dankito.fints.messages.segmente.id.ISegmentId
import net.dankito.fints.messages.segmente.id.MessageSegmentId
import net.dankito.fints.response.segments.ReceivedMessageHeader
import net.dankito.fints.response.segments.ReceivedSegment
open class Response(
val didReceiveResponse: Boolean,
val didResponseContainErrors: Boolean,
val receivedResponse: String? = null,
val receivedSegments: List<ReceivedSegment> = listOf(),
val error: Exception? = null
) {
open val successful: Boolean
get() = didReceiveResponse && didResponseContainErrors == false
open val messageHeader: ReceivedMessageHeader?
get() = getFirstSegmentById(MessageSegmentId.MessageHeader)
open fun <T : ReceivedSegment> getFirstSegmentById(id: ISegmentId): T? {
return getFirstSegmentById(id.id)
}
open fun <T : ReceivedSegment> getFirstSegmentById(id: String): T? {
return receivedSegments.firstOrNull { it.segmentId == id } as T?
}
open fun getSegmentsById(id: ISegmentId): List<ReceivedSegment> {
return getSegmentsById(id.id)
}
open fun getSegmentsById(id: String): List<ReceivedSegment> {
return receivedSegments.filter { it.segmentId == id }
}
override fun toString(): String {
return "Successful? $successful"
}
}

View File

@ -0,0 +1,81 @@
package net.dankito.fints.response
import net.dankito.fints.messages.Separators
import net.dankito.fints.messages.segmente.id.MessageSegmentId
import net.dankito.fints.response.segments.ReceivedMessageHeader
import net.dankito.fints.response.segments.ReceivedSegment
import net.dankito.fints.response.segments.ReceivedSynchronization
import org.slf4j.LoggerFactory
open class ResponseParser {
companion object {
private val log = LoggerFactory.getLogger(ResponseParser::class.java)
}
open fun parse(response: String): Response {
try {
val segments = response.split(Separators.SegmentSeparator)
val parsedSegments = segments.mapNotNull { parseSegment(it) }
return Response(true, determineContainsErrors(parsedSegments), response, parsedSegments)
} catch (e: Exception) {
log.error("Could not parse response '$response'", e)
return Response(true, true, response, error = e)
}
}
protected open fun determineContainsErrors(parsedSegments: List<ReceivedSegment>): Boolean {
return false // TODO
}
protected open fun parseSegment(segment: String): ReceivedSegment? {
try {
if (segment.isNotEmpty()) { // filter out empty lines
val dataElementGroups = segment.split(Separators.DataElementGroupsSeparator)
val segmentId = segment.substring(0, segment.indexOf(Separators.DataElementsSeparator))
return parseSegment(segment, segmentId, dataElementGroups)
}
} catch (e: Exception) {
log.error("Could not parse segment '$segment'", e) // TODO: what to do here, how to inform user?
}
return null
}
protected open fun parseSegment(segment: String, segmentId: String, dataElementGroups: List<String>): ReceivedSegment? {
return when (segmentId) {
MessageSegmentId.MessageHeader.id -> parseMessageHeaderSegment(segment, dataElementGroups)
InstituteSegmentId.Synchronization.id -> parseSynchronization(segment, dataElementGroups)
else -> null
}
}
protected open fun parseMessageHeaderSegment(segment: String, dataElementGroups: List<String>): ReceivedMessageHeader {
val messageSize = dataElementGroups[1].toInt()
val finTsVersion = dataElementGroups[2].toInt()
val dialogId = dataElementGroups[3]
val messageNumber = dataElementGroups[4].toInt()
return ReceivedMessageHeader(messageSize, finTsVersion, dialogId, messageNumber, segment)
}
protected open fun parseSynchronization(segment: String, dataElementGroups: List<String>): ReceivedSynchronization {
val customerSystemId = dataElementGroups[1]
val lastMessageNumber = if (dataElementGroups.size > 2) dataElementGroups[2] else null
val securityReferenceNumberForSigningKey = if (dataElementGroups.size > 3) dataElementGroups[3] else null
val securityReferenceNumberForDigitalSignature = if (dataElementGroups.size > 4) dataElementGroups[4] else null
return ReceivedSynchronization(segment, customerSystemId, lastMessageNumber,
securityReferenceNumberForSigningKey, securityReferenceNumberForDigitalSignature)
}
}

View File

@ -0,0 +1,11 @@
package net.dankito.fints.response.segments
open class ReceivedMessageHeader(
val messageSize: Int,
val finTsVersion: Int,
val dialogId: String,
val messageNumber: Int,
segmentString: String)
: ReceivedSegment(segmentString)

View File

@ -0,0 +1,34 @@
package net.dankito.fints.response.segments
import net.dankito.fints.messages.Separators
open class ReceivedSegment(
val segmentId: String,
val segmentNumber: Int,
val segmentVersion: Int,
val referenceSegmentNumber: Int? = null,
val segmentString: String
) {
/**
* Convenience constructor for setting [segmentId], [segmentNumber] and [segmentVersion].
*
* [segmentHeader] has to have three or four elements like [net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf] has.
*/
constructor(segmentHeader: List<String>, segmentString: String) :
this(segmentHeader[0], segmentHeader[1].toInt(), segmentHeader[2].toInt(),
if (segmentHeader.size >= 4) segmentHeader[3].toInt() else null, segmentString)
constructor(segmentString: String) : this(
segmentString.split(Separators.DataElementGroupsSeparator).first().split(Separators.DataElementsSeparator),
segmentString
)
override fun toString(): String {
return segmentId
}
}

View File

@ -0,0 +1,31 @@
package net.dankito.fints.response.segments
import net.dankito.fints.messages.datenelemente.implementierte.Synchronisierungsmodus
open class ReceivedSynchronization(
segmentString: String,
/**
* Only set if [Synchronisierungsmodus] was set to [Synchronisierungsmodus.NeueKundensystemIdZurueckmelden]
*/
val customerSystemId: String? = null,
/**
* Only set if [Synchronisierungsmodus] was set to [Synchronisierungsmodus.LetzteVerarbeiteteNachrichtennummerZurueckmelden]
*/
val lastMessageNumber: String? = null,
/**
* Only set if [Synchronisierungsmodus] was set to [Synchronisierungsmodus.SignaturIdZurueckmelden]
*/
val securityReferenceNumberForSigningKey: String? = null,
/**
* Only set if [Synchronisierungsmodus] was set to [Synchronisierungsmodus.SignaturIdZurueckmelden]
*/
val securityReferenceNumberForDigitalSignature: String? = null
)
: ReceivedSegment(segmentString)

View File

@ -0,0 +1,64 @@
package net.dankito.fints.response
import net.dankito.fints.messages.segmente.id.ISegmentId
import net.dankito.fints.messages.segmente.id.MessageSegmentId
import net.dankito.fints.response.segments.ReceivedMessageHeader
import net.dankito.fints.response.segments.ReceivedSynchronization
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class ResponseParserTest {
private val underTest = ResponseParser()
@Test
fun parseMessageHeader() {
// when
val result = underTest.parse("HNHBK:1:3+000000000596+300+817407729605=887211382312BLB4=+2+817407729605=887211382312BLB4=:2")
// then
assertSuccessfullyParsedSegment(result, MessageSegmentId.MessageHeader, 1, 3)
assertThat(result.messageHeader).isNotNull
val header = result.receivedSegments.first() as ReceivedMessageHeader
assertThat(header.messageSize).isEqualTo(596)
assertThat(header.finTsVersion).isEqualTo(300)
assertThat(header.dialogId).isEqualTo("817407729605=887211382312BLB4=")
assertThat(header.messageNumber).isEqualTo(2)
}
@Test
fun parseSynchronization() {
// when
val result = underTest.parse("HISYN:173:4:6+WL/2/Trhmm0BAAAjIADlyFkXrAQA")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.Synchronization, 173, 4, 6)
val segment = result.receivedSegments.first() as ReceivedSynchronization
assertThat(segment.customerSystemId).isEqualTo("WL/2/Trhmm0BAAAjIADlyFkXrAQA")
}
private fun assertSuccessfullyParsedSegment(result: Response, segmentId: ISegmentId, segmentNumber: Int,
segmentVersion: Int, referenceSegmentNumber: Int? = null) {
assertThat(result.successful).isTrue()
assertThat(result.receivedResponse).isNotNull()
assertThat(result.receivedSegments).hasSize(1)
val segment = result.receivedSegments.first()
assertThat(segment.segmentId).isEqualTo(segmentId.id)
assertThat(segment.segmentNumber).isEqualTo(segmentNumber)
assertThat(segment.segmentVersion).isEqualTo(segmentVersion)
assertThat(segment.referenceSegmentNumber).isEqualTo(referenceSegmentNumber)
}
}