Version 3 #67

Merged
danieljsummers merged 53 commits from version-3 into master 2021-10-26 23:39:59 +00:00
5 changed files with 47 additions and 51 deletions
Showing only changes of commit c1f6054e15 - Show all commits

View File

@ -79,7 +79,6 @@ module Indexes =
/// All data manipulations within myPrayerJournal /// All data manipulations within myPrayerJournal
module Data = module Data =
open FSharp.Control.Tasks.V2.ContextInsensitive
open Indexes open Indexes
open Microsoft.FSharpLu open Microsoft.FSharpLu
open Raven.Client.Documents open Raven.Client.Documents

View File

@ -2,6 +2,8 @@
/// The data model for myPrayerJournal /// The data model for myPrayerJournal
module MyPrayerJournal.Domain module MyPrayerJournal.Domain
// fsharplint:disable RecordFieldNames
open Cuid open Cuid
/// Request ID is a CUID /// Request ID is a CUID
@ -9,9 +11,9 @@ type RequestId =
| RequestId of Cuid | RequestId of Cuid
module RequestId = module RequestId =
/// The string representation of the request ID /// 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 /// 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) /// User ID is a string (the "sub" part of the JWT)
@ -19,7 +21,7 @@ type UserId =
| UserId of string | UserId of string
module UserId = module UserId =
/// The string representation of the user ID /// 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 /// A long integer representing seconds since the epoch
@ -27,7 +29,7 @@ type Ticks =
| Ticks of int64 | Ticks of int64
module Ticks = module Ticks =
/// The int64 (long) representation of 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" /// How frequently a request should reappear after it is marked "Prayed"
@ -38,16 +40,16 @@ type Recurrence =
| Weeks | Weeks
module Recurrence = module Recurrence =
/// Create a recurrence value from a string /// Create a recurrence value from a string
let fromString x = let fromString =
match x with function
| "Immediate" -> Immediate | "Immediate" -> Immediate
| "Hours" -> Hours | "Hours" -> Hours
| "Days" -> Days | "Days" -> Days
| "Weeks" -> Weeks | "Weeks" -> Weeks
| _ -> invalidOp (sprintf "%s is not a valid recurrence" x) | it -> invalidOp $"{it} is not a valid recurrence"
/// The duration of the recurrence /// The duration of the recurrence
let duration x = let duration =
match x with function
| Immediate -> 0L | Immediate -> 0L
| Hours -> 3600000L | Hours -> 3600000L
| Days -> 86400000L | Days -> 86400000L
@ -62,13 +64,13 @@ type RequestAction =
| Answered | Answered
module RequestAction = module RequestAction =
/// Create a RequestAction from a string /// Create a RequestAction from a string
let fromString x = let fromString =
match x with function
| "Created" -> Created | "Created" -> Created
| "Prayed" -> Prayed | "Prayed" -> Prayed
| "Updated" -> Updated | "Updated" -> Updated
| "Answered" -> Answered | "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 /// History is a record of action taken on a prayer request, including updates to its text

View File

