Env swap #21
|
@ -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 ]
|
||||
]
|
||||
]
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,7 +191,7 @@ module Profile =
|
|||
>=> fun next ctx -> task {
|
||||
let! theCount = Data.Profile.count (conn ctx)
|
||||
return! json { count = theCount } next ctx
|
||||
}
|
||||
}
|
||||
|
||||
// POST: /api/profile/save
|
||||
let save : HttpHandler =
|
||||
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user