Fix tests
This commit is contained in:
parent
efdc50eb1d
commit
87bd780f9f
34 changed files with 230 additions and 303 deletions
24
.scalafix.conf
Normal file
24
.scalafix.conf
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
rules = [
|
||||
DisableSyntax,
|
||||
LeakingImplicitClassVal,
|
||||
NoAutoTupling,
|
||||
NoValInForComprehension,
|
||||
OrganizeImports
|
||||
]
|
||||
|
||||
OrganizeImports {
|
||||
targetDialect = Scala3
|
||||
blankLines = Manual
|
||||
expandRelative = true
|
||||
removeUnused=false
|
||||
groupedImports=Keep
|
||||
groups = [
|
||||
"---"
|
||||
"re:(javax?|scala)\\."
|
||||
"---"
|
||||
"*"
|
||||
"---"
|
||||
"lu.foyer"
|
||||
"---"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,23 +1,18 @@
|
|||
package lu.foyer
|
||||
|
||||
import zio.*
|
||||
import zio.http.*
|
||||
import zio.http.Header.AccessControlAllowOrigin
|
||||
import zio.http.Middleware.CorsConfig
|
||||
import zio.http.Middleware.cors
|
||||
import zio.http.codec.*
|
||||
import zio.http.codec.PathCodec.path
|
||||
import zio.http.endpoint.openapi.OpenAPIGen
|
||||
import zio.http.endpoint.openapi.SwaggerUI
|
||||
|
||||
import lu.foyer.clients.*
|
||||
import lu.foyer.contracts.*
|
||||
import zio.*
|
||||
import zio.Console.*
|
||||
import zio.http.*
|
||||
import zio.http.codec.*
|
||||
import zio.http.codec.PathCodec.path
|
||||
import zio.http.endpoint.*
|
||||
import zio.http.endpoint.openapi.OpenAPIGen
|
||||
import zio.http.endpoint.openapi.SwaggerUI
|
||||
import zio.schema.*
|
||||
import zio.http.Middleware.cors
|
||||
import zio.http.Middleware.CorsConfig
|
||||
import zio.http.Header.AccessControlAllowOrigin
|
||||
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
import zio.schema.codec.JsonCodec.ExplicitConfig
|
||||
|
||||
object HttpServer:
|
||||
|
||||
|
|
@ -33,6 +28,11 @@ object HttpServer:
|
|||
++ SwaggerUI.routes("docs" / "openapi", openAPI)
|
||||
|
||||
object App extends ZIOAppDefault:
|
||||
|
||||
override val bootstrap = CodecConfig.configLayer(
|
||||
CodecConfig(explicitNulls = ExplicitConfig(encoding = false, decoding = false))
|
||||
)
|
||||
|
||||
val app =
|
||||
for
|
||||
routes <- HttpServer.routes
|
||||
|
|
|
|||
|
|
@ -1,39 +1,32 @@
|
|||
package lu.foyer
|
||||
|
||||
import lu.foyer.JsonApiResponse.One
|
||||
import zio.*
|
||||
import zio.Console.*
|
||||
import zio.http.*
|
||||
import zio.http.codec.*
|
||||
import zio.http.codec.PathCodec.path
|
||||
import zio.http.endpoint.*
|
||||
import zio.schema.*
|
||||
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
|
||||
trait CommandEngineController[Command: Schema, Event: Schema, State: Schema]
|
||||
extends JsonApiController:
|
||||
trait CommandEngineController[Command, Event: Schema, State: Schema] extends JsonApiController:
|
||||
|
||||
def commandEngine: CommandEngine[Command, Event, State]
|
||||
|
||||
private lazy val fetchMany =
|
||||
Endpoint(Method.GET / entityName)
|
||||
// .query(HttpCodec.query[Page])
|
||||
.query(HttpCodec.query[Page])
|
||||
.jsonApiMany[State]
|
||||
|
||||
private lazy val fetchOne =
|
||||
Endpoint(Method.GET / entityName / uuid("entityId"))
|
||||
Endpoint(Method.GET / entityName / string("entityId"))
|
||||
.jsonApiOne[State]
|
||||
|
||||
private lazy val fetchEventsMany =
|
||||
Endpoint(Method.GET / entityName / uuid("entityId") / "events")
|
||||
// .query(HttpCodec.query[Page])
|
||||
Endpoint(Method.GET / entityName / string("entityId") / "events")
|
||||
.query(HttpCodec.query[Page])
|
||||
.jsonApiMany[Event]
|
||||
|
||||
private lazy val fetchEventsOne =
|
||||
Endpoint(Method.GET / entityName / uuid("entityId") / "events" / uuid("eventId"))
|
||||
Endpoint(Method.GET / entityName / string("entityId") / "events" / string("eventId"))
|
||||
.jsonApiOne[Event]
|
||||
|
||||
private def generateCommands = commandEngine.handlers.map(handler =>
|
||||
|
|
@ -49,15 +42,17 @@ trait CommandEngineController[Command: Schema, Event: Schema, State: Schema]
|
|||
val route = endpoint.implementJsonApiOneEvent(command =>
|
||||
for
|
||||
entityId <- Random.nextUUID
|
||||
(event, state) <- commandEngine
|
||||
.handleCommand(command, handler.name, entityId)
|
||||
(event, _) <- commandEngine
|
||||
.handleCommand(command, handler.name, entityId.toString)
|
||||
yield Some(event)
|
||||
)
|
||||
(endpoint, route)
|
||||
|
||||
private def generateUpdateCommand(handler: CommandHandler[Command, Event, State]) =
|
||||
given Schema[Command] = handler.commandSchema.asInstanceOf[Schema[Command]]
|
||||
val endpoint = Endpoint(Method.PUT / entityName / uuid("entityId") / "commands" / handler.name)
|
||||
val endpoint = Endpoint(
|
||||
Method.PUT / entityName / string("entityId") / "commands" / handler.name
|
||||
)
|
||||
.in[Command]
|
||||
.jsonApiOne[Event]
|
||||
val route = endpoint.implementJsonApiOneEvent((entityId, command) =>
|
||||
|
|
@ -70,17 +65,13 @@ trait CommandEngineController[Command: Schema, Event: Schema, State: Schema]
|
|||
private lazy val (commands, commandsRoutes) = generateCommands.unzip
|
||||
|
||||
private lazy val fetchManyRoute =
|
||||
fetchMany.implementJsonApiManyEntity(_ =>
|
||||
commandEngine.stateRepo.fetchMany(Page(None, None, totals = Some(true)))
|
||||
)
|
||||
fetchMany.implementJsonApiManyEntity(commandEngine.stateRepo.fetchMany)
|
||||
|
||||
private lazy val fetchOneRoute =
|
||||
fetchOne.implementJsonApiOneEntity(commandEngine.stateRepo.fetchOne)
|
||||
|
||||
private lazy val fetchEventsManyRoute =
|
||||
fetchEventsMany.implementJsonApiManyEvent(entityId =>
|
||||
commandEngine.eventRepo.fetchMany(entityId, Page(None, None, totals = Some(true)))
|
||||
)
|
||||
fetchEventsMany.implementJsonApiManyEvent(commandEngine.eventRepo.fetchMany(_, _))
|
||||
|
||||
private lazy val fetchEventsOneRoute =
|
||||
fetchEventsOne.implementJsonApiOneEvent(commandEngine.eventRepo.fetchOne(_, _))
|
||||
|
|
|
|||
|
|
@ -1,19 +1,14 @@
|
|||
package lu.foyer
|
||||
|
||||
import lu.foyer.JsonApiResponse.Many
|
||||
import lu.foyer.JsonApiResponse.One
|
||||
import zio.*
|
||||
import zio.http.*
|
||||
import zio.http.Header.Forwarded
|
||||
import zio.http.codec.*
|
||||
import zio.http.codec.PathCodec.path
|
||||
import zio.http.endpoint.*
|
||||
import zio.schema.*
|
||||
import zio.schema.annotation.discriminatorName
|
||||
import zio.schema.annotation.fieldName
|
||||
|
||||
import java.util.UUID
|
||||
import scala.annotation.targetName
|
||||
import lu.foyer.JsonApiResponse.Many
|
||||
import lu.foyer.JsonApiResponse.One
|
||||
|
||||
object JsonApiResponse:
|
||||
|
||||
|
|
@ -26,7 +21,7 @@ object JsonApiResponse:
|
|||
|
||||
case class Entity[T](
|
||||
`type`: String,
|
||||
id: UUID,
|
||||
id: String,
|
||||
attributes: T,
|
||||
relationships: Option[Relationships] = None,
|
||||
links: Map[String, String])
|
||||
|
|
@ -34,17 +29,17 @@ object JsonApiResponse:
|
|||
|
||||
case class Relationships(_entity: RelationshipsEntity) derives Schema
|
||||
object Relationships:
|
||||
def apply(id: UUID, `type`: String, entityUrl: String): Relationships = Relationships(
|
||||
def apply(id: String, `type`: String, entityUrl: String): Relationships = Relationships(
|
||||
RelationshipsEntity(RelationshipsData(id, `type`), RelationshipsLinks(entityUrl))
|
||||
)
|
||||
case class RelationshipsEntity(data: RelationshipsData, links: RelationshipsLinks) derives Schema
|
||||
case class RelationshipsData(id: UUID, `type`: String) derives Schema
|
||||
case class RelationshipsData(id: String, `type`: String) derives Schema
|
||||
case class RelationshipsLinks(related: String) derives Schema
|
||||
|
||||
@discriminatorName("errorType")
|
||||
enum Error(title: String) derives Schema:
|
||||
case NotFound(id: String) extends Error(s"Entity $id not found")
|
||||
case InternalServerError(title: String) extends Error(title)
|
||||
enum Error derives Schema:
|
||||
case NotFound(id: String)
|
||||
case InternalServerError(title: String)
|
||||
object Error:
|
||||
given Schema[Error.NotFound] = DeriveSchema.gen
|
||||
given Schema[Error.InternalServerError] = DeriveSchema.gen
|
||||
|
|
@ -105,7 +100,7 @@ trait JsonApiController:
|
|||
)
|
||||
def implementJsonApiOne[Env, A](
|
||||
f: Input => RIO[Env, Option[A]],
|
||||
getId: A => UUID,
|
||||
getId: A => String,
|
||||
getEntity: A => Output,
|
||||
onthology: String = this.onthology,
|
||||
links: (A, ProxyHeaders) => Map[String, String] = (_: A, _: ProxyHeaders) => Map.empty,
|
||||
|
|
@ -177,7 +172,7 @@ trait JsonApiController:
|
|||
)
|
||||
def implementJsonApiMany[Env, A](
|
||||
f: Input => RIO[Env, Paged[A]],
|
||||
getId: A => UUID,
|
||||
getId: A => String,
|
||||
getEntity: A => Output,
|
||||
links: (A, ProxyHeaders) => Map[String, String] = (_: A, _: ProxyHeaders) => Map.empty
|
||||
)(implicit trace: Trace
|
||||
|
|
|
|||
|
|
@ -2,16 +2,6 @@ package lu.foyer
|
|||
package clients
|
||||
|
||||
import zio.*
|
||||
import zio.Console.*
|
||||
import zio.http.*
|
||||
import zio.http.codec.*
|
||||
import zio.http.codec.PathCodec.path
|
||||
import zio.http.endpoint.*
|
||||
import zio.schema.*
|
||||
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
|
||||
class ClientController(val commandEngine: CommandEngine[ClientCommand, ClientEvent, ClientState])
|
||||
extends CommandEngineController[ClientCommand, ClientEvent, ClientState]:
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
package lu.foyer
|
||||
package clients
|
||||
|
||||
import java.util.UUID
|
||||
import zio.*
|
||||
|
||||
class ClientEventRepositoryInMemory(events: Ref[Map[UUID, Event[ClientEvent]]])
|
||||
class ClientEventRepositoryInMemory(events: Ref[Map[String, Event[ClientEvent]]])
|
||||
extends EventRepository[ClientEvent]
|
||||
with InMemoryRepository[Event[ClientEvent]](events):
|
||||
def fetchOne(entityId: UUID, eventId: UUID): Task[Option[Event[ClientEvent]]] =
|
||||
def fetchOne(entityId: String, eventId: String): Task[Option[Event[ClientEvent]]] =
|
||||
events.get.map(_.get(eventId))
|
||||
def fetchMany(entityId: UUID, page: Page): Task[Paged[Event[ClientEvent]]] =
|
||||
def fetchMany(entityId: String, page: Page): Task[Paged[Event[ClientEvent]]] =
|
||||
events.get
|
||||
.map(entities =>
|
||||
val items = entities.values
|
||||
.filter(_.entityId == entityId)
|
||||
.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)
|
||||
)
|
||||
|
||||
object ClientEventRepositoryInMemory:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package lu.foyer
|
||||
package clients
|
||||
|
||||
import java.util.UUID
|
||||
import zio.*
|
||||
|
||||
class ClientStateRepositoryInMemory(clients: Ref[Map[UUID, Entity[ClientState]]])
|
||||
class ClientStateRepositoryInMemory(clients: Ref[Map[String, Entity[ClientState]]])
|
||||
extends StateRepository[ClientState]
|
||||
with InMemoryRepository[Entity[ClientState]](clients)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,6 @@ package lu.foyer
|
|||
package contracts
|
||||
|
||||
import zio.*
|
||||
import zio.Console.*
|
||||
import zio.http.*
|
||||
import zio.http.codec.*
|
||||
import zio.http.codec.PathCodec.path
|
||||
import zio.http.endpoint.*
|
||||
import zio.schema.*
|
||||
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
|
||||
class ContractController(
|
||||
val commandEngine: CommandEngine[ContractCommand, ContractEvent, ContractState])
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import java.util.UUID
|
||||
import zio.*
|
||||
|
||||
class ContractEventRepositoryInMemory(events: Ref[Map[UUID, Event[ContractEvent]]])
|
||||
class ContractEventRepositoryInMemory(events: Ref[Map[String, Event[ContractEvent]]])
|
||||
extends EventRepository[ContractEvent]
|
||||
with InMemoryRepository[Event[ContractEvent]](events):
|
||||
def fetchOne(entityId: UUID, eventId: UUID): Task[Option[Event[ContractEvent]]] =
|
||||
def fetchOne(entityId: String, eventId: String): Task[Option[Event[ContractEvent]]] =
|
||||
events.get.map(_.get(eventId))
|
||||
def fetchMany(entityId: UUID, page: Page): Task[Paged[Event[ContractEvent]]] =
|
||||
def fetchMany(entityId: String, page: Page): Task[Paged[Event[ContractEvent]]] =
|
||||
events.get
|
||||
.map(entities =>
|
||||
val items = entities.values
|
||||
.filter(_.entityId == entityId)
|
||||
.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)
|
||||
)
|
||||
|
||||
object ContractEventRepositoryInMemory:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import java.util.UUID
|
||||
import zio.*
|
||||
|
||||
class ContractStateRepositoryInMemory(clients: Ref[Map[UUID, Entity[ContractState]]])
|
||||
class ContractStateRepositoryInMemory(clients: Ref[Map[String, Entity[ContractState]]])
|
||||
extends StateRepository[ContractState]
|
||||
with InMemoryRepository[Entity[ContractState]](clients)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import java.util.UUID
|
||||
import zio.*
|
||||
import lu.foyer.clients.Address
|
||||
import scala.math.BigDecimal.RoundingMode
|
||||
import java.util.Currency
|
||||
import lu.foyer.clients.Country
|
||||
|
||||
object EmployeeServiceImpl extends EmployeeService:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import java.util.UUID
|
||||
import zio.*
|
||||
import lu.foyer.clients.Address
|
||||
import scala.math.BigDecimal.RoundingMode
|
||||
import java.util.Currency
|
||||
import scala.math.BigDecimal.RoundingMode
|
||||
|
||||
import zio.*
|
||||
|
||||
import lu.foyer.clients.Address
|
||||
import lu.foyer.clients.Country
|
||||
|
||||
object PremiumServiceImpl extends PremiumService:
|
||||
|
|
|
|||
48
build.mill
Normal file
48
build.mill
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//| mvnDeps:
|
||||
//| - com.goyeau::mill-scalafix::0.6.0
|
||||
|
||||
package build
|
||||
import mill.*, scalalib.*
|
||||
import com.goyeau.mill.scalafix.ScalafixModule
|
||||
|
||||
object Versions:
|
||||
val zio = "2.1.21"
|
||||
val zioJson = "0.7.44"
|
||||
val zioSchema = "1.7.5"
|
||||
val zioHttp = "3.5.1"
|
||||
val zioPrelude = "1.0.0-RC42"
|
||||
|
||||
|
||||
trait CommonModule extends ScalaModule with ScalafixModule:
|
||||
def scalaVersion = "3.7.2"
|
||||
def scalacOptions = Seq(
|
||||
"-Wunused:all",
|
||||
"-preview",
|
||||
"-feature",
|
||||
"-language:implicitConversions",
|
||||
"-Wvalue-discard",
|
||||
"-Wnonunit-statement"
|
||||
)
|
||||
def mvnDeps = Seq(
|
||||
mvn"dev.zio::zio:${Versions.zio}",
|
||||
mvn"dev.zio::zio-json:${Versions.zioJson}",
|
||||
mvn"dev.zio::zio-schema-json:${Versions.zioSchema}",
|
||||
mvn"dev.zio::zio-schema:${Versions.zioSchema}",
|
||||
mvn"dev.zio::zio-schema-derivation:${Versions.zioSchema}",
|
||||
mvn"dev.zio::zio-prelude:${Versions.zioPrelude}"
|
||||
)
|
||||
|
||||
|
||||
object model extends CommonModule
|
||||
|
||||
object core extends CommonModule :
|
||||
def moduleDeps = Seq(model)
|
||||
|
||||
|
||||
object api extends CommonModule :
|
||||
def moduleDeps = Seq(core)
|
||||
def mvnDeps = Seq(
|
||||
mvn"dev.zio::zio:${Versions.zio}",
|
||||
mvn"dev.zio::zio-http:${Versions.zioHttp}"
|
||||
)
|
||||
|
||||
45
build.sc
45
build.sc
|
|
@ -1,45 +0,0 @@
|
|||
// scalafmt: { runner.dialect = scala213 }
|
||||
package build
|
||||
import mill._, scalalib._
|
||||
import coursier.maven.MavenRepository
|
||||
|
||||
val sonatypeSnapshots = Seq(
|
||||
MavenRepository("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
)
|
||||
|
||||
object Versions {
|
||||
val zio = "2.1.15"
|
||||
val zioJson = "0.7.33"
|
||||
val zioSchema = "1.6.3"
|
||||
val zioHttp = "3.1.0"
|
||||
val zioPrelude = "1.0.0-RC39"
|
||||
}
|
||||
|
||||
trait CommonModule extends ScalaModule {
|
||||
def scalaVersion = "3.6.3"
|
||||
def ivyDeps = Agg(
|
||||
ivy"dev.zio::zio:${Versions.zio}",
|
||||
ivy"dev.zio::zio-json:${Versions.zioJson}",
|
||||
ivy"dev.zio::zio-schema-json:${Versions.zioSchema}",
|
||||
ivy"dev.zio::zio-schema:${Versions.zioSchema}",
|
||||
ivy"dev.zio::zio-schema-derivation:${Versions.zioSchema}",
|
||||
ivy"dev.zio::zio-prelude:${Versions.zioPrelude}"
|
||||
)
|
||||
def repositoriesTask = Task.Anon {
|
||||
super.repositoriesTask() ++ sonatypeSnapshots
|
||||
}
|
||||
}
|
||||
|
||||
object model extends CommonModule
|
||||
|
||||
object core extends CommonModule {
|
||||
def moduleDeps = Seq(model)
|
||||
}
|
||||
|
||||
object api extends CommonModule {
|
||||
def moduleDeps = Seq(core)
|
||||
def ivyDeps = Agg(
|
||||
ivy"dev.zio::zio:${Versions.zio}",
|
||||
ivy"dev.zio::zio-http:${Versions.zioHttp}"
|
||||
)
|
||||
}
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
132
flake.lock
generated
132
flake.lock
generated
|
|
@ -9,16 +9,20 @@
|
|||
"devenv"
|
||||
],
|
||||
"git-hooks": [
|
||||
"devenv"
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737621947,
|
||||
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=",
|
||||
"lastModified": 1752264895,
|
||||
"narHash": "sha256-1zBPE/PNAkPNUsOWFET4J0cjlvziH8DOekesDmjND+w=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5",
|
||||
"rev": "47053aef762f452e816e44eb9a23fbc3827b241a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -32,6 +36,7 @@
|
|||
"inputs": {
|
||||
"cachix": "cachix",
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks": "git-hooks",
|
||||
"nix": "nix",
|
||||
"nixpkgs": [
|
||||
|
|
@ -39,11 +44,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742480343,
|
||||
"narHash": "sha256-AN6X0t0pX0GLDh6CMG474aNffJUKPQtSEJ9aCZed474=",
|
||||
"lastModified": 1759767645,
|
||||
"narHash": "sha256-36Cm0InaiezqXYmzM7sNmLChWV2jfL4OWccIbL1n3nU=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "56fe80518d1c949520a2cea8e9c2a83ec3f9bdc5",
|
||||
"rev": "692a6db69ca067f9555f736b92c9d83468053f13",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -55,11 +60,11 @@
|
|||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -72,16 +77,15 @@
|
|||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"devenv",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712014858,
|
||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||
"lastModified": 1756770412,
|
||||
"narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||
"rev": "4524271976b625a4a605beefd893f270620fd751",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -93,7 +97,8 @@
|
|||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
|
|
@ -102,11 +107,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1740849354,
|
||||
"narHash": "sha256-oy33+t09FraucSZ2rZ6qnD1Y1c8azKKmQuCvF2ytUko=",
|
||||
"lastModified": 1758108966,
|
||||
"narHash": "sha256-ytw7ROXaWZ7OfwHrQ9xvjpUWeGVm86pwnEd1QhzawIo=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "4a709a8ce9f8c08fa7ddb86761fe488ff7858a07",
|
||||
"rev": "54df955a695a84cd47d4a43e08e1feaf90b1fd9b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -137,94 +142,53 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1697646580,
|
||||
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-parts": [
|
||||
"devenv",
|
||||
"flake-parts"
|
||||
],
|
||||
"git-hooks-nix": [
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"flake-parts": "flake-parts",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-23-11": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs-regression": [
|
||||
"devenv"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1741798497,
|
||||
"narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=",
|
||||
"owner": "domenkozar",
|
||||
"lastModified": 1758763079,
|
||||
"narHash": "sha256-Bx1A+lShhOWwMuy3uDzZQvYiBKBFcKwy6G6NEohhv6A=",
|
||||
"owner": "cachix",
|
||||
"repo": "nix",
|
||||
"rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd",
|
||||
"rev": "6f0140527c2b0346df4afad7497baa08decb929f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.24",
|
||||
"owner": "cachix",
|
||||
"ref": "devenv-2.30.5",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1733212471,
|
||||
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1717432640,
|
||||
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1733477122,
|
||||
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
|
||||
"lastModified": 1758532697,
|
||||
"narHash": "sha256-bhop0bR3u7DCw9/PtLCwr7GwEWDlBSxHp+eVQhCW9t4=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
|
||||
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -237,7 +201,7 @@
|
|||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package lu.foyer
|
||||
|
||||
import zio.schema.*
|
||||
|
||||
enum AppError(description: String) extends Throwable:
|
||||
enum AppError(description: String) extends Throwable(description):
|
||||
case NotFound(desc: String) extends AppError(desc)
|
||||
case Unexpected(desc: String) extends AppError(desc)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
package lu.foyer
|
||||
|
||||
import zio.schema.*
|
||||
import java.util.UUID
|
||||
|
||||
opaque type ClientEntityId <: UUID = UUID
|
||||
object ClientEntityId extends RefinedUUID[ClientEntityId]
|
||||
opaque type ClientEntityId <: String = String
|
||||
object ClientEntityId extends NonBlankString[ClientEntityId]
|
||||
|
||||
opaque type Email <: String = String
|
||||
object Email extends NonBlankString[Email]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package lu.foyer
|
||||
|
||||
import zio.prelude.*
|
||||
import zio.schema.Schema
|
||||
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
|
||||
import zio.prelude.*
|
||||
import zio.schema.Schema
|
||||
|
||||
trait RefinedType[Base, New]:
|
||||
inline def assume(value: Base): New = value.asInstanceOf[New]
|
||||
def validation(value: Base): Validation[String, New]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package lu.foyer
|
||||
package clients
|
||||
|
||||
import zio.schema.*
|
||||
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
|
||||
import zio.schema.*
|
||||
|
||||
opaque type ClientLastName <: String = String
|
||||
object ClientLastName extends NonBlankString[ClientLastName]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package lu.foyer
|
|||
package contracts
|
||||
|
||||
import zio.schema.*
|
||||
import java.util.Currency
|
||||
|
||||
opaque type EmployeeDisplayName <: String = String
|
||||
object EmployeeDisplayName extends NonBlankString[EmployeeDisplayName]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package lu.foyer
|
||||
package contracts
|
||||
|
||||
import zio.schema.*
|
||||
import java.util.Currency
|
||||
|
||||
import zio.schema.*
|
||||
|
||||
opaque type VehiclePlate <: String = String
|
||||
object VehiclePlate extends NonBlankString[VehiclePlate]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue