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: def commandEngine: CommandEngine[Command, Event, State] private lazy val fetchMany = Endpoint(Method.GET / entityName) // .query(HttpCodec.query[Page]) .jsonApiMany[State] private lazy val fetchOne = Endpoint(Method.GET / entityName / uuid("entityId")) .jsonApiOne[State] private lazy val fetchEventsMany = Endpoint(Method.GET / entityName / uuid("entityId") / "events") // .query(HttpCodec.query[Page]) .jsonApiMany[Event] private lazy val fetchEventsOne = 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] .jsonApiOneWithStatus[Event](Status.Created) 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 lazy val (commands, commandsRoutes) = generateCommands.unzip private lazy val fetchManyRoute = fetchMany.implementJsonApiManyEntity(_ => commandEngine.stateRepo.fetchMany(Page(None, None, totals = Some(true))) ) 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))) ) private lazy val fetchEventsOneRoute = fetchEventsOne.implementJsonApiOneEvent(commandEngine.eventRepo.fetchOne(_, _)) lazy val endpoints = List( fetchMany, fetchOne, fetchEventsMany, fetchEventsOne ) ++ commands lazy val routes = (Routes( fetchManyRoute, fetchOneRoute, fetchEventsManyRoute, fetchEventsOneRoute ) ++ Routes.fromIterable(commandsRoutes)) @@ proxyHeadersAspect end CommandEngineController