Created
January 8, 2019 14:08
-
-
Save aaronmu/1d5d5db8c73b67928a0a6811f37d1bfd to your computer and use it in GitHub Desktop.
Revisions
-
aaronmu created this gist
Jan 8, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,266 @@ module WerknemerInRooster module Types = open System open Common.Validation type Werknemer = { WerknemerId : string WerknemerInternalId : Guid RoosterId : Guid Voornaam : string Achternaam : string } type WerknemerWerdToegevoegdAanRooster = { WerknemerId : string WerknemerInternalId : Guid RoosterId : Guid Voornaam : string Achternaam : string } type WerknemerWerdVerwijderdUitRooster = { RoosterId : Guid WerknemerInternalId : Guid } type Event = | WerknemerWerdToegevoegdAanRooster of WerknemerWerdToegevoegdAanRooster | WerknemerWerdVerwijderdUitRooster of WerknemerWerdVerwijderdUitRooster [<CLIMutable>] type UnvalidatedVoegWerknemerToeForm = { WerknemerId : string WerknemerInternalId : string RoosterId : string Voornaam : string Achternaam : string } type ValidatedVoegWerknemerToeForm = { WerknemerId : string WerknemerInternalId : Guid RoosterId : Guid Voornaam : string Achternaam : string } type VoegWerknemerToeAanRoosterError = | Validation of ValidationError list | PersistenceError of exn list | AuthenticationError [<CLIMutable>] type UnvalidatedVerwijderWerknemerUitRoosterForm = { RoosterId : string WerknemerInternalId : string } type ValidatedVerwijderWerknemerUitRoosterForm = { RoosterId : Guid WerknemerInternalId : Guid } type VerwijderWerknemerUitRoosterError = | Validation of ValidationError list | PersistenceError of exn list | AuthenticationError module UnvalidatedVoegWerknemerToeForm = open Types open Common.Validation let validate (unvalidated : UnvalidatedVoegWerknemerToeForm) : Result<ValidatedVoegWerknemerToeForm, ValidationError list> = let werknemerIdResult = notBlank "WerknemerId" unvalidated.WerknemerId let werknemerInternalIdResult = validateGuid "WerknemerInternalId" unvalidated.WerknemerInternalId let roosterIdResult = validateGuid "RoosterId" unvalidated.RoosterId let voornaamResult = notBlank "Voornaam" unvalidated.Voornaam let achternaamResult = notBlank "Achternaam" unvalidated.Achternaam match werknemerIdResult, werknemerInternalIdResult, roosterIdResult, voornaamResult, achternaamResult with | Ok werknemerId, Ok werknemerInternalIdResult, Ok roosterId, Ok voornaam, Ok achternaam -> Ok { WerknemerId = werknemerId WerknemerInternalId = werknemerInternalIdResult RoosterId = roosterId Voornaam = voornaam Achternaam = achternaam } | _ -> let err = function | Ok _ -> None | Error s -> Some s [err werknemerIdResult; err werknemerInternalIdResult; err roosterIdResult; err voornaamResult; err achternaamResult] |> List.choose id |> Error module UnvalidatedVerwijderWerknemerUitRoosterForm = open Common.Validation open Types let validate (unvalidated : UnvalidatedVerwijderWerknemerUitRoosterForm) = let roosterIdResult = validateGuid "RoosterId" unvalidated.RoosterId let werknemerInternalIdResult = validateGuid "WerknemerInternalId" unvalidated.WerknemerInternalId match roosterIdResult, werknemerInternalIdResult with | Ok roosterId, Ok werknemerInternalId -> Ok { RoosterId = roosterId WerknemerInternalId = werknemerInternalId } | _ -> let err = function | Ok _ -> None | Error s -> Some s [err roosterIdResult; err werknemerInternalIdResult] |> List.choose id |> Error module Aggregate = open System open Types let voegWerknemerToeAanRooster (werknemerId:string) (werknemerInternalId:Guid) (roosterId:Guid) (voornaam:string) (achternaam:string) = let werknemer : WerknemerWerdToegevoegdAanRooster = { WerknemerId = werknemerId WerknemerInternalId = werknemerInternalId RoosterId = roosterId Voornaam = voornaam Achternaam = achternaam } WerknemerWerdToegevoegdAanRooster werknemer let verwijderWerknemerUitRooster (roosterId:Guid) (werknemerInternalId:Guid) = WerknemerWerdVerwijderdUitRooster { RoosterId = roosterId; WerknemerInternalId = werknemerInternalId } module Serialization = open Types open Common open Newtonsoft.Json let contract (event : Event) = let ns = "WerknemerInRooster" let version = 1 let createContract = Contract.create ns version match event with | WerknemerWerdToegevoegdAanRooster _ -> "WerknemerWerdToegevoegdAanRooster" | WerknemerWerdVerwijderdUitRooster _ -> "WerknemerWerdVerwijderdUitRooster" |> createContract let serializePayload = function | WerknemerWerdToegevoegdAanRooster payload -> JsonConvert.SerializeObject payload | WerknemerWerdVerwijderdUitRooster payload -> JsonConvert.SerializeObject payload module Persistence = open System open Types open Npgsql.FSharp open Infrastructure let toStmt event : Psql.Statement = match event with | WerknemerWerdToegevoegdAanRooster e -> let sql = "INSERT INTO werknemers (werknemerid, werknemerinternalid, roosterid, voornaam, achternaam) VALUES (@werknemerId, @werknemerInternalId, @roosterId, @voornaam, @achternaam)" let ps = [ "werknemerId", SqlValue.String e.WerknemerId "werknemerInternalId", SqlValue.Uuid e.WerknemerInternalId "roosterId", SqlValue.Uuid e.RoosterId "voornaam", SqlValue.String e.Voornaam "achternaam", SqlValue.String e.Achternaam ] sql,ps | WerknemerWerdVerwijderdUitRooster e -> let sqls = [ "DELETE FROM werknemers WHERE werknemerinternalid = @id" "UPDATE diensten SET huidigebezetting = (huidigebezetting - 1) WHERE dienstid IN (SELECT dienstid FROM geplandediensten WHERE medewerkerid = @id)" "DELETE FROM geplandediensten WHERE medewerkerid = @id" ] let ps = [ "id", SqlValue.Uuid e.WerknemerInternalId ] String.concat ";" sqls, ps let save (now : unit -> DateTime) user conn events = let append = EventStore.append now Serialization.serializePayload Serialization.contract user conn let eventStmts = append events let stmts = List.map toStmt events [ stmts; eventStmts ] |> List.concat |> Psql.transactional conn module UseCases = open System open Types open Common open Common.Types open Common.AsyncResult.Operators let private now () = DateTime.Now let private authenticateOr err msg = msg.Claims |> Helpers.getUsername |> Result.ofOption err |> Result.map (tuple msg.Payload) |> Async.retn // @todo twee keer zelfde werknemer toevoegen zal momenteel crashen // kunnen dit makkelijk idempotent maken let voegWerknemerToeAanRooster conn (msg : Message<UnvalidatedVoegWerknemerToeForm>) = let validate (form, username) = form |> UnvalidatedVoegWerknemerToeForm.validate |> Result.mapError VoegWerknemerToeAanRoosterError.Validation |> Result.map (tuple username) |> Async.retn let create ((username, form) : string * ValidatedVoegWerknemerToeForm) = Aggregate.voegWerknemerToeAanRooster form.WerknemerId form.WerknemerInternalId form.RoosterId form.Voornaam form.Achternaam |> List.retn |> tuple username |> AsyncResult.retn let save (username, events) = events |> Persistence.save now conn username |> AsyncResult.map (always events) |> AsyncResult.mapError VoegWerknemerToeAanRoosterError.PersistenceError let msg = AsyncResult.retn msg msg >>= authenticateOr VoegWerknemerToeAanRoosterError.AuthenticationError >>= validate >>= create >>= save let verwijderWerknerUitRooster conn (msg : Message<UnvalidatedVerwijderWerknemerUitRoosterForm>) = let validate (form, username) = form |> UnvalidatedVerwijderWerknemerUitRoosterForm.validate |> Result.mapError VerwijderWerknemerUitRoosterError.Validation |> Result.map (tuple username) |> Async.retn let verwijder ((username, form) : string * ValidatedVerwijderWerknemerUitRoosterForm) = (form.RoosterId, form.WerknemerInternalId) ||> Aggregate.verwijderWerknemerUitRooster |> List.retn |> tuple username |> AsyncResult.retn let save (username, events) = events |> Persistence.save now conn username |> AsyncResult.map (always events) |> AsyncResult.mapError VerwijderWerknemerUitRoosterError.PersistenceError let msg = AsyncResult.retn msg msg >>= authenticateOr VerwijderWerknemerUitRoosterError.AuthenticationError >>= validate >>= verwijder >>= save