Version 3 #67
@ -79,7 +79,6 @@ module Indexes =
|
||||
/// All data manipulations within myPrayerJournal
|
||||
module Data =
|
||||
|
||||
open FSharp.Control.Tasks.V2.ContextInsensitive
|
||||
open Indexes
|
||||
open Microsoft.FSharpLu
|
||||
open Raven.Client.Documents
|
||||
|
@ -2,6 +2,8 @@
|
||||
/// The data model for myPrayerJournal
|
||||
module MyPrayerJournal.Domain
|
||||
|
||||
// fsharplint:disable RecordFieldNames
|
||||
|
||||
open Cuid
|
||||
|
||||
/// Request ID is a CUID
|
||||
@ -9,9 +11,9 @@ type RequestId =
|
||||
| RequestId of Cuid
|
||||
module RequestId =
|
||||
/// The string representation of the request ID
|
||||
let toString x = match x with RequestId y -> (Cuid.toString >> sprintf "Requests/%s") y
|
||||
let toString = function RequestId x -> $"Requests/{Cuid.toString x}"
|
||||
/// Create a request ID from a string representation
|
||||
let fromIdString (y : string) = (Cuid >> RequestId) <| y.Replace("Requests/", "")
|
||||
let fromIdString (x : string) = x.Replace ("Requests/", "") |> (Cuid >> RequestId)
|
||||
|
||||
|
||||
/// User ID is a string (the "sub" part of the JWT)
|
||||
@ -19,7 +21,7 @@ type UserId =
|
||||
| UserId of string
|
||||
module UserId =
|
||||
/// The string representation of the user ID
|
||||
let toString x = match x with UserId y -> y
|
||||
let toString = function UserId x -> x
|
||||
|
||||
|
||||
/// A long integer representing seconds since the epoch
|
||||
@ -27,7 +29,7 @@ type Ticks =
|
||||
| Ticks of int64
|
||||
module Ticks =
|
||||
/// The int64 (long) representation of ticks
|
||||
let toLong x = match x with Ticks y -> y
|
||||
let toLong = function Ticks x -> x
|
||||
|
||||
|
||||
/// How frequently a request should reappear after it is marked "Prayed"
|
||||
@ -38,16 +40,16 @@ type Recurrence =
|
||||
| Weeks
|
||||
module Recurrence =
|
||||
/// Create a recurrence value from a string
|
||||
let fromString x =
|
||||
match x with
|
||||
let fromString =
|
||||
function
|
||||
| "Immediate" -> Immediate
|
||||
| "Hours" -> Hours
|
||||
| "Days" -> Days
|
||||
| "Weeks" -> Weeks
|
||||
| _ -> invalidOp (sprintf "%s is not a valid recurrence" x)
|
||||
| it -> invalidOp $"{it} is not a valid recurrence"
|
||||
/// The duration of the recurrence
|
||||
let duration x =
|
||||
match x with
|
||||
let duration =
|
||||
function
|
||||
| Immediate -> 0L
|
||||
| Hours -> 3600000L
|
||||
| Days -> 86400000L
|
||||
@ -62,13 +64,13 @@ type RequestAction =
|
||||
| Answered
|
||||
module RequestAction =
|
||||
/// Create a RequestAction from a string
|
||||
let fromString x =
|
||||
match x with
|
||||
let fromString =
|
||||
function
|
||||
| "Created" -> Created
|
||||
| "Prayed" -> Prayed
|
||||
| "Updated" -> Updated
|
||||
| "Answered" -> Answered
|
||||
| _ -> (sprintf "Bad request action %s" >> invalidOp) x
|
||||
| it -> invalidOp $"Bad request action {it}"
|
||||
|
||||
|
||||
/// History is a record of action taken on a prayer request, including updates to its text
|
||||
|
@ -2,6 +2,8 @@
|
||||
[<RequireQualifiedAccess>]
|
||||
module MyPrayerJournal.Handlers
|
||||
|
||||
// fsharplint:disable RecordFieldNames
|
||||
|
||||
open Giraffe
|
||||
|
||||
/// Handler to return Vue files
|
||||
@ -60,11 +62,10 @@ module private Helpers =
|
||||
|
||||
/// Create a request ID from a string
|
||||
let toReqId x =
|
||||
let reqId =
|
||||
match Cuid.ofString x with
|
||||
| Ok cuid -> cuid
|
||||
| Error msg -> invalidOp msg
|
||||
RequestId reqId
|
||||
match Cuid.ofString x with
|
||||
| Ok cuid -> cuid
|
||||
| Error msg -> invalidOp msg
|
||||
|> RequestId
|
||||
|
||||
/// Return a 201 CREATED response
|
||||
let created next ctx =
|
||||
@ -88,12 +89,12 @@ module private Helpers =
|
||||
|
||||
/// Work-around to let the Json.NET serializer synchronously deserialize from the request stream
|
||||
// TODO: Remove this once there is an async serializer
|
||||
let allowSyncIO : HttpHandler =
|
||||
fun next ctx ->
|
||||
match ctx.Features.Get<Features.IHttpBodyControlFeature>() with
|
||||
| null -> ()
|
||||
| f -> f.AllowSynchronousIO <- true
|
||||
next ctx
|
||||
// let allowSyncIO : HttpHandler =
|
||||
// fun next ctx ->
|
||||
// match ctx.Features.Get<Features.IHttpBodyControlFeature>() with
|
||||
// | null -> ()
|
||||
// | f -> f.AllowSynchronousIO <- true
|
||||
// next ctx
|
||||
|
||||
|
||||
/// Strongly-typed models for post requests
|
||||
@ -142,8 +143,6 @@ module Models =
|
||||
until : int64
|
||||
}
|
||||
|
||||
open FSharp.Control.Tasks.V2.ContextInsensitive
|
||||
|
||||
/// /api/journal URLs
|
||||
module Journal =
|
||||
|
||||
@ -165,7 +164,7 @@ module Request =
|
||||
/// POST /api/request
|
||||
let add : HttpHandler =
|
||||
authorize
|
||||
>=> allowSyncIO
|
||||
// >=> allowSyncIO
|
||||
>=> fun next ctx ->
|
||||
task {
|
||||
let! r = ctx.BindJsonAsync<Models.Request> ()
|
||||
@ -197,7 +196,7 @@ module Request =
|
||||
/// POST /api/request/[req-id]/history
|
||||
let addHistory requestId : HttpHandler =
|
||||
authorize
|
||||
>=> allowSyncIO
|
||||
// >=> allowSyncIO
|
||||
>=> fun next ctx ->
|
||||
task {
|
||||
use sess = session ctx
|
||||
@ -229,7 +228,7 @@ module Request =
|
||||
/// POST /api/request/[req-id]/note
|
||||
let addNote requestId : HttpHandler =
|
||||
authorize
|
||||
>=> allowSyncIO
|
||||
// >=> allowSyncIO
|
||||
>=> fun next ctx ->
|
||||
task {
|
||||
use sess = session ctx
|
||||
@ -309,7 +308,7 @@ module Request =
|
||||
/// PATCH /api/request/[req-id]/snooze
|
||||
let snooze requestId : HttpHandler =
|
||||
authorize
|
||||
>=> allowSyncIO
|
||||
// >=> allowSyncIO
|
||||
>=> fun next ctx ->
|
||||
task {
|
||||
use sess = session ctx
|
||||
@ -327,7 +326,7 @@ module Request =
|
||||
/// PATCH /api/request/[req-id]/recurrence
|
||||
let updateRecurrence requestId : HttpHandler =
|
||||
authorize
|
||||
>=> allowSyncIO
|
||||
// >=> allowSyncIO
|
||||
>=> fun next ctx ->
|
||||
task {
|
||||
use sess = session ctx
|
||||
@ -344,12 +343,11 @@ module Request =
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
||||
open Giraffe.TokenRouter
|
||||
open Giraffe.EndpointRouting
|
||||
|
||||
/// The routes for myPrayerJournal
|
||||
let webApp : HttpHandler =
|
||||
router Error.notFound [
|
||||
route "/" Vue.app
|
||||
let routes =
|
||||
[ route "/" Vue.app
|
||||
subRoute "/api/" [
|
||||
GET [
|
||||
route "journal" Journal.journal
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.0.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -14,17 +14,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FunctionalCuid" Version="1.0.0" />
|
||||
<PackageReference Include="Giraffe" Version="4.1.0" />
|
||||
<PackageReference Include="Giraffe.TokenRouter" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.FSharpLu" Version="0.11.6" />
|
||||
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.6" />
|
||||
<PackageReference Include="Giraffe" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.10" />
|
||||
<PackageReference Include="Microsoft.FSharpLu" Version="0.11.7" />
|
||||
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />
|
||||
<PackageReference Include="RavenDb.Client" Version="4.2.102" />
|
||||
<PackageReference Include="TaskBuilder.fs" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="FSharp.Core" Version="4.7.1" />
|
||||
<PackageReference Include="RethinkDb.Driver" Version="2.3.150" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -36,7 +36,6 @@ module Configure =
|
||||
(Path.Combine >> bldr.UseWebRoot) pathSegments
|
||||
|
||||
open Giraffe
|
||||
open Giraffe.Serialization
|
||||
open Microsoft.AspNetCore.Authentication.JwtBearer
|
||||
open Microsoft.Extensions.DependencyInjection
|
||||
open MyPrayerJournal.Indexes
|
||||
@ -51,7 +50,7 @@ module Configure =
|
||||
let svcs (sc : IServiceCollection) =
|
||||
/// Custom settings for the JSON serializer (uses compact representation for options and DUs)
|
||||
let jsonSettings =
|
||||
let x = NewtonsoftJsonSerializer.DefaultSettings
|
||||
let x = NewtonsoftJson.Serializer.DefaultSettings
|
||||
Converters.all |> List.ofSeq |> List.iter x.Converters.Add
|
||||
x.NullValueHandling <- NullValueHandling.Ignore
|
||||
x.MissingMemberHandling <- MissingMemberHandling.Error
|
||||
@ -61,7 +60,8 @@ module Configure =
|
||||
|
||||
use sp = sc.BuildServiceProvider ()
|
||||
let cfg = sp.GetRequiredService<IConfiguration> ()
|
||||
sc.AddGiraffe()
|
||||
sc.AddRouting()
|
||||
.AddGiraffe()
|
||||
.AddAuthentication(
|
||||
/// Use HTTP "Bearer" authentication with JWTs
|
||||
fun opts ->
|
||||
@ -75,7 +75,7 @@ module Configure =
|
||||
opts.Audience <- jwtCfg.["Id"]
|
||||
)
|
||||
|> ignore
|
||||
sc.AddSingleton<IJsonSerializer> (NewtonsoftJsonSerializer jsonSettings)
|
||||
sc.AddSingleton<Json.ISerializer> (NewtonsoftJson.Serializer jsonSettings)
|
||||
|> ignore
|
||||
let config = sc.BuildServiceProvider().GetRequiredService<IConfiguration>().GetSection "RavenDB"
|
||||
let store = new DocumentStore ()
|
||||
@ -104,6 +104,7 @@ module Configure =
|
||||
bldr.ConfigureLogging logz
|
||||
|
||||
open System
|
||||
open Giraffe.EndpointRouting
|
||||
|
||||
/// Configure the web application
|
||||
let application (bldr : IWebHostBuilder) =
|
||||
@ -118,7 +119,8 @@ module Configure =
|
||||
| a ->
|
||||
a.UseAuthentication()
|
||||
.UseStaticFiles()
|
||||
.UseGiraffe Handlers.webApp
|
||||
.UseRouting()
|
||||
.UseEndpoints (fun e -> e.MapGiraffeEndpoints Handlers.routes)
|
||||
|> ignore)
|
||||
bldr.Configure appConfig
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user