@ -2,6 +2,8 @@
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module MyPrayerJournal.Handlers module MyPrayerJournal.Handlers
// fsharplint:disable RecordFieldNames
open Giraffe open Giraffe
/// Handler to return Vue files /// Handler to return Vue files
@ -60,11 +62,10 @@ module private Helpers =
/// Create a request ID from a string /// Create a request ID from a string
let toReqId x = let toReqId x =
let reqId = match Cuid.ofString x with
match Cuid.ofString x with | Ok cuid -> cuid
| Ok cuid -> cuid | Error msg -> invalidOp msg
| Error msg -> invalidOp msg |> RequestId
RequestId reqId
/// Return a 201 CREATED response /// Return a 201 CREATED response
let created next ctx = 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 /// Work-around to let the Json.NET serializer synchronously deserialize from the request stream
// TODO: Remove this once there is an async serializer // TODO: Remove this once there is an async serializer
let allowSyncIO : HttpHandler = // let allowSyncIO : HttpHandler =
fun next ctx -> // fun next ctx ->
match ctx.Features.Get<Features.IHttpBodyControlFeature>() with // match ctx.Features.Get<Features.IHttpBodyControlFeature>() with
| null -> () // | null -> ()
| f -> f.AllowSynchronousIO <- true // | f -> f.AllowSynchronousIO <- true
next ctx // next ctx
/// Strongly-typed models for post requests /// Strongly-typed models for post requests
@ -142,8 +143,6 @@ module Models =
until : int64 until : int64
} }
open FSharp.Control.Tasks.V2.ContextInsensitive
/// /api/journal URLs /// /api/journal URLs
module Journal = module Journal =
@ -165,7 +164,7 @@ module Request =
/// POST /api/request /// POST /api/request
let add : HttpHandler = let add : HttpHandler =
authorize authorize
>=> allowSyncIO // >=> allowSyncIO
>=> fun next ctx -> >=> fun next ctx ->
task { task {
let! r = ctx.BindJsonAsync<Models.Request> () let! r = ctx.BindJsonAsync<Models.Request> ()
@ -197,7 +196,7 @@ module Request =
/// POST /api/request/[req-id]/history /// POST /api/request/[req-id]/history
let addHistory requestId : HttpHandler = let addHistory requestId : HttpHandler =
authorize authorize
>=> allowSyncIO // >=> allowSyncIO
>=> fun next ctx -> >=> fun next ctx ->
task { task {
use sess = session ctx use sess = session ctx
@ -229,7 +228,7 @@ module Request =
/// POST /api/request/[req-id]/note /// POST /api/request/[req-id]/note
let addNote requestId : HttpHandler = let addNote requestId : HttpHandler =
authorize authorize
>=> allowSyncIO // >=> allowSyncIO
>=> fun next ctx -> >=> fun next ctx ->
task { task {
use sess = session ctx use sess = session ctx
@ -309,7 +308,7 @@ module Request =
/// PATCH /api/request/[req-id]/snooze /// PATCH /api/request/[req-id]/snooze
let snooze requestId : HttpHandler = let snooze requestId : HttpHandler =
authorize authorize
>=> allowSyncIO // >=> allowSyncIO
>=> fun next ctx -> >=> fun next ctx ->
task { task {
use sess = session ctx use sess = session ctx
@ -327,7 +326,7 @@ module Request =
/// PATCH /api/request/[req-id]/recurrence /// PATCH /api/request/[req-id]/recurrence
let updateRecurrence requestId : HttpHandler = let updateRecurrence requestId : HttpHandler =
authorize authorize
>=> allowSyncIO // >=> allowSyncIO
>=> fun next ctx -> >=> fun next ctx ->
task { task {
use sess = session ctx use sess = session ctx
@ -344,12 +343,11 @@ module Request =
| None -> return! Error.notFound next ctx | None -> return! Error.notFound next ctx
} }
open Giraffe.TokenRouter open Giraffe.EndpointRouting
/// The routes for myPrayerJournal /// The routes for myPrayerJournal
let webApp : HttpHandler = let routes =
router Error.notFound [ [ route "/" Vue.app
route "/" Vue.app
subRoute "/api/" [ subRoute "/api/" [
GET [ GET [
route "journal" Journal.journal route "journal" Journal.journal

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.0.0.0</Version> <Version>3.0.0.0</Version>
</PropertyGroup> </PropertyGroup>
@ -14,17 +14,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FunctionalCuid" Version="1.0.0" /> <PackageReference Include="FunctionalCuid" Version="1.0.0" />
<PackageReference Include="Giraffe" Version="4.1.0" /> <PackageReference Include="Giraffe" Version="5.0.0" />
<PackageReference Include="Giraffe.TokenRouter" Version="1.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.3" /> <PackageReference Include="Microsoft.FSharpLu" Version="0.11.7" />
<PackageReference Include="Microsoft.FSharpLu" Version="0.11.6" /> <PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.6" />
<PackageReference Include="RavenDb.Client" Version="4.2.102" /> <PackageReference Include="RavenDb.Client" Version="4.2.102" />
<PackageReference Include="TaskBuilder.fs" Version="2.1.0" /> <PackageReference Include="RethinkDb.Driver" Version="2.3.150" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -36,7 +36,6 @@ module Configure =
(Path.Combine >> bldr.UseWebRoot) pathSegments (Path.Combine >> bldr.UseWebRoot) pathSegments
open Giraffe open Giraffe
open Giraffe.Serialization
open Microsoft.AspNetCore.Authentication.JwtBearer open Microsoft.AspNetCore.Authentication.JwtBearer
open Microsoft.Extensions.DependencyInjection open Microsoft.Extensions.DependencyInjection
open MyPrayerJournal.Indexes open MyPrayerJournal.Indexes
@ -51,7 +50,7 @@ module Configure =
let svcs (sc : IServiceCollection) = let svcs (sc : IServiceCollection) =
/// Custom settings for the JSON serializer (uses compact representation for options and DUs) /// Custom settings for the JSON serializer (uses compact representation for options and DUs)
let jsonSettings = let jsonSettings =
let x = NewtonsoftJsonSerializer.DefaultSettings let x = NewtonsoftJson.Serializer.DefaultSettings
Converters.all |> List.ofSeq |> List.iter x.Converters.Add Converters.all |> List.ofSeq |> List.iter x.Converters.Add
x.NullValueHandling <- NullValueHandling.Ignore x.NullValueHandling <- NullValueHandling.Ignore
x.MissingMemberHandling <- MissingMemberHandling.Error x.MissingMemberHandling <- MissingMemberHandling.Error
@ -61,7 +60,8 @@ module Configure =
use sp = sc.BuildServiceProvider () use sp = sc.BuildServiceProvider ()
let cfg = sp.GetRequiredService<IConfiguration> () let cfg = sp.GetRequiredService<IConfiguration> ()
sc.AddGiraffe() sc.AddRouting()
.AddGiraffe()
.AddAuthentication( .AddAuthentication(
/// Use HTTP "Bearer" authentication with JWTs /// Use HTTP "Bearer" authentication with JWTs
fun opts -> fun opts ->
@ -75,7 +75,7 @@ module Configure =
opts.Audience <- jwtCfg.["Id"] opts.Audience <- jwtCfg.["Id"]
) )
|> ignore |> ignore
sc.AddSingleton<IJsonSerializer> (NewtonsoftJsonSerializer jsonSettings) sc.AddSingleton<Json.ISerializer> (NewtonsoftJson.Serializer jsonSettings)
|> ignore |> ignore
let config = sc.BuildServiceProvider().GetRequiredService<IConfiguration>().GetSection "RavenDB" let config = sc.BuildServiceProvider().GetRequiredService<IConfiguration>().GetSection "RavenDB"
let store = new DocumentStore () let store = new DocumentStore ()
@ -104,6 +104,7 @@ module Configure =
bldr.ConfigureLogging logz bldr.ConfigureLogging logz
open System open System
open Giraffe.EndpointRouting
/// Configure the web application /// Configure the web application
let application (bldr : IWebHostBuilder) = let application (bldr : IWebHostBuilder) =
@ -118,7 +119,8 @@ module Configure =
| a -> | a ->
a.UseAuthentication() a.UseAuthentication()
.UseStaticFiles() .UseStaticFiles()
.UseGiraffe Handlers.webApp .UseRouting()
.UseEndpoints (fun e -> e.MapGiraffeEndpoints Handlers.routes)
|> ignore) |> ignore)
bldr.Configure appConfig bldr.Configure appConfig