Skip to content

Instantly share code, notes, and snippets.

@Kalimaha
Last active November 8, 2019 05:00
Show Gist options
  • Save Kalimaha/51684b1c428b1377077c35dc8696d775 to your computer and use it in GitHub Desktop.
Save Kalimaha/51684b1c428b1377077c35dc8696d775 to your computer and use it in GitHub Desktop.
ESM mapper with monads
@Component
open class PushAccountMapper @Autowired
constructor(private val validator: Validator, private val objectMapper: ObjectMapper) : (String) -> CardProvisioningRequest {
@Throws(ServiceException::class)
override fun invoke(payload: String): CardProvisioningRequest {
try {
val request = objectMapper.readValue(payload, CardProvisioningRequest::class.java)
val violations = validator.validate(request)
if (violations.isNotEmpty()) {
val stringBuilder = StringBuilder()
violations.forEach { stringBuilder.append("${it.propertyPath} ${it.message}") }
throw ServiceException(PushAccountError.REQUEST_VALIDATION_ERROR, "Invalid message content: $stringBuilder")
}
return request
} catch (e: IOException) {
throw ServiceException(PushAccountError.REQUEST_VALIDATION_ERROR, e.localizedMessage, e)
}
}
}
@Component
open class PushAccountMapperMonads @Autowired
constructor(private val validator: Validator, private val objectMapper: ObjectMapper) : (String) -> Either<Throwable, CardProvisioningRequest> {
override fun invoke(payload: String): Either<Throwable, CardProvisioningRequest> =
Try
.of { objectMapper.readValue(payload, CardProvisioningRequest::class.java) }
.toEither()
.flatMap { validatePayload(validator, it) }
private fun validatePayload(validator: Validator, request: CardProvisioningRequest): Either<Throwable, CardProvisioningRequest> {
val violations = validator.validate(request)
return if (violations.isNotEmpty()) {
val stringBuilder = StringBuilder()
violations.forEach { stringBuilder.append("${it.propertyPath} ${it.message}") }
Either.left(Exception("Invalid message content: $stringBuilder"))
} else {
Either.right(request)
}
}
}
package au.com.mebank.integration.dwbfaprovisioning.operation.pushAccount
import au.com.mebank.integration.dwbfaprovisioning.models.CardProvisioningRequest
import com.fasterxml.jackson.databind.ObjectMapper
import io.vavr.control.Either
import io.vavr.control.Try
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import javax.validation.Validator
@Component
open class PushAccountMapperMonads @Autowired
constructor(private val validator: Validator, private val objectMapper: ObjectMapper) : (String) -> Either<Throwable, CardProvisioningRequest> {
override fun invoke(payload: String): Either<Throwable, CardProvisioningRequest> =
Try
.of { objectMapper.readValue(payload, CardProvisioningRequest::class.java) }
.toEither()
.flatMap { validatePayload(validator, it) }
private fun validatePayload(validator: Validator, request: CardProvisioningRequest): Either<Throwable, CardProvisioningRequest> {
val violations = validator.validate(request)
return if (violations.isNotEmpty()) {
val stringBuilder = StringBuilder()
violations.forEach { stringBuilder.append("${it.propertyPath} ${it.message}") }
Either.left(Exception("Invalid message content: $stringBuilder"))
} else {
Either.right(request)
}
}
}
package au.com.mebank.integration.dwbfaprovisioning.operation
import au.com.mebank.integration.dwbfaprovisioning.models.CardProvisioningRequest
import au.com.mebank.integration.dwbfaprovisioning.operation.pushAccount.PushAccountMapperMonads
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class PushAccountMapperMonadsTest {
@Autowired
lateinit var pushAccountMapperMonads: PushAccountMapperMonads
lateinit var payload: String
@BeforeEach
fun init() {
payload = """
{
"requestId": "ME00042", "cardHostId": "4444333322221111",
"walletProvider": "applePay", "platform": "ios"
}
""".trimIndent()
}
@Test
fun `returns an instance of CardProvisioningRequest`() {
assertThat(pushAccountMapperMonads.invoke(payload).get())
.isEqualTo(CardProvisioningRequest(
"ME00042", "4444333322221111", "applePay", "ios"
))
}
@Nested
inner class WhenThereAreErrors {
@Nested
inner class WhenAFieldIsBlank {
@BeforeEach
fun init() {
payload = """
{
"requestId": "", "cardHostId": "4444333322221111",
"walletProvider": "applePay", "platform": "ios"
}
""".trimIndent()
}
@Test
fun `returns an error message`() {
assertThat(pushAccountMapperMonads.invoke(payload).left.localizedMessage)
.isEqualTo("Invalid message content: requestId must not be blank")
}
}
@Nested
inner class WhenTheFieldsDoNotBelongToTheEnumerations {
@BeforeEach
fun init() {
payload = """
{
"requestId": "ME00042", "cardHostId": "4444333322221111",
"walletProvider": "applePay", "platform": "nokia"
}
""".trimIndent()
}
@Test
fun `returns an error message`() {
assertThat(pushAccountMapperMonads.invoke(payload).left.localizedMessage)
.isEqualTo("Invalid message content: platform must match \"android|ios\"")
}
}
@Nested
inner class WhenThereAreMissingFields {
@BeforeEach
fun init() {
payload = "{\"requestId\": \"ME00042\"}"
}
@Test
fun `returns an error message`() {
assertThat(pushAccountMapperMonads.invoke(payload).left.localizedMessage)
.contains("missing (therefore NULL) value for creator parameter cardHostId")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment