Fix tests
This commit is contained in:
parent
efdc50eb1d
commit
87bd780f9f
34 changed files with 230 additions and 303 deletions
|
|
@ -3,15 +3,13 @@ package lu.foyer
|
|||
import zio.*
|
||||
import zio.schema.Schema
|
||||
|
||||
import java.util.UUID
|
||||
final case class Entity[T](entityId: String, data: T, version: Long)
|
||||
final case class Event[T](entityId: String, data: T, eventId: String)
|
||||
|
||||
final case class Entity[T](entityId: UUID, data: T, version: Long)
|
||||
final case class Event[T](entityId: UUID, data: T, eventId: UUID)
|
||||
|
||||
trait StateRepository[Data] extends Repository[Entity[Data], UUID]
|
||||
trait EventRepository[Data] extends Repository[Event[Data], UUID]:
|
||||
def fetchOne(entityId: UUID, eventId: UUID): Task[Option[Event[Data]]]
|
||||
def fetchMany(entityId: UUID, page: Page): Task[Paged[Event[Data]]]
|
||||
trait StateRepository[Data] extends Repository[Entity[Data], String]
|
||||
trait EventRepository[Data] extends Repository[Event[Data], String]:
|
||||
def fetchOne(entityId: String, eventId: String): Task[Option[Event[Data]]]
|
||||
def fetchMany(entityId: String, page: Page): Task[Paged[Event[Data]]]
|
||||
|
||||
trait Reducer[Event, State]:
|
||||
def fromEmpty: PartialFunction[Event, State]
|
||||
|
|
@ -30,13 +28,13 @@ trait CommandHandler[+Command, +Event, +State]:
|
|||
def commandSchema: Schema[?]
|
||||
|
||||
trait CommandHandlerCreate[Command: Schema, Event] extends CommandHandler[Command, Event, Nothing]:
|
||||
def onCommand(entityId: UUID, command: Command): Task[Event]
|
||||
def onCommand(entityId: String, command: Command): Task[Event]
|
||||
val isCreate = true
|
||||
val commandSchema = summon[Schema[Command]]
|
||||
|
||||
trait CommandHandlerUpdate[Command: Schema, Event, State]
|
||||
extends CommandHandler[Command, Event, State]:
|
||||
def onCommand(entityId: UUID, state: State, command: Command): Task[Event]
|
||||
def onCommand(entityId: String, state: State, command: Command): Task[Event]
|
||||
val isCreate = false
|
||||
val commandSchema = summon[Schema[Command]]
|
||||
|
||||
|
|
@ -46,7 +44,7 @@ class CommandEngine[Command, Event, State](
|
|||
val eventRepo: EventRepository[Event],
|
||||
val stateRepo: StateRepository[State]):
|
||||
|
||||
def handleCommand(command: Command, name: String, entityId: UUID)
|
||||
def handleCommand(command: Command, name: String, entityId: String)
|
||||
: Task[(lu.foyer.Event[Event], lu.foyer.Entity[State])] =
|
||||
for
|
||||
handler <- ZIO
|
||||
|
|
@ -61,14 +59,14 @@ class CommandEngine[Command, Event, State](
|
|||
newEntity = Entity(entityId, newState, entityOption.map(_.version).getOrElse(1))
|
||||
_ <- if entityOption.isEmpty then stateRepo.insert(newEntity.entityId, newEntity)
|
||||
else stateRepo.update(newEntity.entityId, newEntity)
|
||||
eventEntity <- Random.nextUUID.map(Event(newEntity.entityId, event, _))
|
||||
eventEntity <- Random.nextUUID.map(id => Event(newEntity.entityId, event, id.toString))
|
||||
_ <- eventRepo.insert(eventEntity.eventId, eventEntity)
|
||||
yield (eventEntity, newEntity)
|
||||
|
||||
private def transition(
|
||||
command: Command,
|
||||
name: String,
|
||||
entityId: UUID,
|
||||
entityId: String,
|
||||
entityOption: Option[Entity[State]],
|
||||
handler: CommandHandler[Command, Event, State]
|
||||
): Task[(Event, Option[State])] = (entityOption, handler) match
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import zio.*
|
|||
import zio.schema.*
|
||||
import zio.schema.annotation.fieldName
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
final case class Page(
|
||||
@fieldName("page[number]")
|
||||
number: Option[Int],
|
||||
|
|
@ -14,6 +12,9 @@ final case class Page(
|
|||
@fieldName("page[totals]")
|
||||
totals: Option[Boolean])
|
||||
derives Schema
|
||||
object Page:
|
||||
val default = Page(None, None, totals = Some(true))
|
||||
|
||||
final case class Paged[T](items: List[T], totals: Option[Long])
|
||||
|
||||
trait Repository[Entity, Id]:
|
||||
|
|
@ -22,15 +23,16 @@ trait Repository[Entity, Id]:
|
|||
def insert(id: Id, entity: Entity): Task[Unit]
|
||||
def update(id: Id, entity: Entity): Task[Unit]
|
||||
|
||||
trait InMemoryRepository[State](entities: Ref[Map[UUID, State]]) extends Repository[State, UUID]:
|
||||
def fetchOne(id: UUID): Task[Option[State]] = entities.get.map(_.get(id))
|
||||
trait InMemoryRepository[State](entities: Ref[Map[String, State]])
|
||||
extends Repository[State, String]:
|
||||
def fetchOne(id: String): Task[Option[State]] = entities.get.map(_.get(id))
|
||||
def fetchMany(page: Page): Task[Paged[State]] = entities.get.map(entities =>
|
||||
val items =
|
||||
entities.values
|
||||
.drop(page.number.getOrElse(0) * page.size.getOrElse(50)).take(page.size.getOrElse(50))
|
||||
Paged(items.toList, if page.totals.getOrElse(false) then Some(entities.size) else None)
|
||||
Paged(items.toList, if page.totals.getOrElse(true) then Some(entities.size) else None)
|
||||
)
|
||||
def insert(id: UUID, entity: State): Task[Unit] =
|
||||
def insert(id: String, entity: State): Task[Unit] =
|
||||
entities.update(entities => entities.updated(id, entity))
|
||||
def update(id: UUID, entity: State): Task[Unit] =
|
||||
def update(id: String, entity: State): Task[Unit] =
|
||||
entities.update(entities => entities.updated(id, entity))
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package clients
|
|||
|
||||
import zio.schema.*
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
enum ClientCommand derives Schema:
|
||||
case Create(
|
||||
lastName: ClientLastName,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import zio.schema.*
|
|||
import zio.schema.annotation.caseName
|
||||
import zio.schema.annotation.discriminatorName
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
@discriminatorName("eventType")
|
||||
sealed trait ClientEvent derives Schema
|
||||
object ClientEvent:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package lu.foyer
|
|||
package clients
|
||||
|
||||
import zio.*
|
||||
import java.util.UUID
|
||||
|
||||
object ClientHandlers:
|
||||
val layer: ULayer[List[CommandHandler[ClientCommand, ClientEvent, ClientState]]] =
|
||||
|
|
@ -16,7 +15,7 @@ object ClientHandlers:
|
|||
|
||||
object CreateHandler extends CommandHandlerCreate[ClientCommand.Create, ClientEvent.Created]:
|
||||
val name = "create"
|
||||
def onCommand(entityId: UUID, command: ClientCommand.Create): Task[ClientEvent.Created] =
|
||||
def onCommand(entityId: String, command: ClientCommand.Create): Task[ClientEvent.Created] =
|
||||
ZIO.succeed(
|
||||
ClientEvent.Created(
|
||||
command.lastName,
|
||||
|
|
@ -32,7 +31,7 @@ object CreateHandler extends CommandHandlerCreate[ClientCommand.Create, ClientEv
|
|||
object UpdateHandler
|
||||
extends CommandHandlerUpdate[ClientCommand.Update, ClientEvent.Updated, ClientState.Actif]:
|
||||
val name = "update"
|
||||
def onCommand(entityId: UUID, state: ClientState.Actif, command: ClientCommand.Update)
|
||||
def onCommand(entityId: String, state: ClientState.Actif, command: ClientCommand.Update)
|
||||
: Task[ClientEvent.Updated] =
|
||||
ZIO.succeed(
|
||||
ClientEvent.Updated(
|
||||
|
|
@ -49,6 +48,6 @@ object UpdateHandler
|
|||
object DisableHandler
|
||||
extends CommandHandlerUpdate[ClientCommand.Disable, ClientEvent.Disabled, ClientState.Actif]:
|
||||
val name = "disable"
|
||||
def onCommand(entityId: UUID, state: ClientState.Actif, command: ClientCommand.Disable)
|
||||
def onCommand(entityId: String, state: ClientState.Actif, command: ClientCommand.Disable)
|
||||
: Task[ClientEvent.Disabled] =
|
||||
ZIO.succeed(ClientEvent.Disabled(command.reason))
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ClientReducer() extends Reducer[ClientEvent, ClientState]:
|
|||
|
||||
override val fromState =
|
||||
case (s: ClientState.Actif, e: ClientEvent.Updated) => s.update(e)
|
||||
case (s: ClientState.Actif, e: ClientEvent.Disabled) => s.disable(e)
|
||||
case (s: ClientState.Actif, _: ClientEvent.Disabled) => s.disable()
|
||||
|
||||
object ClientReducer:
|
||||
val layer: ULayer[Reducer[ClientEvent, ClientState]] = ZLayer.succeed(ClientReducer())
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import zio.schema.*
|
|||
import zio.schema.annotation.caseName
|
||||
import zio.schema.annotation.discriminatorName
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
@discriminatorName("statusType")
|
||||
sealed trait ClientState derives Schema
|
||||
object ClientState:
|
||||
|
|
@ -32,7 +30,7 @@ object ClientState:
|
|||
e.email.orElse(email),
|
||||
e.address.orElse(address)
|
||||
)
|
||||
def disable(e: ClientEvent.Disabled) =
|
||||
def disable() =
|
||||
ClientState.Inactif(
|
||||
lastName,
|
||||
firstName,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ package contracts
|
|||
|
||||
import zio.schema.*
|
||||
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
|
||||
enum ContractCommand derives Schema:
|
||||
case Subscribe(
|
||||
product: ProductType,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import zio.schema.*
|
|||
import zio.schema.annotation.caseName
|
||||
import zio.schema.annotation.discriminatorName
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
@discriminatorName("eventType")
|
||||
sealed trait ContractEvent derives Schema
|
||||
object ContractEvent:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import lu.foyer.clients.*
|
||||
import zio.*
|
||||
import java.util.UUID
|
||||
|
||||
import lu.foyer.clients.*
|
||||
|
||||
object ContractHandlers:
|
||||
val layer =
|
||||
|
|
@ -29,7 +29,7 @@ class SubscribeHandler(
|
|||
|
||||
val name = "create"
|
||||
|
||||
def onCommand(entityId: UUID, command: ContractCommand.Subscribe)
|
||||
def onCommand(entityId: String, command: ContractCommand.Subscribe)
|
||||
: Task[ContractEvent.Subscribed] =
|
||||
for
|
||||
holder <- clientStateRepo.fetchOne(command.holder).someOrFailException
|
||||
|
|
@ -39,7 +39,6 @@ class SubscribeHandler(
|
|||
ZIO.succeed(address)
|
||||
case _ =>
|
||||
ZIO.fail(new IllegalArgumentException(s"No active holder found for ${command.holder}"))
|
||||
premium = premiumService.computePremium(command.formula, command.vehicle, residentialAddress)
|
||||
premium <-
|
||||
ZIO
|
||||
.fromEither(
|
||||
|
|
@ -60,7 +59,7 @@ class AmendHandler(clientStateRepo: StateRepository[ClientState], premiumService
|
|||
|
||||
val name = "amend"
|
||||
|
||||
def onCommand(entityId: UUID, state: ContractState, command: ContractCommand.Amend)
|
||||
def onCommand(entityId: String, state: ContractState, command: ContractCommand.Amend)
|
||||
: Task[ContractEvent.Amended] =
|
||||
for
|
||||
holder <- clientStateRepo.fetchOne(state.holder).someOrFailException
|
||||
|
|
@ -96,7 +95,7 @@ class ApproveHandler(employeeService: EmployeeService)
|
|||
|
||||
val name = "approve"
|
||||
|
||||
def onCommand(entityId: UUID, state: ContractState.Pending, command: ContractCommand.Approve)
|
||||
def onCommand(entityId: String, state: ContractState.Pending, command: ContractCommand.Approve)
|
||||
: Task[ContractEvent.Approved] =
|
||||
for
|
||||
user <- ZIO.succeed("") // TODO current user
|
||||
|
|
@ -114,7 +113,7 @@ class RejectHandler(employeeService: EmployeeService)
|
|||
|
||||
val name = "reject"
|
||||
|
||||
def onCommand(entityId: UUID, state: ContractState.Pending, command: ContractCommand.Reject)
|
||||
def onCommand(entityId: String, state: ContractState.Pending, command: ContractCommand.Reject)
|
||||
: Task[ContractEvent.Rejected] =
|
||||
for
|
||||
user <- ZIO.succeed("") // TODO current user
|
||||
|
|
@ -132,6 +131,6 @@ class TerminateHandler()
|
|||
|
||||
val name = "reject"
|
||||
|
||||
def onCommand(entityId: UUID, state: ContractState, command: ContractCommand.Terminate)
|
||||
def onCommand(entityId: String, state: ContractState, command: ContractCommand.Terminate)
|
||||
: Task[ContractEvent.Terminated] =
|
||||
ZIO.succeed(ContractEvent.Terminated(command.reason))
|
||||
|
|
|
|||
|
|
@ -10,17 +10,17 @@ class ContractReducer() extends Reducer[ContractEvent, ContractState]:
|
|||
|
||||
override val fromState =
|
||||
case (s: ContractState.Actif, e: ContractEvent.Amended) => s.amend(e)
|
||||
case (s: ContractState.Actif, e: ContractEvent.Terminated) => s.terminate(e)
|
||||
case (s: ContractState.Actif, _: ContractEvent.Terminated) => s.terminate()
|
||||
|
||||
case (s: ContractState.PendingSubscription, e: ContractEvent.Amended) => s.amend(e)
|
||||
case (s: ContractState.PendingSubscription, e: ContractEvent.Approved) => s.approve(e)
|
||||
case (s: ContractState.PendingSubscription, e: ContractEvent.Rejected) => s.reject(e)
|
||||
case (s: ContractState.PendingSubscription, e: ContractEvent.Terminated) => s.terminate(e)
|
||||
case (s: ContractState.PendingSubscription, _: ContractEvent.Approved) => s.approve()
|
||||
case (s: ContractState.PendingSubscription, _: ContractEvent.Rejected) => s.reject()
|
||||
case (s: ContractState.PendingSubscription, _: ContractEvent.Terminated) => s.terminate()
|
||||
|
||||
case (s: ContractState.PendingAmendment, e: ContractEvent.Amended) => s.amend(e)
|
||||
case (s: ContractState.PendingAmendment, e: ContractEvent.Approved) => s.approve(e)
|
||||
case (s: ContractState.PendingAmendment, e: ContractEvent.Rejected) => s.reject(e)
|
||||
case (s: ContractState.PendingAmendment, e: ContractEvent.Terminated) => s.terminate(e)
|
||||
case (s: ContractState.PendingAmendment, _: ContractEvent.Approved) => s.approve()
|
||||
case (s: ContractState.PendingAmendment, _: ContractEvent.Rejected) => s.reject()
|
||||
case (s: ContractState.PendingAmendment, _: ContractEvent.Terminated) => s.terminate()
|
||||
|
||||
object ContractReducer:
|
||||
val layer: ULayer[Reducer[ContractEvent, ContractState]] = ZLayer.succeed(ContractReducer())
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import zio.schema.*
|
|||
import zio.schema.annotation.caseName
|
||||
import zio.schema.annotation.discriminatorName
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
@discriminatorName("statusType")
|
||||
sealed trait ContractState derives Schema:
|
||||
def product: ProductType
|
||||
|
|
@ -32,7 +30,7 @@ object ContractState:
|
|||
def amend(e: ContractEvent.Amended) =
|
||||
Actif(e.product, holder, e.vehicle, e.formula, e.premium)
|
||||
|
||||
def terminate(e: ContractEvent.Terminated) =
|
||||
def terminate() =
|
||||
Terminated(product, holder, vehicle, formula, premium)
|
||||
|
||||
@discriminatorName("statusType")
|
||||
|
|
@ -50,13 +48,13 @@ object ContractState:
|
|||
def amend(e: ContractEvent.Amended) =
|
||||
PendingSubscription(e.product, holder, e.vehicle, e.formula, e.premium)
|
||||
|
||||
def approve(e: ContractEvent.Approved) =
|
||||
def approve() =
|
||||
Actif(product, holder, vehicle, formula, premium)
|
||||
|
||||
def reject(e: ContractEvent.Rejected) =
|
||||
def reject() =
|
||||
Terminated(product, holder, vehicle, formula, premium)
|
||||
|
||||
def terminate(e: ContractEvent.Terminated) =
|
||||
def terminate() =
|
||||
Terminated(product, holder, vehicle, formula, premium)
|
||||
|
||||
final case class PendingChanges(
|
||||
|
|
@ -79,7 +77,7 @@ object ContractState:
|
|||
def amend(e: ContractEvent.Amended) =
|
||||
copy(pendingChanges = PendingChanges(e.product, e.vehicle, e.formula, e.premium))
|
||||
|
||||
def approve(e: ContractEvent.Approved) =
|
||||
def approve() =
|
||||
Actif(
|
||||
pendingChanges.product,
|
||||
holder,
|
||||
|
|
@ -88,10 +86,10 @@ object ContractState:
|
|||
pendingChanges.premium
|
||||
)
|
||||
|
||||
def reject(e: ContractEvent.Rejected) =
|
||||
def reject() =
|
||||
Actif(product, holder, vehicle, formula, premium)
|
||||
|
||||
def terminate(e: ContractEvent.Terminated) =
|
||||
def terminate() =
|
||||
ContractState.Terminated(product, holder, vehicle, formula, premium)
|
||||
|
||||
@caseName("terminated")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import lu.foyer.clients.Address
|
||||
|
||||
trait EmployeeService:
|
||||
def fetchOne(subject: String): Either[String, Employee]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue