myPrayerJournal v2 #27
|
@ -2,37 +2,36 @@
|
|||
|
||||
open FSharp.Control.Tasks.V2.ContextInsensitive
|
||||
open Microsoft.FSharpLu
|
||||
|
||||
open Newtonsoft.Json
|
||||
open Raven.Client.Documents
|
||||
open Raven.Client.Documents.Indexes
|
||||
open Raven.Client.Documents.Linq
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open Raven.Client.Documents.Linq
|
||||
open Raven.Client.Documents
|
||||
|
||||
/// JSON converter for request IDs
|
||||
type RequestIdJsonConverter() =
|
||||
inherit JsonConverter<RequestId>()
|
||||
type RequestIdJsonConverter () =
|
||||
inherit JsonConverter<RequestId> ()
|
||||
override __.WriteJson(writer : JsonWriter, value : RequestId, _ : JsonSerializer) =
|
||||
(string >> writer.WriteValue) value
|
||||
(RequestId.toString >> writer.WriteValue) value
|
||||
override __.ReadJson(reader: JsonReader, _ : Type, _ : RequestId, _ : bool, _ : JsonSerializer) =
|
||||
(string >> RequestId.fromIdString) reader.Value
|
||||
|
||||
|
||||
/// JSON converter for user IDs
|
||||
type UserIdJsonConverter() =
|
||||
inherit JsonConverter<UserId>()
|
||||
type UserIdJsonConverter () =
|
||||
inherit JsonConverter<UserId> ()
|
||||
override __.WriteJson(writer : JsonWriter, value : UserId, _ : JsonSerializer) =
|
||||
(string >> writer.WriteValue) value
|
||||
(UserId.toString >> writer.WriteValue) value
|
||||
override __.ReadJson(reader: JsonReader, _ : Type, _ : UserId, _ : bool, _ : JsonSerializer) =
|
||||
(string >> UserId) reader.Value
|
||||
|
||||
|
||||
/// JSON converter for Ticks
|
||||
type TicksJsonConverter() =
|
||||
inherit JsonConverter<Ticks>()
|
||||
type TicksJsonConverter () =
|
||||
inherit JsonConverter<Ticks> ()
|
||||
override __.WriteJson(writer : JsonWriter, value : Ticks, _ : JsonSerializer) =
|
||||
writer.WriteValue (value.toLong ())
|
||||
(Ticks.toLong >> writer.WriteValue) value
|
||||
override __.ReadJson(reader: JsonReader, _ : Type, _ : Ticks, _ : bool, _ : JsonSerializer) =
|
||||
(string >> int64 >> Ticks) reader.Value
|
||||
|
||||
|
@ -72,7 +71,6 @@ module Extensions =
|
|||
open Raven.Client.Documents.Commands.Batches
|
||||
open Raven.Client.Documents.Operations
|
||||
open Raven.Client.Documents.Session
|
||||
open System
|
||||
|
||||
/// Format an RQL query by a strongly-typed index
|
||||
let fromIndex (typ : Type) =
|
||||
|
@ -98,12 +96,12 @@ module Extensions =
|
|||
|
||||
/// Add a history entry
|
||||
member this.AddHistory (reqId : RequestId) (hist : History) =
|
||||
listPush "history" (string reqId) hist
|
||||
listPush "history" (RequestId.toString reqId) hist
|
||||
|> this.Advanced.Defer
|
||||
|
||||
/// Add a note
|
||||
member this.AddNote (reqId : RequestId) (note : Note) =
|
||||
listPush "notes" (string reqId) note
|
||||
listPush "notes" (RequestId.toString reqId) note
|
||||
|> this.Advanced.Defer
|
||||
|
||||
/// Add a request
|
||||
|
@ -114,20 +112,20 @@ module Extensions =
|
|||
// TODO: not right
|
||||
member this.AnsweredRequests (userId : UserId) =
|
||||
sprintf "%s where userId = '%s' and lastStatus = 'Answered' order by asOf as long desc"
|
||||
(fromIndex typeof<Requests_AsJournal>) (string userId)
|
||||
(fromIndex typeof<Requests_AsJournal>) (UserId.toString userId)
|
||||
|> this.Advanced.AsyncRawQuery<JournalRequest>
|
||||
|
||||
/// Retrieve the user's current journal
|
||||
// TODO: probably not right either
|
||||
member this.JournalByUserId (userId : UserId) =
|
||||
sprintf "%s where userId = '%s' and lastStatus <> 'Answered' order by showAfter as long"
|
||||
(fromIndex typeof<Requests_AsJournal>) (string userId)
|
||||
(fromIndex typeof<Requests_AsJournal>) (UserId.toString userId)
|
||||
|> this.Advanced.AsyncRawQuery<JournalRequest>
|
||||
|
||||
/// Retrieve a request, including its history and notes, by its ID and user ID
|
||||
member this.TryFullRequestById (reqId : RequestId) userId =
|
||||
task {
|
||||
let! req = this.LoadAsync (string reqId)
|
||||
let! req = RequestId.toString reqId |> this.LoadAsync
|
||||
match Option.fromObject req with
|
||||
| Some r when r.userId = userId -> return Some r
|
||||
| _ -> return None
|
||||
|
@ -154,7 +152,7 @@ module Extensions =
|
|||
task {
|
||||
let! req =
|
||||
this.Query<Request, Requests_AsJournal>()
|
||||
.Where(fun x -> x.Id = (string reqId) && x.userId = userId)
|
||||
.Where(fun x -> x.Id = (RequestId.toString reqId) && x.userId = userId)
|
||||
.ProjectInto<JournalRequest>()
|
||||
.FirstOrDefaultAsync ()
|
||||
return Option.fromObject req
|
||||
|
@ -166,19 +164,19 @@ module Extensions =
|
|||
r.Script <- "this.recurType = args.Type; this.recurCount = args.Count"
|
||||
r.Values.["Type"] <- string recurType
|
||||
r.Values.["Count"] <- recurCount
|
||||
PatchCommandData (string reqId, null, r, null) |> this.Advanced.Defer
|
||||
PatchCommandData (RequestId.toString reqId, null, r, null) |> this.Advanced.Defer
|
||||
|
||||
/// Update the "show after" timestamp for a request
|
||||
member this.UpdateShowAfter (reqId : RequestId) (showAfter : Ticks) =
|
||||
fieldUpdate "showAfter" (string reqId) (showAfter.toLong ())
|
||||
fieldUpdate "showAfter" (RequestId.toString reqId) (Ticks.toLong showAfter)
|
||||
|> this.Advanced.Defer
|
||||
|
||||
/// Update a snoozed request
|
||||
member this.UpdateSnoozed (reqId : RequestId) (until : Ticks) =
|
||||
let r = PatchRequest()
|
||||
r.Script <- "this.snoozedUntil = args.Item; this.showAfter = args.Item"
|
||||
r.Values.["Item"] <- until.toLong ()
|
||||
PatchCommandData (string reqId, null, r, null) |> this.Advanced.Defer
|
||||
r.Values.["Item"] <- Ticks.toLong until
|
||||
PatchCommandData (RequestId.toString reqId, null, r, null) |> this.Advanced.Defer
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,35 +5,35 @@ module MyPrayerJournal.Domain
|
|||
/// A Collision-resistant Unique IDentifier
|
||||
type Cuid =
|
||||
| Cuid of string
|
||||
with
|
||||
module Cuid =
|
||||
/// The string value of the CUID
|
||||
override x.ToString () = match x with Cuid y -> y
|
||||
let toString x = match x with Cuid y -> y
|
||||
|
||||
|
||||
/// Request ID is a CUID
|
||||
type RequestId =
|
||||
| RequestId of Cuid
|
||||
with
|
||||
module RequestId =
|
||||
/// The string representation of the request ID
|
||||
override x.ToString () = match x with RequestId y -> (string >> sprintf "Requests/%s") y
|
||||
let toString x = match x with RequestId y -> (Cuid.toString >> sprintf "Requests/%s") y
|
||||
/// Create a request ID from a string representation
|
||||
static member fromIdString (y : string) = (Cuid >> RequestId) <| y.Replace("Requests/", "")
|
||||
let fromIdString (y : string) = (Cuid >> RequestId) <| y.Replace("Requests/", "")
|
||||
|
||||
|
||||
/// User ID is a string (the "sub" part of the JWT)
|
||||
type UserId =
|
||||
| UserId of string
|
||||
with
|
||||
module UserId =
|
||||
/// The string representation of the user ID
|
||||
override x.ToString () = match x with UserId y -> y
|
||||
let toString x = match x with UserId y -> y
|
||||
|
||||
|
||||
/// A long integer representing seconds since the epoch
|
||||
type Ticks =
|
||||
| Ticks of int64
|
||||
with
|
||||
module Ticks =
|
||||
/// The int64 (long) representation of ticks
|
||||
member x.toLong () = match x with Ticks y -> y
|
||||
let toLong x = match x with Ticks y -> y
|
||||
|
||||
|
||||
/// How frequently a request should reappear after it is marked "Prayed"
|
||||
|
@ -42,16 +42,16 @@ type Recurrence =
|
|||
| Hours
|
||||
| Days
|
||||
| Weeks
|
||||
with
|
||||
module Recurrence =
|
||||
/// The string reprsentation used in the database and the web app
|
||||
override x.ToString () =
|
||||
let toString x =
|
||||
match x with
|
||||
| Immediate -> "immediate"
|
||||
| Hours -> "hours"
|
||||
| Days -> "days"
|
||||
| Weeks -> "weeks"
|
||||
/// Create a recurrence value from a string
|
||||
static member fromString x =
|
||||
let fromString x =
|
||||
match x with
|
||||
| "immediate" -> Immediate
|
||||
| "hours" -> Hours
|
||||
|
@ -59,7 +59,7 @@ with
|
|||
| "weeks" -> Weeks
|
||||
| _ -> invalidOp (sprintf "%s is not a valid recurrence" x)
|
||||
/// The duration of the recurrence
|
||||
member x.duration =
|
||||
let duration x =
|
||||
match x with
|
||||
| Immediate -> 0L
|
||||
| Hours -> 3600000L
|
||||
|
|
|
@ -44,10 +44,6 @@ module private Helpers =
|
|||
open System.Threading.Tasks
|
||||
open System.Security.Claims
|
||||
|
||||
/// Get the database context from DI
|
||||
// let db (ctx : HttpContext) =
|
||||
// ctx.GetService<AppDbContext> ()
|
||||
|
||||
/// Create a RavenDB session
|
||||
let session (ctx : HttpContext) =
|
||||
ctx.GetService<IDocumentStore>().OpenAsyncSession ()
|
||||
|
@ -62,7 +58,7 @@ module private Helpers =
|
|||
((user >> Option.get) ctx).Value |> UserId
|
||||
|
||||
/// Create a request ID from a string
|
||||
let toReqId = Domain.Cuid >> RequestId
|
||||
let toReqId = Cuid >> RequestId
|
||||
|
||||
/// Return a 201 CREATED response
|
||||
let created next ctx =
|
||||
|
@ -170,7 +166,7 @@ module Request =
|
|||
let now = jsNow ()
|
||||
do! sess.AddRequest
|
||||
{ Request.empty with
|
||||
Id = string reqId
|
||||
Id = RequestId.toString reqId
|
||||
userId = usrId
|
||||
enteredOn = now
|
||||
showAfter = now
|
||||
|
@ -209,7 +205,8 @@ module Request =
|
|||
|> sess.AddHistory reqId
|
||||
match hist.status with
|
||||
| "Prayed" ->
|
||||
(Ticks >> sess.UpdateShowAfter reqId) <| now.toLong () + (req.recurType.duration * int64 req.recurCount)
|
||||
(Ticks.toLong now) + (Recurrence.duration req.recurType * int64 req.recurCount)
|
||||
|> (Ticks >> sess.UpdateShowAfter reqId)
|
||||
| _ -> ()
|
||||
do! sess.SaveChangesAsync ()
|
||||
return! created next ctx
|
||||
|
|
|
@ -14,7 +14,7 @@ module Configure =
|
|||
cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional = true, reloadOnChange = true)
|
||||
.AddJsonFile(sprintf "appsettings.%s.json" ctx.HostingEnvironment.EnvironmentName)
|
||||
.AddEnvironmentVariables()
|
||||
.AddEnvironmentVariables ()
|
||||
|> ignore
|
||||
|
||||
open Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
|
@ -46,7 +46,7 @@ module Configure =
|
|||
|
||||
/// Configure dependency injection
|
||||
let services (sc : IServiceCollection) =
|
||||
use sp = sc.BuildServiceProvider()
|
||||
use sp = sc.BuildServiceProvider ()
|
||||
let cfg = sp.GetRequiredService<IConfiguration> ()
|
||||
sc.AddGiraffe()
|
||||
.AddAuthentication(
|
||||
|
@ -61,7 +61,7 @@ module Configure =
|
|||
opts.Authority <- sprintf "https://%s/" jwtCfg.["Domain"]
|
||||
opts.Audience <- jwtCfg.["Id"])
|
||||
|> ignore
|
||||
sc.AddSingleton<IJsonSerializer>(NewtonsoftJsonSerializer jsonSettings)
|
||||
sc.AddSingleton<IJsonSerializer> (NewtonsoftJsonSerializer jsonSettings)
|
||||
|> ignore
|
||||
let config = sc.BuildServiceProvider().GetRequiredService<IConfiguration>().GetSection "RavenDB"
|
||||
let store = new DocumentStore ()
|
||||
|
|
Loading…
Reference in New Issue
Block a user