-
-
Save jorgebo10/c7fdfde6b69e6e23b81b2432cd09e07a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package funcore | |
import arrow.core.Either | |
import arrow.core.left | |
import arrow.core.right | |
import io.travelx.flights.itineraryreservations.core.domain.model.* | |
sealed interface ReservationDomainError | |
@JvmInline | |
value class ReservationNotFoundDomainError private constructor(val reservationId: ReservationId) : ReservationDomainError { | |
companion object { | |
fun fromId(id: String) = ReservationNotFoundDomainError(ReservationId(id)) | |
fun fromReservationId(reservationId: ReservationId) = ReservationNotFoundDomainError(reservationId) | |
} | |
} | |
object NotEnoughFundsDomainError : ReservationDomainError | |
object InvalidStatusDomainError : ReservationDomainError | |
object NoDomainError : ReservationDomainError | |
fun payReservation(reservation: Reservation): Either<ReservationDomainError, Reservation> = | |
if (reservation.reservationId.value == "invalidStatusError") { | |
InvalidStatusDomainError.left() | |
} else { | |
reservation.copy(status = ReservationStatus.PAID).right() | |
} | |
fun createReservation(reservationId: ReservationId) = | |
Reservation( | |
reservationId, | |
Pnr("XDFG"), | |
PurchaseId("12"), | |
null, | |
ReservationStatus.BOOKED, | |
null, | |
Itinerary( | |
ItineraryId("eee"), | |
emptyList(), | |
priceInfo = PriceInfo( | |
adults = PaxPrice( | |
passengerType = "ADT", | |
totalFare = "1", | |
baseFare = "1", | |
baseTax = "1", | |
ptcTotal = "1", | |
totalTax = "0", | |
quantity = 1U, | |
fareType = "PUB", | |
extraTaxes = emptyList(), | |
policies = emptyList(), | |
surcharges = emptyList(), | |
corporateIds = listOf("corporateId"), | |
), | |
currency = "ARS" | |
), | |
createdAt = "", | |
validatingCarrier = "AR", | |
provider = "FO", | |
flightType = "ONE_WAY", | |
isDomestic = false, | |
includedBaggageOnly = false, | |
), | |
contextData = ContextData(), | |
fareRules = Rules(emptyMap()) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@file:OptIn(ExperimentalCoroutinesApi::class) | |
package mutableshell | |
import arrow.core.* | |
import io.travelx.flights.itineraryreservations.core.domain.* | |
import io.travelx.flights.itineraryreservations.core.domain.model.ReservationId | |
import io.travelx.flights.itineraryreservations.core.domain.model.ReservationStatus.* | |
import io.travelx.flights.itineraryreservations.infrastructure.repository.funcore.* | |
import kotlinx.coroutines.ExperimentalCoroutinesApi | |
import kotlinx.coroutines.test.runTest | |
import org.junit.jupiter.api.Test | |
internal class EffectTest { | |
@Test | |
fun router() = runTest { | |
/* | |
This creates a description of a program that ONLY when executed will trigger a side effect. | |
e.g.: an exception or access to a database | |
So in a functional architecture it is part of the functional core, however the point where it is | |
executed belongs to the imperative shell where exceptions will be handled | |
iterate over these reservationIds for testing different cases | |
storeException notFoundError ok storeException getByIdException invalidStatusError notEnoughFundsError invalidStatusError | |
*/ | |
PayReservationUseCase(ReservationRepositoryImpl(), PaymentApi()) | |
.invoke(ReservationId("storeException")) | |
.fold( | |
{ | |
throw IllegalArgumentException("Problem details status=500 title=reservations/500", it) | |
}, { | |
when (it) { | |
is ReservationNotFoundDomainError -> throw IllegalArgumentException("Problem details status=404 title=reservations/404") | |
NoDomainError -> TODO("this might never happen") | |
InvalidStatusDomainError -> throw IllegalArgumentException("Problem details status=400 title=reservations/400 detail=invalidStatus") | |
NotEnoughFundsDomainError -> throw IllegalArgumentException("Problem details status=400 title=reservations/400 detail=notEnoughFunds") | |
} | |
}, | |
{ | |
println("reservation updated") | |
}) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package funcore | |
import arrow.core.continuations.Effect | |
import arrow.core.continuations.effect | |
import io.travelx.flights.itineraryreservations.core.domain.model.Reservation | |
import io.travelx.flights.itineraryreservations.core.domain.model.ReservationId | |
class ReservationRepositoryImpl { | |
fun getById(reservationId: ReservationId): Effect<ReservationDomainError, Reservation> = effect { | |
when (reservationId.value) { | |
"notFoundError" -> { | |
shift(ReservationNotFoundDomainError.fromReservationId(reservationId)) | |
} | |
"ok" -> { | |
createReservation(reservationId) | |
} | |
"storeException" -> { | |
createReservation(reservationId) | |
} | |
"getByIdException" -> { | |
throw java.lang.IllegalArgumentException("getByIdException") | |
} | |
"invalidStatusError" -> { | |
createReservation(reservationId) | |
} | |
"notEnoughFundsError" -> { | |
createReservation(reservationId) | |
} | |
else -> { | |
throw IllegalArgumentException("Exception") | |
} | |
} | |
} | |
fun store(reservation: Reservation): Effect<Unit, Unit> = effect { | |
if (reservation.reservationId.value == "storeException") { | |
throw IllegalArgumentException("Store exception") | |
} else | |
Unit | |
} | |
} | |
class PaymentApi { | |
fun registerPaymentForReservation(reservation: Reservation): Effect<ReservationDomainError, String> = effect { | |
if (reservation.reservationId.value == "notEnoughFundsError") { | |
shift(NotEnoughFundsDomainError) | |
} else { | |
"Ok" | |
} | |
} | |
} | |
class PayReservationUseCase( | |
private val repository: ReservationRepositoryImpl, | |
private val paymentApi: PaymentApi, | |
) { | |
fun invoke(reservationId: ReservationId): Effect<ReservationDomainError, Unit> = effect { | |
val reservation = repository.getById(reservationId).bind() | |
val paidReservation = payReservation(reservation).bind() | |
paymentApi.registerPaymentForReservation(paidReservation).bind() | |
repository.store(paidReservation).handleError { shift(NoDomainError) }.bind() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment