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