175 lines
5.1 KiB
Scala
175 lines
5.1 KiB
Scala
|
|
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
|