105 lines
3.2 KiB
Scala
105 lines
3.2 KiB
Scala
|
|
package lu.foyer
|
||
|
|
|
||
|
|
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
|
||
|
|
import lu.foyer.JsonApiResponse.One
|
||
|
|
|
||
|
|
trait CommandEngineController[Command: Schema, Event: Schema, State: Schema](
|
||
|
|
domain: String,
|
||
|
|
entityName: String)
|
||
|
|
extends JsonApiController:
|
||
|
|
|
||
|
|
def commandEngine: CommandEngine[Command, Event, State]
|
||
|
|
|
||
|
|
val onthology = s"$domain:$entityName"
|
||
|
|
|
||
|
|
private val fetchMany =
|
||
|
|
Endpoint(Method.GET / entityName)
|
||
|
|
.query(HttpCodec.query[Page])
|
||
|
|
.jsonApiMany[State]
|
||
|
|
|
||
|
|
private val fetchOne =
|
||
|
|
Endpoint(Method.GET / entityName / uuid("entityId"))
|
||
|
|
.jsonApiOne[State]
|
||
|
|
|
||
|
|
private val fetchEventsMany =
|
||
|
|
Endpoint(Method.GET / entityName / uuid("entityId") / "events")
|
||
|
|
.query(HttpCodec.query[Page])
|
||
|
|
.jsonApiMany[Event]
|
||
|
|
|
||
|
|
private val fetchEventsOne: Endpoint[(UUID, UUID), (UUID, UUID), JsonApiResponse.Error, One[
|
||
|
|
Event
|
||
|
|
], zio.http.endpoint.AuthType.None.type] =
|
||
|
|
Endpoint(Method.GET / entityName / uuid("entityId") / "events" / uuid("eventId"))
|
||
|
|
.jsonApiOne[Event]
|
||
|
|
|
||
|
|
private def generateCommands = commandEngine.handlers.map(handler =>
|
||
|
|
if handler.isCreate then generateCreateCommand(handler)
|
||
|
|
else generateUpdateCommand(handler)
|
||
|
|
)
|
||
|
|
|
||
|
|
private def generateCreateCommand(handler: CommandHandler[Command, Event, State]) =
|
||
|
|
given Schema[Command] = handler.commandSchema.asInstanceOf[Schema[Command]]
|
||
|
|
val endpoint = Endpoint(Method.POST / entityName / "commands" / handler.name)
|
||
|
|
.in[Command]
|
||
|
|
.jsonApiOne[Event]
|
||
|
|
val route = endpoint.implementJsonApiOneEvent(command =>
|
||
|
|
for
|
||
|
|
entityId <- Random.nextUUID
|
||
|
|
(event, state) <- commandEngine
|
||
|
|
.handleCommand(command, handler.name, entityId)
|
||
|
|
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)
|
||
|
|
.in[Command]
|
||
|
|
.jsonApiOne[Event]
|
||
|
|
val route = endpoint.implementJsonApiOneEvent((entityId, command) =>
|
||
|
|
for (event, _) <- commandEngine
|
||
|
|
.handleCommand(command, handler.name, entityId)
|
||
|
|
yield Some(event)
|
||
|
|
)
|
||
|
|
(endpoint, route)
|
||
|
|
|
||
|
|
private val (commands, commandsRoutes) = generateCommands.unzip
|
||
|
|
|
||
|
|
private val fetchManyRoute =
|
||
|
|
fetchMany.implementJsonApiManyEntity(commandEngine.stateRepo.fetchMany)
|
||
|
|
|
||
|
|
private val fetchOneRoute =
|
||
|
|
fetchOne.implementJsonApiOneEntity(commandEngine.stateRepo.fetchOne)
|
||
|
|
|
||
|
|
private val fetchEventsManyRoute =
|
||
|
|
fetchEventsMany.implementJsonApiManyEvent(commandEngine.eventRepo.fetchMany(_, _))
|
||
|
|
|
||
|
|
private val fetchEventsOneRoute =
|
||
|
|
fetchEventsOne.implementJsonApiOneEvent(commandEngine.eventRepo.fetchOne(_, _))
|
||
|
|
|
||
|
|
val endpoints = List(
|
||
|
|
fetchMany,
|
||
|
|
fetchOne,
|
||
|
|
fetchEventsMany,
|
||
|
|
fetchEventsOne
|
||
|
|
) ++ commands
|
||
|
|
|
||
|
|
val routes = Routes(
|
||
|
|
fetchManyRoute,
|
||
|
|
fetchOneRoute,
|
||
|
|
fetchEventsManyRoute,
|
||
|
|
fetchEventsOneRoute
|
||
|
|
) ++ Routes.fromIterable(commandsRoutes)
|
||
|
|
|
||
|
|
end CommandEngineController
|