Env swap #21

Merged
danieljsummers merged 30 commits from env-swap into help-wanted 2021-08-10 03:23:50 +00:00
4 changed files with 106 additions and 54 deletions
Showing only changes of commit bd8cebbbe2 - Show all commits

View File

@ -12,21 +12,20 @@ open Giraffe.EndpointRouting
let webApp = [
subRoute "/api" [
subRoute "/citizen" [
GET [
GET_HEAD [
routef "/log-on/%s" Handlers.Citizen.logOn
routef "/get/%O" Handlers.Citizen.get
]
DELETE [ route "" Handlers.Citizen.delete ]
]
subRoute "/continent" [
GET [ route "/all" Handlers.Continent.all ]
]
GET_HEAD [ route "/continent/all" Handlers.Continent.all ]
subRoute "/profile" [
GET [
GET_HEAD [
route "" Handlers.Profile.current
route "/count" Handlers.Profile.count
routef "/get/%O" Handlers.Profile.get
]
PATCH [ route "/employment-found" Handlers.Profile.employmentFound ]
POST [ route "/save" Handlers.Profile.save ]
]
]

View File

@ -186,6 +186,49 @@ let withReconn (conn : IConnection) =
| false -> ()))
/// Profile data access functions
[<RequireQualifiedAccess>]
module Profile =
let count conn =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Profile)
.Count()
.RunResultAsync<int64> conn)
/// Find a profile by citizen ID
let findById (citizenId : CitizenId) conn = task {
let! profile =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Profile)
.Get(citizenId)
.RunResultAsync<Profile> conn)
return toOption profile
}
/// Insert or update a profile
let save (profile : Profile) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Profile)
.Get(profile.id)
.Replace(profile)
.RunWriteAsync conn
()
})
/// Delete a citizen's profile
let delete (citizenId : CitizenId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Profile)
.Get(citizenId)
.Delete()
.RunWriteAsync conn
()
})
/// Citizen data access functions
[<RequireQualifiedAccess>]
module Citizen =
@ -233,13 +276,9 @@ module Citizen =
})
/// Delete a citizen
let delete (citizenId : CitizenId) conn =
let delete citizenId conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Profile)
.Get(citizenId)
.Delete()
.RunWriteAsync conn
do! Profile.delete citizenId conn
let! _ =
r.Table(Table.Success)
.GetAll(citizenId).OptArg("index", "citizenId")
@ -276,38 +315,6 @@ module Continent =
.RunResultAsync<Continent list> conn)
/// Profile data access functions
[<RequireQualifiedAccess>]
module Profile =
let count conn =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Profile)
.Count()
.RunResultAsync<int64> conn)
/// Find a profile by citizen ID
let findById (citizenId : CitizenId) conn = task {
let! profile =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Profile)
.Get(citizenId)
.RunResultAsync<Profile> conn)
return toOption profile
}
/// Insert or update a profile
let save (profile : Profile) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Profile)
.Get(profile.id)
.Replace(profile)
.RunWriteAsync conn
()
})
/// Success story data access functions
[<RequireQualifiedAccess>]
module Success =

View File

@ -59,13 +59,12 @@ module Helpers =
/// Get the RethinkDB connection from the request context
let conn (ctx : HttpContext) = ctx.GetService<IConnection> ()
/// Return None if the string is null, empty, or whitespace; otherwise, return Some and the trimmed string
let noneIfEmpty x =
match (defaultArg (Option.ofObj x) "").Trim () with | "" -> None | it -> Some it
/// `None` if a `string option` is `None`, whitespace, or empty
let noneIfBlank (s : string option) =
s |> Option.map (fun x -> match x.Trim () with "" -> None | _ -> Some x) |> Option.flatten
/// Return None if an optional string is None or empty
let noneIfBlank s =
s |> Option.map (fun x -> match x with "" -> None | _ -> Some x) |> Option.flatten
/// `None` if a `string` is null, empty, or whitespace; otherwise, `Some` and the trimmed string
let noneIfEmpty = Option.ofObj >> noneIfBlank
/// Try to get the current user
let tryUser (ctx : HttpContext) =
@ -79,7 +78,10 @@ module Helpers =
/// Get the ID of the currently logged in citizen
// NOTE: if no one is logged in, this will raise an exception
let currentCitizenId ctx = (tryUser >> Option.get >> CitizenId.ofString) ctx
let currentCitizenId = tryUser >> Option.get >> CitizenId.ofString
/// Return an empty OK response
let ok : HttpHandler = Successful.OK ""
@ -143,7 +145,7 @@ module Citizen =
authorize
>=> fun next ctx -> task {
do! Data.Citizen.delete (currentCitizenId ctx) (conn ctx)
return! Successful.OK "" next ctx
return! ok next ctx
}
@ -222,6 +224,26 @@ module Profile =
})
} dbConn
do! Data.Citizen.realNameUpdate citizenId (noneIfBlank (Some form.realName)) dbConn
return! Successful.OK "" next ctx
return! ok next ctx
}
// PATCH: /api/profile/employment-found
let employmentFound : HttpHandler =
authorize
>=> fun next ctx -> task {
let dbConn = conn ctx
match! Data.Profile.findById (currentCitizenId ctx) dbConn with
| Some profile ->
do! Data.Profile.save { profile with seekingEmployment = false } dbConn
return! ok next ctx
| None -> return! Error.notFound next ctx
}
// DELETE: /api/profile
let delete : HttpHandler =
authorize
>=> fun next ctx -> task {
do! Data.Profile.delete (currentCitizenId ctx) (conn ctx)
return! ok next ctx
}

View File

@ -78,3 +78,27 @@ module ProfileForm =
notes = s.notes
})
}
/// The various ways profiles can be searched
type ProfileSearch = {
/// Retrieve citizens from this continent
continentId : string option
/// Text for a search within a citizen's skills
skill : string option
/// Text for a search with a citizen's professional biography and experience fields
bioExperience : string option
/// Whether to retrieve citizens who do or do not want remote work
remoteWork : string
}
/// Support functions for profile searches
module ProfileSearch =
/// Is the search empty?
let isEmptySearch search =
[ search.continentId
search.skill
search.bioExperience
match search.remoteWork with "" -> Some search.remoteWork | _ -> None
]
|> List.exists Option.isSome