diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt new file mode 100644 index 00000000..1a16a4f5 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt @@ -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") + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt new file mode 100644 index 00000000..c7945e67 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt @@ -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 = listOf(), + val error: Exception? = null +) { + + open val successful: Boolean + get() = didReceiveResponse && didResponseContainErrors == false + + + open val messageHeader: ReceivedMessageHeader? + get() = getFirstSegmentById(MessageSegmentId.MessageHeader) + + open fun getFirstSegmentById(id: ISegmentId): T? { + return getFirstSegmentById(id.id) + } + + open fun getFirstSegmentById(id: String): T? { + return receivedSegments.firstOrNull { it.segmentId == id } as T? + } + + open fun getSegmentsById(id: ISegmentId): List { + return getSegmentsById(id.id) + } + + open fun getSegmentsById(id: String): List { + return receivedSegments.filter { it.segmentId == id } + } + + + override fun toString(): String { + return "Successful? $successful" + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt new file mode 100644 index 00000000..e5c7aa1e --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt @@ -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): 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): 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): 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): 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) + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedMessageHeader.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedMessageHeader.kt new file mode 100644 index 00000000..2b18359f --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedMessageHeader.kt @@ -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) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedSegment.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedSegment.kt new file mode 100644 index 00000000..ba7cc829 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedSegment.kt @@ -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, 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 + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedSynchronization.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedSynchronization.kt new file mode 100644 index 00000000..dda3679c --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ReceivedSynchronization.kt @@ -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) \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt new file mode 100644 index 00000000..c9b0c094 --- /dev/null +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt @@ -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) + } + +} \ No newline at end of file