Fix tests

This commit is contained in:
Paul-Henri Froidmont 2025-10-13 15:46:22 +02:00
parent efdc50eb1d
commit 87bd780f9f
Signed by: phfroidmont
GPG key ID: BE948AFD7E7873BE
34 changed files with 230 additions and 303 deletions

View file

@ -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

View file

@ -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(_, _))

View file

@ -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

View file

@ -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]:

View file

@ -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:

View file

@ -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)

View file

@ -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])

View file

@ -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:

View file

@ -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)

View file

@ -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:

View file

@ -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: