Convert Data Storage to PostgreSQL Documents #74

Merged
danieljsummers merged 11 commits from pg-doc into main 2023-10-10 02:15:39 +00:00
4 changed files with 116 additions and 83 deletions
Showing only changes of commit cc4347bc6e - Show all commits

View File

@ -1,6 +1,6 @@
module MyPrayerJournal.Data module MyPrayerJournal.Data
/// Table(s) used by myPrayerJournal /// Table(!) used by myPrayerJournal
module Table = module Table =
/// Requests /// Requests
@ -8,16 +8,69 @@ module Table =
let Request = "mpj.request" let Request = "mpj.request"
/// JSON serialization customizations
[<RequireQualifiedAccess>]
module Json =
open System.Text.Json.Serialization
/// Convert a wrapped DU to/from its string representation
type WrappedJsonConverter<'T> (wrap : string -> 'T, unwrap : 'T -> string) =
inherit JsonConverter<'T> ()
override _.Read(reader, _, _) =
wrap (reader.GetString ())
override _.Write(writer, value, _) =
writer.WriteStringValue (unwrap value)
open System.Text.Json
open NodaTime.Serialization.SystemTextJson
/// JSON serializer options to support the target domain
let options =
let opts = JsonSerializerOptions ()
[ WrappedJsonConverter (Recurrence.ofString, Recurrence.toString) :> JsonConverter
WrappedJsonConverter (RequestAction.ofString, RequestAction.toString)
WrappedJsonConverter (RequestId.ofString, RequestId.toString)
WrappedJsonConverter (UserId, UserId.toString)
JsonFSharpConverter ()
]
|> List.iter opts.Converters.Add
let _ = opts.ConfigureForNodaTime NodaTime.DateTimeZoneProviders.Tzdb
opts.PropertyNamingPolicy <- JsonNamingPolicy.CamelCase
opts
open BitBadger.Npgsql.FSharp.Documents open BitBadger.Npgsql.FSharp.Documents
module DataConnection = /// Connection
[<RequireQualifiedAccess>]
module Connection =
let ensureDb () = backgroundTask { open BitBadger.Npgsql.Documents
open Microsoft.Extensions.Configuration
open Npgsql
open System.Text.Json
/// Ensure the database is ready to use
let private ensureDb () = backgroundTask {
do! Custom.nonQuery "CREATE SCHEMA IF NOT EXISTS mpj" [] do! Custom.nonQuery "CREATE SCHEMA IF NOT EXISTS mpj" []
do! Definition.ensureTable Table.Request do! Definition.ensureTable Table.Request
do! Definition.ensureIndex Table.Request Optimized do! Definition.ensureIndex Table.Request Optimized
} }
/// Set up the data environment
let setUp (cfg : IConfiguration) = backgroundTask {
let builder = NpgsqlDataSourceBuilder (cfg.GetConnectionString "mpj")
let _ = builder.UseNodaTime ()
Configuration.useDataSource (builder.Build ())
Configuration.useSerializer
{ new IDocumentSerializer with
member _.Serialize<'T> (it : 'T) = JsonSerializer.Serialize (it, Json.options)
member _.Deserialize<'T> (it : string) = JsonSerializer.Deserialize<'T> (it, Json.options)
}
do! ensureDb ()
}
/// Data access functions for requests /// Data access functions for requests
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -30,6 +83,10 @@ module Request =
do! insert Table.Request (RequestId.toString req.Id) req do! insert Table.Request (RequestId.toString req.Id) req
} }
/// Does a request exist for the given request ID and user ID?
let existsById (reqId : RequestId) (userId : UserId) =
Exists.byContains Table.Request {| Id = reqId; UserId = userId |}
/// Retrieve a request by its ID and user ID (includes history and notes) /// Retrieve a request by its ID and user ID (includes history and notes)
let tryByIdFull reqId userId = backgroundTask { let tryByIdFull reqId userId = backgroundTask {
match! Find.byId<Request> Table.Request (RequestId.toString reqId) with match! Find.byId<Request> Table.Request (RequestId.toString reqId) with
@ -44,10 +101,6 @@ module Request =
| None -> return None | None -> return None
} }
/// Does a request exist for the given request ID and user ID?
let private existsById (reqId : RequestId) (userId : UserId) =
Exists.byContains Table.Request {| Id = reqId; UserId = userId |}
/// Update recurrence for a request /// Update recurrence for a request
let updateRecurrence reqId userId (recurType : Recurrence) = backgroundTask { let updateRecurrence reqId userId (recurType : Recurrence) = backgroundTask {
let dbId = RequestId.toString reqId let dbId = RequestId.toString reqId
@ -57,7 +110,7 @@ module Request =
} }
/// Update the show-after time for a request /// Update the show-after time for a request
let updateShowAfter reqId userId (showAfter : Instant) = backgroundTask { let updateShowAfter reqId userId (showAfter : Instant option) = backgroundTask {
let dbId = RequestId.toString reqId let dbId = RequestId.toString reqId
match! existsById reqId userId with match! existsById reqId userId with
| true -> do! Update.partialById Table.Request dbId {| ShowAfter = showAfter |} | true -> do! Update.partialById Table.Request dbId {| ShowAfter = showAfter |}
@ -65,7 +118,7 @@ module Request =
} }
/// Update the snoozed and show-after values for a request /// Update the snoozed and show-after values for a request
let updateSnoozed reqId userId (until : Instant) = backgroundTask { let updateSnoozed reqId userId (until : Instant option) = backgroundTask {
let dbId = RequestId.toString reqId let dbId = RequestId.toString reqId
match! existsById reqId userId with match! existsById reqId userId with
| true -> do! Update.partialById Table.Request dbId {| SnoozedUntil = until; ShowAfter = until |} | true -> do! Update.partialById Table.Request dbId {| SnoozedUntil = until; ShowAfter = until |}

View File

@ -45,16 +45,12 @@ module Error =
open System.Security.Claims open System.Security.Claims
open LiteDB
open Microsoft.AspNetCore.Http open Microsoft.AspNetCore.Http
open NodaTime open NodaTime
/// Extensions on the HTTP context /// Extensions on the HTTP context
type HttpContext with type HttpContext with
/// The LiteDB database
member this.Db = this.GetService<LiteDatabase> ()
/// The "sub" for the current user (None if no user is authenticated) /// The "sub" for the current user (None if no user is authenticated)
member this.CurrentUser = member this.CurrentUser =
this.User this.User
@ -83,6 +79,8 @@ type HttpContext with
| None -> DateTimeZone.Utc | None -> DateTimeZone.Utc
open MyPrayerJournal.Data
/// Handler helpers /// Handler helpers
[<AutoOpen>] [<AutoOpen>]
module private Helpers = module private Helpers =
@ -127,7 +125,7 @@ module private Helpers =
let pageContext (ctx : HttpContext) pageTitle content = backgroundTask { let pageContext (ctx : HttpContext) pageTitle content = backgroundTask {
let! hasSnoozed = let! hasSnoozed =
match ctx.CurrentUser with match ctx.CurrentUser with
| Some _ -> LiteData.hasSnoozed ctx.UserId (ctx.Now ()) ctx.Db | Some _ -> Journal.hasSnoozed ctx.UserId (ctx.Now ())
| None -> Task.FromResult false | None -> Task.FromResult false
return return
{ IsAuthenticated = Option.isSome ctx.CurrentUser { IsAuthenticated = Option.isSome ctx.CurrentUser
@ -238,7 +236,6 @@ module Models =
} }
open MyPrayerJournal.LiteData.Extensions
open NodaTime.Text open NodaTime.Text
/// Handlers for less-than-full-page HTML requests /// Handlers for less-than-full-page HTML requests
@ -254,14 +251,14 @@ module Components =
| Some snooze, _ when snooze < now -> true | Some snooze, _ when snooze < now -> true
| _, Some hide when hide < now -> true | _, Some hide when hide < now -> true
| _, _ -> false | _, _ -> false
let! journal = LiteData.journalByUserId ctx.UserId ctx.Db let! journal = Journal.forUser ctx.UserId
let shown = journal |> List.filter shouldBeShown let shown = journal |> List.filter shouldBeShown
return! renderComponent [ Views.Journal.journalItems now ctx.TimeZone shown ] next ctx return! renderComponent [ Views.Journal.journalItems now ctx.TimeZone shown ] next ctx
} }
// GET /components/request-item/[req-id] // GET /components/request-item/[req-id]
let requestItem reqId : HttpHandler = requireUser >=> fun next ctx -> task { let requestItem reqId : HttpHandler = requireUser >=> fun next ctx -> task {
match! LiteData.tryJournalById (RequestId.ofString reqId) ctx.UserId ctx.Db with match! Journal.tryById (RequestId.ofString reqId) ctx.UserId with
| Some req -> return! renderComponent [ Views.Request.reqListItem (ctx.Now ()) ctx.TimeZone req ] next ctx | Some req -> return! renderComponent [ Views.Request.reqListItem (ctx.Now ()) ctx.TimeZone req ] next ctx
| None -> return! Error.notFound next ctx | None -> return! Error.notFound next ctx
} }
@ -272,7 +269,7 @@ module Components =
// GET /components/request/[req-id]/notes // GET /components/request/[req-id]/notes
let notes requestId : HttpHandler = requireUser >=> fun next ctx -> task { let notes requestId : HttpHandler = requireUser >=> fun next ctx -> task {
let! notes = LiteData.notesById (RequestId.ofString requestId) ctx.UserId ctx.Db let! notes = Note.byRequestId (RequestId.ofString requestId) ctx.UserId
return! renderComponent (Views.Request.notes (ctx.Now ()) ctx.TimeZone (List.ofArray notes)) next ctx return! renderComponent (Views.Request.notes (ctx.Now ()) ctx.TimeZone (List.ofArray notes)) next ctx
} }
@ -333,7 +330,7 @@ module Request =
return! partial "Add Prayer Request" return! partial "Add Prayer Request"
(Views.Request.edit (JournalRequest.ofRequestLite Request.empty) returnTo true) next ctx (Views.Request.edit (JournalRequest.ofRequestLite Request.empty) returnTo true) next ctx
| _ -> | _ ->
match! LiteData.tryJournalById (RequestId.ofString requestId) ctx.UserId ctx.Db with match! Journal.tryById (RequestId.ofString requestId) ctx.UserId with
| Some req -> | Some req ->
debug ctx "Found - sending view" debug ctx "Found - sending view"
return! partial "Edit Prayer Request" (Views.Request.edit req returnTo false) next ctx return! partial "Edit Prayer Request" (Views.Request.edit req returnTo false) next ctx
@ -344,46 +341,42 @@ module Request =
// PATCH /request/[req-id]/prayed // PATCH /request/[req-id]/prayed
let prayed requestId : HttpHandler = requireUser >=> fun next ctx -> task { let prayed requestId : HttpHandler = requireUser >=> fun next ctx -> task {
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
let reqId = RequestId.ofString requestId let reqId = RequestId.ofString requestId
match! LiteData.tryRequestById reqId userId db with match! Journal.tryById reqId userId with
| Some req -> | Some req ->
let now = ctx.Now () let now = ctx.Now ()
do! LiteData.addHistory reqId userId { AsOf = now; Status = Prayed; Text = None } db do! History.add reqId userId { AsOf = now; Status = Prayed; Text = None }
let nextShow = let nextShow =
match Recurrence.duration req.Recurrence with match Recurrence.duration req.Recurrence with
| 0L -> None | 0L -> None
| duration -> Some <| now.Plus (Duration.FromSeconds duration) | duration -> Some <| now.Plus (Duration.FromSeconds duration)
do! LiteData.updateShowAfter reqId userId nextShow db do! Request.updateShowAfter reqId userId nextShow
do! db.SaveChanges ()
return! (withSuccessMessage "Request marked as prayed" >=> Components.journalItems) next ctx return! (withSuccessMessage "Request marked as prayed" >=> Components.journalItems) next ctx
| None -> return! Error.notFound next ctx | None -> return! Error.notFound next ctx
} }
/// POST /request/[req-id]/note // POST /request/[req-id]/note
let addNote requestId : HttpHandler = requireUser >=> fun next ctx -> task { let addNote requestId : HttpHandler = requireUser >=> fun next ctx -> task {
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
let reqId = RequestId.ofString requestId let reqId = RequestId.ofString requestId
match! LiteData.tryRequestById reqId userId db with match! Request.existsById reqId userId with
| Some _ -> | true ->
let! notes = ctx.BindFormAsync<Models.NoteEntry> () let! notes = ctx.BindFormAsync<Models.NoteEntry> ()
do! LiteData.addNote reqId userId { AsOf = ctx.Now (); Notes = notes.notes } db do! Note.add reqId userId { AsOf = ctx.Now (); Notes = notes.notes }
do! db.SaveChanges ()
return! (withSuccessMessage "Added Notes" >=> hideModal "notes" >=> created) next ctx return! (withSuccessMessage "Added Notes" >=> hideModal "notes" >=> created) next ctx
| None -> return! Error.notFound next ctx | false -> return! Error.notFound next ctx
} }
// GET /requests/active // GET /requests/active
let active : HttpHandler = requireUser >=> fun next ctx -> task { let active : HttpHandler = requireUser >=> fun next ctx -> task {
let! reqs = LiteData.journalByUserId ctx.UserId ctx.Db let! reqs = Journal.forUser ctx.UserId
return! partial "Active Requests" (Views.Request.active (ctx.Now ()) ctx.TimeZone reqs) next ctx return! partial "Active Requests" (Views.Request.active (ctx.Now ()) ctx.TimeZone reqs) next ctx
} }
// GET /requests/snoozed // GET /requests/snoozed
let snoozed : HttpHandler = requireUser >=> fun next ctx -> task { let snoozed : HttpHandler = requireUser >=> fun next ctx -> task {
let! reqs = LiteData.journalByUserId ctx.UserId ctx.Db let! reqs = Journal.forUser ctx.UserId
let now = ctx.Now () let now = ctx.Now ()
let snoozed = reqs let snoozed = reqs
|> List.filter (fun it -> defaultArg (it.SnoozedUntil |> Option.map (fun it -> it > now)) false) |> List.filter (fun it -> defaultArg (it.SnoozedUntil |> Option.map (fun it -> it > now)) false)
@ -392,62 +385,56 @@ module Request =
// GET /requests/answered // GET /requests/answered
let answered : HttpHandler = requireUser >=> fun next ctx -> task { let answered : HttpHandler = requireUser >=> fun next ctx -> task {
let! reqs = LiteData.answeredRequests ctx.UserId ctx.Db let! reqs = Journal.answered ctx.UserId
return! partial "Answered Requests" (Views.Request.answered (ctx.Now ()) ctx.TimeZone reqs) next ctx return! partial "Answered Requests" (Views.Request.answered (ctx.Now ()) ctx.TimeZone reqs) next ctx
} }
// GET /request/[req-id]/full // GET /request/[req-id]/full
let getFull requestId : HttpHandler = requireUser >=> fun next ctx -> task { let getFull requestId : HttpHandler = requireUser >=> fun next ctx -> task {
match! LiteData.tryFullRequestById (RequestId.ofString requestId) ctx.UserId ctx.Db with match! Request.tryByIdFull (RequestId.ofString requestId) ctx.UserId with
| Some req -> return! partial "Prayer Request" (Views.Request.full ctx.Clock ctx.TimeZone req) next ctx | Some req -> return! partial "Prayer Request" (Views.Request.full ctx.Clock ctx.TimeZone req) next ctx
| None -> return! Error.notFound next ctx | None -> return! Error.notFound next ctx
} }
// PATCH /request/[req-id]/show // PATCH /request/[req-id]/show
let show requestId : HttpHandler = requireUser >=> fun next ctx -> task { let show requestId : HttpHandler = requireUser >=> fun next ctx -> task {
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
let reqId = RequestId.ofString requestId let reqId = RequestId.ofString requestId
match! LiteData.tryRequestById reqId userId db with match! Request.existsById reqId userId with
| Some _ -> | true ->
do! LiteData.updateShowAfter reqId userId None db do! Request.updateShowAfter reqId userId None
do! db.SaveChanges ()
return! (withSuccessMessage "Request now shown" >=> Components.requestItem requestId) next ctx return! (withSuccessMessage "Request now shown" >=> Components.requestItem requestId) next ctx
| None -> return! Error.notFound next ctx | false -> return! Error.notFound next ctx
} }
// PATCH /request/[req-id]/snooze // PATCH /request/[req-id]/snooze
let snooze requestId : HttpHandler = requireUser >=> fun next ctx -> task { let snooze requestId : HttpHandler = requireUser >=> fun next ctx -> task {
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
let reqId = RequestId.ofString requestId let reqId = RequestId.ofString requestId
match! LiteData.tryRequestById reqId userId db with match! Request.existsById reqId userId with
| Some _ -> | true ->
let! until = ctx.BindFormAsync<Models.SnoozeUntil> () let! until = ctx.BindFormAsync<Models.SnoozeUntil> ()
let date = let date =
LocalDatePattern.CreateWithInvariantCulture("yyyy-MM-dd").Parse(until.until).Value LocalDatePattern.CreateWithInvariantCulture("yyyy-MM-dd").Parse(until.until).Value
.AtStartOfDayInZone(DateTimeZone.Utc) .AtStartOfDayInZone(DateTimeZone.Utc)
.ToInstant () .ToInstant ()
do! LiteData.updateSnoozed reqId userId (Some date) db do! Request.updateSnoozed reqId userId (Some date)
do! db.SaveChanges ()
return! return!
(withSuccessMessage $"Request snoozed until {until.until}" (withSuccessMessage $"Request snoozed until {until.until}"
>=> hideModal "snooze" >=> hideModal "snooze"
>=> Components.journalItems) next ctx >=> Components.journalItems) next ctx
| None -> return! Error.notFound next ctx | false -> return! Error.notFound next ctx
} }
// PATCH /request/[req-id]/cancel-snooze // PATCH /request/[req-id]/cancel-snooze
let cancelSnooze requestId : HttpHandler = requireUser >=> fun next ctx -> task { let cancelSnooze requestId : HttpHandler = requireUser >=> fun next ctx -> task {
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
let reqId = RequestId.ofString requestId let reqId = RequestId.ofString requestId
match! LiteData.tryRequestById reqId userId db with match! Request.existsById reqId userId with
| Some _ -> | true ->
do! LiteData.updateSnoozed reqId userId None db do! Request.updateSnoozed reqId userId None
do! db.SaveChanges ()
return! (withSuccessMessage "Request unsnoozed" >=> Components.requestItem requestId) next ctx return! (withSuccessMessage "Request unsnoozed" >=> Components.requestItem requestId) next ctx
| None -> return! Error.notFound next ctx | false -> return! Error.notFound next ctx
} }
/// Derive a recurrence from its representation in the form /// Derive a recurrence from its representation in the form
@ -458,7 +445,6 @@ module Request =
// POST /request // POST /request
let add : HttpHandler = requireUser >=> fun next ctx -> task { let add : HttpHandler = requireUser >=> fun next ctx -> task {
let! form = ctx.BindModelAsync<Models.Request> () let! form = ctx.BindModelAsync<Models.Request> ()
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
let now = ctx.Now () let now = ctx.Now ()
let req = let req =
@ -475,8 +461,7 @@ module Request =
} }
|] |]
} }
LiteData.addRequest req db do! Request.add req
do! db.SaveChanges ()
Messages.pushSuccess ctx "Added prayer request" "/journal" Messages.pushSuccess ctx "Added prayer request" "/journal"
return! seeOther "/journal" next ctx return! seeOther "/journal" next ctx
} }
@ -484,25 +469,24 @@ module Request =
// PATCH /request // PATCH /request
let update : HttpHandler = requireUser >=> fun next ctx -> task { let update : HttpHandler = requireUser >=> fun next ctx -> task {
let! form = ctx.BindModelAsync<Models.Request> () let! form = ctx.BindModelAsync<Models.Request> ()
let db = ctx.Db
let userId = ctx.UserId let userId = ctx.UserId
match! LiteData.tryJournalById (RequestId.ofString form.requestId) userId db with // TODO: update the instance and save rather than all these little updates
match! Journal.tryById (RequestId.ofString form.requestId) userId with
| Some req -> | Some req ->
// update recurrence if changed // update recurrence if changed
let recur = parseRecurrence form let recur = parseRecurrence form
match recur = req.Recurrence with match recur = req.Recurrence with
| true -> () | true -> ()
| false -> | false ->
do! LiteData.updateRecurrence req.RequestId userId recur db do! Request.updateRecurrence req.RequestId userId recur
match recur with match recur with
| Immediate -> do! LiteData.updateShowAfter req.RequestId userId None db | Immediate -> do! Request.updateShowAfter req.RequestId userId None
| _ -> () | _ -> ()
// append history // append history
let upd8Text = form.requestText.Trim () let upd8Text = form.requestText.Trim ()
let text = if upd8Text = req.Text then None else Some upd8Text let text = if upd8Text = req.Text then None else Some upd8Text
do! LiteData.addHistory req.RequestId userId do! History.add req.RequestId userId
{ AsOf = ctx.Now (); Status = (Option.get >> RequestAction.ofString) form.status; Text = text } db { AsOf = ctx.Now (); Status = (Option.get >> RequestAction.ofString) form.status; Text = text }
do! db.SaveChanges ()
let nextUrl = let nextUrl =
match form.returnTo with match form.returnTo with
| "active" -> "/requests/active" | "active" -> "/requests/active"

View File

@ -21,15 +21,16 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BitBadger.Npgsql.FSharp.Documents" Version="1.0.0-beta3" /> <PackageReference Include="BitBadger.Npgsql.FSharp.Documents" Version="1.0.0-beta3" />
<PackageReference Include="FSharp.SystemTextJson" Version="1.1.23" /> <PackageReference Include="FSharp.SystemTextJson" Version="1.2.42" />
<PackageReference Include="FunctionalCuid" Version="1.0.0" /> <PackageReference Include="FunctionalCuid" Version="1.0.0" />
<PackageReference Include="Giraffe" Version="6.0.0" /> <PackageReference Include="Giraffe" Version="6.0.0" />
<PackageReference Include="Giraffe.Htmx" Version="1.9.2" /> <PackageReference Include="Giraffe.Htmx" Version="1.9.6" />
<PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.9.2" /> <PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.9.6" />
<PackageReference Include="LiteDB" Version="5.0.16" /> <PackageReference Include="LiteDB" Version="5.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.5" />
<PackageReference Include="NodaTime" Version="3.1.2" /> <PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.1.2" />
<PackageReference Update="FSharp.Core" Version="7.0.300" /> <PackageReference Include="Npgsql.NodaTime" Version="7.0.6" />
<PackageReference Update="FSharp.Core" Version="7.0.400" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\" /> <Folder Include="wwwroot\" />

View File

@ -20,7 +20,7 @@ module Configure =
.SetBasePath(bldr.Environment.ContentRootPath) .SetBasePath(bldr.Environment.ContentRootPath)
.AddJsonFile("appsettings.json", optional = false, reloadOnChange = true) .AddJsonFile("appsettings.json", optional = false, reloadOnChange = true)
.AddJsonFile($"appsettings.{bldr.Environment.EnvironmentName}.json", optional = true, reloadOnChange = true) .AddJsonFile($"appsettings.{bldr.Environment.EnvironmentName}.json", optional = true, reloadOnChange = true)
.AddEnvironmentVariables () .AddEnvironmentVariables "MPJ_"
|> ignore |> ignore
bldr bldr
@ -53,16 +53,15 @@ module Configure =
open Giraffe open Giraffe
open LiteDB
open Microsoft.AspNetCore.Authentication.Cookies open Microsoft.AspNetCore.Authentication.Cookies
open Microsoft.AspNetCore.Authentication.OpenIdConnect open Microsoft.AspNetCore.Authentication.OpenIdConnect
open Microsoft.AspNetCore.Http open Microsoft.AspNetCore.Http
open Microsoft.Extensions.DependencyInjection open Microsoft.Extensions.DependencyInjection
open Microsoft.IdentityModel.Protocols.OpenIdConnect open Microsoft.IdentityModel.Protocols.OpenIdConnect
open MyPrayerJournal.Data
open NodaTime open NodaTime
open System open System
open System.Text.Json open System.Text.Json
open System.Text.Json.Serialization
open System.Threading.Tasks open System.Threading.Tasks
/// Configure dependency injection /// Configure dependency injection
@ -128,13 +127,9 @@ module Configure =
ctx.ProtocolMessage.RedirectUri <- string bldr ctx.ProtocolMessage.RedirectUri <- string bldr
Task.CompletedTask) Task.CompletedTask)
let jsonOptions = JsonSerializerOptions () let _ = bldr.Services.AddSingleton<JsonSerializerOptions> Json.options
jsonOptions.Converters.Add (JsonFSharpConverter ()) let _ = bldr.Services.AddSingleton<Json.ISerializer> (SystemTextJson.Serializer Json.options)
let db = new LiteDatabase (bldr.Configuration.GetConnectionString "db") let _ = Connection.setUp bldr.Configuration |> Async.AwaitTask |> Async.RunSynchronously
LiteData.Startup.ensureDb db
let _ = bldr.Services.AddSingleton jsonOptions
let _ = bldr.Services.AddSingleton<Json.ISerializer, SystemTextJson.Serializer> ()
let _ = bldr.Services.AddSingleton<LiteDatabase> db
bldr.Build () bldr.Build ()