Env swap #21
|
@ -12,21 +12,20 @@ open Giraffe.EndpointRouting
|
||||||
let webApp = [
|
let webApp = [
|
||||||
subRoute "/api" [
|
subRoute "/api" [
|
||||||
subRoute "/citizen" [
|
subRoute "/citizen" [
|
||||||
GET [
|
GET_HEAD [
|
||||||
routef "/log-on/%s" Handlers.Citizen.logOn
|
routef "/log-on/%s" Handlers.Citizen.logOn
|
||||||
routef "/get/%O" Handlers.Citizen.get
|
routef "/get/%O" Handlers.Citizen.get
|
||||||
]
|
]
|
||||||
DELETE [ route "" Handlers.Citizen.delete ]
|
DELETE [ route "" Handlers.Citizen.delete ]
|
||||||
]
|
]
|
||||||
subRoute "/continent" [
|
GET_HEAD [ route "/continent/all" Handlers.Continent.all ]
|
||||||
GET [ route "/all" Handlers.Continent.all ]
|
|
||||||
]
|
|
||||||
subRoute "/profile" [
|
subRoute "/profile" [
|
||||||
GET [
|
GET_HEAD [
|
||||||
route "" Handlers.Profile.current
|
route "" Handlers.Profile.current
|
||||||
route "/count" Handlers.Profile.count
|
route "/count" Handlers.Profile.count
|
||||||
routef "/get/%O" Handlers.Profile.get
|
routef "/get/%O" Handlers.Profile.get
|
||||||
]
|
]
|
||||||
|
PATCH [ route "/employment-found" Handlers.Profile.employmentFound ]
|
||||||
POST [ route "/save" Handlers.Profile.save ]
|
POST [ route "/save" Handlers.Profile.save ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
@ -186,6 +186,49 @@ let withReconn (conn : IConnection) =
|
||||||
| false -> ()))
|
| 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
|
/// Citizen data access functions
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Citizen =
|
module Citizen =
|
||||||
|
@ -233,13 +276,9 @@ module Citizen =
|
||||||
})
|
})
|
||||||
|
|
||||||
/// Delete a citizen
|
/// Delete a citizen
|
||||||
let delete (citizenId : CitizenId) conn =
|
let delete citizenId conn =
|
||||||
withReconn(conn).ExecuteAsync(fun () -> task {
|
withReconn(conn).ExecuteAsync(fun () -> task {
|
||||||
let! _ =
|
do! Profile.delete citizenId conn
|
||||||
r.Table(Table.Profile)
|
|
||||||
.Get(citizenId)
|
|
||||||
.Delete()
|
|
||||||
.RunWriteAsync conn
|
|
||||||
let! _ =
|
let! _ =
|
||||||
r.Table(Table.Success)
|
r.Table(Table.Success)
|
||||||
.GetAll(citizenId).OptArg("index", "citizenId")
|
.GetAll(citizenId).OptArg("index", "citizenId")
|
||||||
|
@ -276,38 +315,6 @@ module Continent =
|
||||||
.RunResultAsync<Continent list> conn)
|
.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
|
/// Success story data access functions
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Success =
|
module Success =
|
||||||
|
|
|
@ -59,13 +59,12 @@ module Helpers =
|
||||||
/// Get the RethinkDB connection from the request context
|
/// Get the RethinkDB connection from the request context
|
||||||
let conn (ctx : HttpContext) = ctx.GetService<IConnection> ()
|
let conn (ctx : HttpContext) = ctx.GetService<IConnection> ()
|
||||||
|
|
||||||
/// Return None if the string is null, empty, or whitespace; otherwise, return Some and the trimmed string
|
/// `None` if a `string option` is `None`, whitespace, or empty
|
||||||
let noneIfEmpty x =
|
let noneIfBlank (s : string option) =
|
||||||
match (defaultArg (Option.ofObj x) "").Trim () with | "" -> None | it -> Some it
|
s |> Option.map (fun x -> match x.Trim () with "" -> None | _ -> Some x) |> Option.flatten
|
||||||
|
|
||||||
/// Return None if an optional string is None or empty
|
/// `None` if a `string` is null, empty, or whitespace; otherwise, `Some` and the trimmed string
|
||||||
let noneIfBlank s =
|
let noneIfEmpty = Option.ofObj >> noneIfBlank
|
||||||
s |> Option.map (fun x -> match x with "" -> None | _ -> Some x) |> Option.flatten
|
|
||||||
|
|
||||||
/// Try to get the current user
|
/// Try to get the current user
|
||||||
let tryUser (ctx : HttpContext) =
|
let tryUser (ctx : HttpContext) =
|
||||||
|
@ -79,7 +78,10 @@ module Helpers =
|
||||||
|
|
||||||
/// Get the ID of the currently logged in citizen
|
/// Get the ID of the currently logged in citizen
|
||||||
// NOTE: if no one is logged in, this will raise an exception
|
// 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
|
authorize
|
||||||
>=> fun next ctx -> task {
|
>=> fun next ctx -> task {
|
||||||
do! Data.Citizen.delete (currentCitizenId ctx) (conn ctx)
|
do! Data.Citizen.delete (currentCitizenId ctx) (conn ctx)
|
||||||
return! Successful.OK "" next ctx
|
return! ok next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -189,7 +191,7 @@ module Profile =
|
||||||
>=> fun next ctx -> task {
|
>=> fun next ctx -> task {
|
||||||
let! theCount = Data.Profile.count (conn ctx)
|
let! theCount = Data.Profile.count (conn ctx)
|
||||||
return! json { count = theCount } next ctx
|
return! json { count = theCount } next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST: /api/profile/save
|
// POST: /api/profile/save
|
||||||
let save : HttpHandler =
|
let save : HttpHandler =
|
||||||
|
@ -222,6 +224,26 @@ module Profile =
|
||||||
})
|
})
|
||||||
} dbConn
|
} dbConn
|
||||||
do! Data.Citizen.realNameUpdate citizenId (noneIfBlank (Some form.realName)) 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
|
||||||
|
}
|
||||||
|
|
|
@ -78,3 +78,27 @@ module ProfileForm =
|
||||||
notes = s.notes
|
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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user