package lu.foyer import zio.* import zio.http.* import zio.http.codec.* import zio.http.codec.PathCodec.path import zio.http.endpoint.* import zio.schema.* 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]) .jsonApiMany[State] private lazy val fetchOne = Endpoint(Method.GET / entityName / string("entityId")) .jsonApiOne[State] private lazy val fetchEventsMany = Endpoint(Method.GET / entityName / string("entityId") / "events") .query(HttpCodec.query[Page]) .jsonApiMany[Event] private lazy val fetchEventsOne = Endpoint(Method.GET / entityName / string("entityId") / "events" / string("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, _) <- 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 / string("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) private lazy val fetchOneRoute = fetchOne.implementJsonApiOneEntity(commandEngine.stateRepo.fetchOne) private lazy val fetchEventsManyRoute = fetchEventsMany.implementJsonApiManyEvent(commandEngine.eventRepo.fetchMany(_, _)) 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