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 object JsonApiResponse: case class One[T]( data: Entity[T], links: Links) derives Schema case class Many[T]( data: List[Entity[T]], links: Links, meta: Meta) derives Schema case class Links( self: String, first: Option[String] = None, prev: Option[String] = None, next: Option[String] = None, last: Option[String] = None) derives Schema case class Meta(totalRecords: Option[Long], totalPages: Option[Long]) derives Schema case class Entity[T](id: String, `type`: String, attributes: T) derives Schema case class Error(message: String) derives Schema // TODO val pageParams = (HttpCodec.query[Option[Int]]("page[number]") & HttpCodec.query[Option[Int]]("page[size]") & HttpCodec.query[Option[Boolean]]("page[totals]")) .transform[Page]((number, size, totals) => Page(number.getOrElse(0), size.getOrElse(50), totals.getOrElse(false)) )(p => (Some(p._1), Some(p._2), Some(p._3))) trait CommandEngineController[Command: Schema, Event: Schema, State: Schema]( entityName: String, commands: List[Command], commandEngine: CommandEngine[Command, Event, State]): private val fetchMany = Endpoint(Method.GET / entityName) .query(pageParams) .out[JsonApiResponse.Many[State]] .outError[JsonApiResponse.Error](Status.InternalServerError) private val fetchOne = Endpoint(Method.GET / entityName / string("entityId")) .out[JsonApiResponse.One[State]] private val fetchEventsMany = Endpoint(Method.GET / entityName / string("entityId") / "events") .query(pageParams) .out[JsonApiResponse.Many[Event]] private val fetchEventsOne = Endpoint(Method.GET / entityName / string("entityId") / "events" / string("eventId")) .out[JsonApiResponse.One[Event]] private val commandsEndpoints = commands.map(command => Endpoint(Method.POST / "clients" / "commands" / command.toString.toLowerCase) .in[Command] .out[JsonApiResponse.One[Event]] ) private val fetchManyRoute = fetchMany.implement(page => commandEngine.stateRepo .fetchMany(page) .map(paged => JsonApiResponse.Many( paged.items.map(entity => JsonApiResponse.Entity(entity.entityId.toString, "todo", entity.data) ), JsonApiResponse.Links("https://api.example.org"), meta = JsonApiResponse.Meta(paged.totals, None) // TODO ) ).mapError(e => JsonApiResponse.Error(e.getMessage)) ) end CommandEngineController object ClientController: private val fetchMany = Endpoint(Method.GET / "clients") .query(pageParams) .out[JsonApiResponse.Many[ClientState]] private val fetchOne = Endpoint(Method.GET / "clients" / string("entityId")) .out[JsonApiResponse.One[ClientState]] private val createCommand = Endpoint(Method.POST / "clients" / "commands" / "create") .in[ClientCommand.Create] .out[JsonApiResponse.One[ClientEvent]] private val updateCommand = Endpoint(Method.PUT / "clients" / string("entityId") / "commands" / "udpate") .in[ClientCommand.Update] .out[JsonApiResponse.One[ClientEvent]] private val disableCommand = Endpoint(Method.PUT / "clients" / string("entityId") / "commands" / "disable") .in[ClientCommand.Disable] .out[JsonApiResponse.One[ClientEvent]] private val fetchEventsMany = Endpoint(Method.GET / "clients" / string("entityId") / "events") .query(pageParams) .out[JsonApiResponse.Many[ClientEvent]] private val fetchEventsOne = Endpoint(Method.GET / "clients" / string("entityId") / "events" / string("eventId")) .out[JsonApiResponse.One[ClientEvent]] private val fetchManyRoute = fetchMany.implement(page => ZIO.succeed( JsonApiResponse.Many[ClientState]( List.empty, JsonApiResponse.Links("https://api.example.org"), meta = JsonApiResponse.Meta(None, None) ) ) ) private val createCommandRoute = createCommand.implement(command => ZIO.succeed( JsonApiResponse.One[ClientEvent]( JsonApiResponse.Entity( id = "todo", `type` = "todo", ClientEvent.Created( lastName = command.lastName, firstName = command.firstName, birthDate = command.birthDate, drivingLicenseDate = command.drivingLicenseDate, phoneNumber = command.phoneNumber, email = command.email, address = command.address ) ), JsonApiResponse.Links("https://api.example.org") ) ) ) val endpoints = List( fetchMany, fetchOne, createCommand, updateCommand, disableCommand, fetchEventsMany, fetchEventsOne ) val routes = Routes(fetchManyRoute) end ClientController