Initial commit
This commit is contained in:
commit
1919e4b72c
14 changed files with 640 additions and 0 deletions
10
.envrc
Normal file
10
.envrc
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
|
||||||
|
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
|
||||||
|
fi
|
||||||
|
|
||||||
|
watch_file flake.nix
|
||||||
|
watch_file flake.lock
|
||||||
|
if ! use flake . --no-pure-eval
|
||||||
|
then
|
||||||
|
echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2
|
||||||
|
fi
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.devenv
|
||||||
|
.direnv
|
||||||
|
out
|
||||||
|
.bloop
|
||||||
|
.metals
|
||||||
24
.scalafmt.conf
Normal file
24
.scalafmt.conf
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
version = "3.8.3"
|
||||||
|
|
||||||
|
preset=defaultWithAlign
|
||||||
|
|
||||||
|
assumeStandardLibraryStripMargin = true
|
||||||
|
maxColumn = 100
|
||||||
|
continuationIndent.callSite = 2
|
||||||
|
continuationIndent.defnSite = 2
|
||||||
|
align.arrowEnumeratorGenerator = true
|
||||||
|
align.openParenDefnSite = false
|
||||||
|
align.stripMargin = true
|
||||||
|
rewrite.rules = [RedundantBraces, Imports, RedundantParens, SortModifiers, PreferCurlyFors]
|
||||||
|
rewrite.redundantBraces.ifElseExpressions = true
|
||||||
|
rewrite.redundantBraces.stringInterpolation = true
|
||||||
|
verticalMultiline.atDefnSite = true
|
||||||
|
verticalMultiline.newlineAfterOpenParen = true
|
||||||
|
optIn.breaksInsideChains = true
|
||||||
|
lineEndings = unix
|
||||||
|
|
||||||
|
runner.dialect = scala3
|
||||||
|
rewrite.scala3.convertToNewSyntax = true
|
||||||
|
rewrite.scala3.removeOptionalBraces = yes
|
||||||
|
rewrite.scala3.insertEndMarkerMinLines = 30
|
||||||
|
rewrite.scala3.removeEndMarkerMaxLines = 29
|
||||||
71
api/src/lu/foyer/App.scala
Normal file
71
api/src/lu/foyer/App.scala
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package lu.foyer
|
||||||
|
|
||||||
|
import lu.foyer.clients.ClientState
|
||||||
|
|
||||||
|
import zio.*
|
||||||
|
import zio.Console.*
|
||||||
|
import zio.http.*
|
||||||
|
import zio.schema.*
|
||||||
|
import zio.http.endpoint.*
|
||||||
|
import zio.http.codec.*
|
||||||
|
import java.net.URI
|
||||||
|
import zio.http.endpoint.openapi.OpenAPIGen
|
||||||
|
import zio.http.endpoint.openapi.SwaggerUI
|
||||||
|
import zio.http.codec.PathCodec.path
|
||||||
|
|
||||||
|
case class JsonApiResponseSingle[T](
|
||||||
|
data: JsonApiResponseEntity[T],
|
||||||
|
links: JsonApiResponseLinks)
|
||||||
|
derives Schema
|
||||||
|
|
||||||
|
case class JsonApiResponseMultiple[T](
|
||||||
|
data: List[JsonApiResponseEntity[T]],
|
||||||
|
links: JsonApiResponseLinks,
|
||||||
|
meta: JsonApiResponseMeta)
|
||||||
|
derives Schema
|
||||||
|
|
||||||
|
case class JsonApiResponseLinks(
|
||||||
|
self: String,
|
||||||
|
first: Option[String] = None,
|
||||||
|
prev: Option[String] = None,
|
||||||
|
next: Option[String] = None,
|
||||||
|
last: Option[String] = None)
|
||||||
|
derives Schema
|
||||||
|
|
||||||
|
case class JsonApiResponseMeta(totalRecords: Int, totalPages: Int) derives Schema
|
||||||
|
|
||||||
|
final case class JsonApiResponseEntity[T](id: String, `type`: String, attributes: T) derives Schema
|
||||||
|
|
||||||
|
case class Page(number: Int, size: Int, totals: Boolean)
|
||||||
|
|
||||||
|
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)))
|
||||||
|
|
||||||
|
object ClientsController:
|
||||||
|
private val fetchMany =
|
||||||
|
Endpoint(Method.GET / "clients").query(pageParams).out[JsonApiResponseMultiple[ClientState]]
|
||||||
|
|
||||||
|
private val fetchManyRoute =
|
||||||
|
fetchMany.implement(page =>
|
||||||
|
ZIO.succeed(
|
||||||
|
JsonApiResponseMultiple[ClientState](
|
||||||
|
List.empty,
|
||||||
|
JsonApiResponseLinks("https://api.example.org"),
|
||||||
|
meta = JsonApiResponseMeta(0, 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val endpoints = List(fetchMany)
|
||||||
|
val routes = Routes(fetchManyRoute)
|
||||||
|
|
||||||
|
object App extends ZIOAppDefault:
|
||||||
|
val openAPI = OpenAPIGen.fromEndpoints(ClientsController.endpoints)
|
||||||
|
val routes = ClientsController.routes ++ SwaggerUI.routes("docs" / "openapi", openAPI)
|
||||||
|
|
||||||
|
override def run = Server.serve(routes).provide(Server.default)
|
||||||
37
build.sc
Normal file
37
build.sc
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
// scalafmt: { runner.dialect = scala213 }
|
||||||
|
package build
|
||||||
|
import mill._, scalalib._
|
||||||
|
|
||||||
|
object Versions {
|
||||||
|
val zio = "2.1.15"
|
||||||
|
val zioJson = "0.7.33"
|
||||||
|
val zioSchema = "1.6.3"
|
||||||
|
val zioHttp = "3.0.1"
|
||||||
|
val zioPrelude = "1.0.0-RC39"
|
||||||
|
}
|
||||||
|
|
||||||
|
trait CommonModule extends ScalaModule {
|
||||||
|
def scalaVersion = "3.6.3"
|
||||||
|
def ivyDeps = Agg(
|
||||||
|
ivy"dev.zio::zio:${Versions.zio}",
|
||||||
|
ivy"dev.zio::zio-schema:${Versions.zioSchema}",
|
||||||
|
ivy"dev.zio::zio-schema-derivation:${Versions.zioSchema}",
|
||||||
|
ivy"dev.zio::zio-prelude:${Versions.zioPrelude}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object model extends CommonModule
|
||||||
|
|
||||||
|
object core extends CommonModule {
|
||||||
|
def moduleDeps = Seq(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
object api extends CommonModule {
|
||||||
|
def moduleDeps = Seq(core)
|
||||||
|
def ivyDeps = Agg(
|
||||||
|
ivy"dev.zio::zio:${Versions.zio}",
|
||||||
|
ivy"dev.zio::zio-http:${Versions.zioHttp}",
|
||||||
|
ivy"dev.zio::zio-json:${Versions.zioJson}",
|
||||||
|
ivy"dev.zio::zio-schema-json:${Versions.zioSchema}"
|
||||||
|
)
|
||||||
|
}
|
||||||
23
core/src/lu/foyer/clients/ClientCommand.scala
Normal file
23
core/src/lu/foyer/clients/ClientCommand.scala
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package lu.foyer
|
||||||
|
package clients
|
||||||
|
|
||||||
|
import zio.schema.*
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
enum ClientCommand derives Schema:
|
||||||
|
case Create(
|
||||||
|
lastName: ClientLastName,
|
||||||
|
firstName: ClientFirstName,
|
||||||
|
drivingLicenseDate: Option[ClientDrivingLicenseDate],
|
||||||
|
phoneNumber: Option[PhoneNumberInput],
|
||||||
|
email: Option[Email],
|
||||||
|
address: Option[Address])
|
||||||
|
case Update(
|
||||||
|
lastName: Option[ClientLastName],
|
||||||
|
firstName: Option[ClientFirstName],
|
||||||
|
drivingLicenseDate: Option[ClientDrivingLicenseDate],
|
||||||
|
phoneNumber: Option[PhoneNumberInput],
|
||||||
|
email: Option[Email],
|
||||||
|
address: Option[Address])
|
||||||
|
case Disable(reason: ClientDisabledReason)
|
||||||
24
core/src/lu/foyer/clients/ClientEvent.scala
Normal file
24
core/src/lu/foyer/clients/ClientEvent.scala
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package lu.foyer
|
||||||
|
package clients
|
||||||
|
|
||||||
|
import zio.schema.*
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
enum ClientEvent derives Schema:
|
||||||
|
case Created(
|
||||||
|
lastName: ClientLastName,
|
||||||
|
firstName: ClientFirstName,
|
||||||
|
birthDate: ClientBirthDate,
|
||||||
|
drivingLicenseDate: Option[ClientDrivingLicenseDate],
|
||||||
|
phoneNumber: Option[PhoneNumberInput],
|
||||||
|
email: Option[Email],
|
||||||
|
address: Option[Address])
|
||||||
|
case Updated(
|
||||||
|
lastName: Option[ClientLastName],
|
||||||
|
firstName: Option[ClientFirstName],
|
||||||
|
birthDate: Option[ClientBirthDate],
|
||||||
|
drivingLicenseDate: Option[ClientDrivingLicenseDate],
|
||||||
|
phoneNumber: Option[PhoneNumberInput],
|
||||||
|
email: Option[Email],
|
||||||
|
address: Option[Address])
|
||||||
24
core/src/lu/foyer/clients/ClientState.scala
Normal file
24
core/src/lu/foyer/clients/ClientState.scala
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package lu.foyer
|
||||||
|
package clients
|
||||||
|
|
||||||
|
import zio.schema.*
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
enum ClientState derives Schema:
|
||||||
|
case Actif(
|
||||||
|
lastName: ClientLastName,
|
||||||
|
firstName: ClientFirstName,
|
||||||
|
birthDate: ClientBirthDate,
|
||||||
|
drivingLicenseDate: Option[ClientDrivingLicenseDate],
|
||||||
|
phoneNumber: Option[PhoneNumberInput],
|
||||||
|
email: Option[Email],
|
||||||
|
address: Option[Address])
|
||||||
|
case Inactif(
|
||||||
|
lastName: ClientLastName,
|
||||||
|
firstName: ClientFirstName,
|
||||||
|
birthDate: ClientBirthDate,
|
||||||
|
drivingLicenseDate: Option[ClientDrivingLicenseDate],
|
||||||
|
phoneNumber: Option[PhoneNumberInput],
|
||||||
|
email: Option[Email],
|
||||||
|
address: Option[Address])
|
||||||
262
flake.lock
generated
Normal file
262
flake.lock
generated
Normal file
|
|
@ -0,0 +1,262 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"cachix": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"git-hooks": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1737621947,
|
||||||
|
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "cachix",
|
||||||
|
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "latest",
|
||||||
|
"repo": "cachix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devenv": {
|
||||||
|
"inputs": {
|
||||||
|
"cachix": "cachix",
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
|
"nix": "nix",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1740460834,
|
||||||
|
"narHash": "sha256-RUL1r8zH5wG5L1YipNj1bmt0Oi8L9qwzXsf/ww8WxBc=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "9e4003b2702483bd962dac3d4ff43e8dafb93cda",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733328505,
|
||||||
|
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"devenv",
|
||||||
|
"nix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712014858,
|
||||||
|
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1737465171,
|
||||||
|
"narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"git-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709087332,
|
||||||
|
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libgit2": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1697646580,
|
||||||
|
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||||
|
"owner": "libgit2",
|
||||||
|
"repo": "libgit2",
|
||||||
|
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "libgit2",
|
||||||
|
"repo": "libgit2",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"libgit2": "libgit2",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"nixpkgs-23-11": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"nixpkgs-regression": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"pre-commit-hooks": [
|
||||||
|
"devenv"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1734114420,
|
||||||
|
"narHash": "sha256-n52PUzub5jZWc8nI/sR7UICOheU8rNA+YZ73YaHeCBg=",
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"repo": "nix",
|
||||||
|
"rev": "bde6a1a0d1f2af86caa4d20d23eca019f3d57eee",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"ref": "devenv-2.24",
|
||||||
|
"repo": "nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733212471,
|
||||||
|
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1717432640,
|
||||||
|
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "release-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733477122,
|
||||||
|
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "rolling",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"nixpkgs": "nixpkgs_3",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
58
flake.nix
Normal file
58
flake.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:cachix/devenv-nixpkgs/rolling";
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
devenv.url = "github:cachix/devenv";
|
||||||
|
devenv.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
nixConfig = {
|
||||||
|
extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=";
|
||||||
|
extra-substituters = "https://devenv.cachix.org";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
devenv,
|
||||||
|
systems,
|
||||||
|
...
|
||||||
|
}@inputs:
|
||||||
|
let
|
||||||
|
forEachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forEachSystem (system: {
|
||||||
|
devenv-up = self.devShells.${system}.default.config.procfileScript;
|
||||||
|
devenv-test = self.devShells.${system}.default.config.test;
|
||||||
|
});
|
||||||
|
|
||||||
|
devShells = forEachSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = devenv.lib.mkShell {
|
||||||
|
inherit inputs pkgs;
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
# https://devenv.sh/reference/options/
|
||||||
|
packages = [
|
||||||
|
pkgs.mill
|
||||||
|
pkgs.kafkactl
|
||||||
|
];
|
||||||
|
|
||||||
|
services = {
|
||||||
|
kafka.enable = true;
|
||||||
|
mongodb.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
26
model/src/lu/foyer/RefinedType.scala
Normal file
26
model/src/lu/foyer/RefinedType.scala
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package lu.foyer
|
||||||
|
|
||||||
|
import zio.prelude.*
|
||||||
|
import zio.schema.Schema
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
trait RefinedType[Base, New]:
|
||||||
|
inline def assume(value: Base): New = value.asInstanceOf[New]
|
||||||
|
def validation(value: Base): Validation[String, New]
|
||||||
|
|
||||||
|
trait RefinedString[New <: String] extends RefinedType[String, New]:
|
||||||
|
given Schema[New] = Schema[String].transformOrFail(
|
||||||
|
validation(_).toEither.left.map(_.toList.mkString(", ")),
|
||||||
|
Right(_)
|
||||||
|
)
|
||||||
|
|
||||||
|
trait RefinedLocalDate[New <: LocalDate] extends RefinedType[LocalDate, New]:
|
||||||
|
override def validation(value: LocalDate): Validation[String, New] =
|
||||||
|
Validation.succeed(assume(value))
|
||||||
|
given Schema[New] = Schema[LocalDate].transform(assume, identity)
|
||||||
|
|
||||||
|
trait NonBlankString[New <: String] extends RefinedString[New]:
|
||||||
|
override def validation(value: String): Validation[String, New] =
|
||||||
|
if value.isBlank then Validation.fail("Cannot be blank")
|
||||||
|
else Validation.succeed(assume(value))
|
||||||
20
model/src/lu/foyer/clients/Address.scala
Normal file
20
model/src/lu/foyer/clients/Address.scala
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package lu.foyer
|
||||||
|
package clients
|
||||||
|
|
||||||
|
import zio.schema.*
|
||||||
|
|
||||||
|
opaque type AddressStreet <: String = String
|
||||||
|
object AddressStreet extends NonBlankString[AddressStreet]
|
||||||
|
|
||||||
|
opaque type AddressPostalCode <: String = String
|
||||||
|
object AddressPostalCode extends NonBlankString[AddressPostalCode]
|
||||||
|
|
||||||
|
opaque type AddressLocality <: String = String
|
||||||
|
object AddressLocality extends NonBlankString[AddressLocality]
|
||||||
|
|
||||||
|
case class Address(
|
||||||
|
street: AddressStreet,
|
||||||
|
postalCode: AddressPostalCode,
|
||||||
|
locality: AddressLocality,
|
||||||
|
country: Country)
|
||||||
|
derives Schema
|
||||||
49
model/src/lu/foyer/clients/Client.scala
Normal file
49
model/src/lu/foyer/clients/Client.scala
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package lu.foyer
|
||||||
|
package clients
|
||||||
|
|
||||||
|
import zio.schema.*
|
||||||
|
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
opaque type ClientLastName <: String = String
|
||||||
|
object ClientLastName extends NonBlankString[ClientLastName]
|
||||||
|
|
||||||
|
opaque type ClientFirstName <: String = String
|
||||||
|
object ClientFirstName extends NonBlankString[ClientFirstName]
|
||||||
|
|
||||||
|
opaque type ClientBirthDate <: LocalDate = LocalDate
|
||||||
|
object ClientBirthDate extends RefinedLocalDate[ClientBirthDate]
|
||||||
|
|
||||||
|
opaque type ClientDrivingLicenseDate <: LocalDate = LocalDate
|
||||||
|
object ClientDrivingLicenseDate extends RefinedLocalDate[ClientDrivingLicenseDate]
|
||||||
|
|
||||||
|
opaque type Email <: String = String
|
||||||
|
object Email extends NonBlankString[Email]
|
||||||
|
|
||||||
|
opaque type NationalNumber <: String = String
|
||||||
|
object NationalNumber extends NonBlankString[NationalNumber]
|
||||||
|
|
||||||
|
case class Client(
|
||||||
|
lastName: ClientFirstName,
|
||||||
|
firstName: ClientLastName,
|
||||||
|
drivingLicenseDate: ClientDrivingLicenseDate,
|
||||||
|
phoneNumber: PhoneNumber,
|
||||||
|
email: Email,
|
||||||
|
address: Address)
|
||||||
|
derives Schema
|
||||||
|
|
||||||
|
// TODO validate using libphonenumber
|
||||||
|
case class PhoneNumber(
|
||||||
|
country: Country,
|
||||||
|
nationalNumber: NationalNumber,
|
||||||
|
lastVerifiedAt: Option[Instant])
|
||||||
|
derives Schema
|
||||||
|
|
||||||
|
case class PhoneNumberInput(
|
||||||
|
country: Country,
|
||||||
|
nationalNumber: String)
|
||||||
|
derives Schema
|
||||||
|
|
||||||
|
enum ClientDisabledReason derives Schema:
|
||||||
|
case GDPR, Death
|
||||||
7
model/src/lu/foyer/clients/Country.scala
Normal file
7
model/src/lu/foyer/clients/Country.scala
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package lu.foyer
|
||||||
|
package clients
|
||||||
|
|
||||||
|
import zio.schema.*
|
||||||
|
|
||||||
|
enum Country derives Schema:
|
||||||
|
case LU, FR, BE
|
||||||
Loading…
Add table
Add a link
Reference in a new issue