Combined all F# code into one project

Less to migrate, less to maintain, and I'll never swap these out as
components; might as well get the ease of managing them all in one
project.
This commit is contained in:
Daniel J. Summers
2016-12-04 22:05:50 -06:00
parent f5b65d320e
commit 33dccf5822
43 changed files with 249 additions and 328 deletions

View File

@@ -0,0 +1,140 @@
module MyWebLog.Data.RethinkDB.Category
open MyWebLog.Entities
open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Get all categories for a web log
let getAllCategories conn (webLogId : string) =
async {
return! r.Table(Table.Category)
.GetAll(webLogId).OptArg("index", "WebLogId")
.OrderBy("Name")
.RunResultAsync<Category list> conn
}
|> Async.RunSynchronously
/// Get a specific category by its Id
let tryFindCategory conn webLogId catId : Category option =
async {
let! c =
r.Table(Table.Category)
.Get(catId)
.RunResultAsync<Category> conn
return
match box c with
| null -> None
| catt ->
let cat : Category = unbox catt
match cat.WebLogId = webLogId with true -> Some cat | _ -> None
}
|> Async.RunSynchronously
/// Add a category
let addCategory conn (cat : Category) =
async {
do! r.Table(Table.Category)
.Insert(cat)
.RunResultAsync conn
}
|> Async.RunSynchronously
type CategoryUpdateRecord =
{ Name : string
Slug : string
Description : string option
ParentId : string option
}
/// Update a category
let updateCategory conn (cat : Category) =
match tryFindCategory conn cat.WebLogId cat.Id with
| Some _ ->
async {
do! r.Table(Table.Category)
.Get(cat.Id)
.Update(
{ CategoryUpdateRecord.Name = cat.Name
Slug = cat.Slug
Description = cat.Description
ParentId = cat.ParentId
})
.RunResultAsync conn
}
|> Async.RunSynchronously
| _ -> ()
/// Update a category's children
let updateChildren conn webLogId parentId (children : string list) =
match tryFindCategory conn webLogId parentId with
| Some _ ->
async {
do! r.Table(Table.Category)
.Get(parentId)
.Update(dict [ "Children", children ])
.RunResultAsync conn
}
|> Async.RunSynchronously
| _ -> ()
/// Delete a category
let deleteCategory conn (cat : Category) =
async {
// Remove the category from its parent
match cat.ParentId with
| Some parentId ->
match tryFindCategory conn cat.WebLogId parentId with
| Some parent -> parent.Children
|> List.filter (fun childId -> childId <> cat.Id)
|> updateChildren conn cat.WebLogId parentId
| _ -> ()
| _ -> ()
// Move this category's children to its parent
cat.Children
|> List.map (fun childId ->
match tryFindCategory conn cat.WebLogId childId with
| Some _ ->
async {
do! r.Table(Table.Category)
.Get(childId)
.Update(dict [ "ParentId", cat.ParentId ])
.RunResultAsync conn
}
|> Some
| _ -> None)
|> List.filter Option.isSome
|> List.map Option.get
|> List.iter Async.RunSynchronously
// Remove the category from posts where it is assigned
let! posts =
r.Table(Table.Post)
.GetAll(cat.WebLogId).OptArg("index", "WebLogId")
.Filter(ReqlFunction1 (fun p -> upcast p.["CategoryIds"].Contains cat.Id))
.RunResultAsync<Post list> conn
|> Async.AwaitTask
posts
|> List.map (fun post ->
async {
do! r.Table(Table.Post)
.Get(post.Id)
.Update(dict [ "CategoryIds", post.CategoryIds |> List.filter (fun c -> c <> cat.Id) ])
.RunResultAsync conn
})
|> List.iter Async.RunSynchronously
// Now, delete the category
do! r.Table(Table.Category)
.Get(cat.Id)
.Delete()
.RunResultAsync conn
}
|> Async.RunSynchronously
/// Get a category by its slug
let tryFindCategoryBySlug conn (webLogId : string) (slug : string) =
async {
let! cat = r.Table(Table.Category)
.GetAll(r.Array (webLogId, slug)).OptArg("index", "Slug")
.RunResultAsync<Category list> conn
return cat |> List.tryHead
}
|> Async.RunSynchronously

View File

@@ -0,0 +1,43 @@
namespace MyWebLog.Data.RethinkDB
open RethinkDb.Driver
open RethinkDb.Driver.Net
open Newtonsoft.Json
/// Data configuration
type DataConfig =
{ /// The hostname for the RethinkDB server
[<JsonProperty("hostname")>]
Hostname : string
/// The port for the RethinkDB server
[<JsonProperty("port")>]
Port : int
/// The authorization key to use when connecting to the server
[<JsonProperty("authKey")>]
AuthKey : string
/// How long an attempt to connect to the server should wait before giving up
[<JsonProperty("timeout")>]
Timeout : int
/// The name of the default database to use on the connection
[<JsonProperty("database")>]
Database : string
/// A connection to the RethinkDB server using the configuration in this object
[<JsonIgnore>]
Conn : IConnection }
with
/// Use RethinkDB defaults for non-provided options, and connect to the server
static member Connect config =
let host cfg = match cfg.Hostname with null -> { cfg with Hostname = RethinkDBConstants.DefaultHostname } | _ -> cfg
let port cfg = match cfg.Port with 0 -> { cfg with Port = RethinkDBConstants.DefaultPort } | _ -> cfg
let auth cfg = match cfg.AuthKey with null -> { cfg with AuthKey = RethinkDBConstants.DefaultAuthkey } | _ -> cfg
let timeout cfg = match cfg.Timeout with 0 -> { cfg with Timeout = RethinkDBConstants.DefaultTimeout } | _ -> cfg
let db cfg = match cfg.Database with null -> { cfg with Database = RethinkDBConstants.DefaultDbName } | _ -> cfg
let connect cfg =
{ cfg with Conn = RethinkDB.R.Connection()
.Hostname(cfg.Hostname)
.Port(cfg.Port)
.AuthKey(cfg.AuthKey)
.Db(cfg.Database)
.Timeout(cfg.Timeout)
.Connect () }
(host >> port >> auth >> timeout >> db >> connect) config

View File

@@ -0,0 +1,16 @@
[<AutoOpen>]
module MyWebLog.Data.RethinkDB.Extensions
open System.Threading.Tasks
// H/T: Suave
type AsyncBuilder with
/// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
/// a standard .NET task
member x.Bind(t : Task<'T>, f:'T -> Async<'R>) : Async<'R> = async.Bind(Async.AwaitTask t, f)
/// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
/// a standard .NET task which does not commpute a value
member x.Bind(t : Task, f : unit -> Async<'R>) : Async<'R> = async.Bind(Async.AwaitTask t, f)
member x.ReturnFrom(t : Task<'T>) = Async.AwaitTask t

View File

@@ -0,0 +1,98 @@
module MyWebLog.Data.RethinkDB.Page
open MyWebLog.Entities
open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Try to find a page by its Id, optionally including revisions
let tryFindPageById conn webLogId (pageId : string) includeRevs =
async {
let q =
r.Table(Table.Page)
.Get pageId
let! thePage =
match includeRevs with
| true -> q.RunResultAsync<Page> conn
| _ -> q.Without("Revisions").RunResultAsync<Page> conn
return
match box thePage with
| null -> None
| page ->
let pg : Page = unbox page
match pg.WebLogId = webLogId with true -> Some pg | _ -> None
}
|> Async.RunSynchronously
/// Find a page by its permalink
let tryFindPageByPermalink conn (webLogId : string) (permalink : string) =
async {
let! pg =
r.Table(Table.Page)
.GetAll(r.Array (webLogId, permalink)).OptArg("index", "Permalink")
.Without("Revisions")
.RunResultAsync<Page list> conn
return List.tryHead pg
}
|> Async.RunSynchronously
/// Get a list of all pages (excludes page text and revisions)
let findAllPages conn (webLogId : string) =
async {
return!
r.Table(Table.Page)
.GetAll(webLogId).OptArg("index", "WebLogId")
.OrderBy("Title")
.Without("Text", "Revisions")
.RunResultAsync<Page list> conn
}
|> Async.RunSynchronously
/// Add a page
let addPage conn (page : Page) =
async {
do! r.Table(Table.Page)
.Insert(page)
.RunResultAsync conn
}
|> (Async.RunSynchronously >> ignore)
type PageUpdateRecord =
{ Title : string
Permalink : string
PublishedOn : int64
UpdatedOn : int64
ShowInPageList : bool
Text : string
Revisions : Revision list }
/// Update a page
let updatePage conn (page : Page) =
match tryFindPageById conn page.WebLogId page.Id false with
| Some _ ->
async {
do! r.Table(Table.Page)
.Get(page.Id)
.Update({ PageUpdateRecord.Title = page.Title
Permalink = page.Permalink
PublishedOn = page.PublishedOn
UpdatedOn = page.UpdatedOn
ShowInPageList = page.ShowInPageList
Text = page.Text
Revisions = page.Revisions })
.RunResultAsync conn
}
|> (Async.RunSynchronously >> ignore)
| _ -> ()
/// Delete a page
let deletePage conn webLogId pageId =
match tryFindPageById conn webLogId pageId false with
| Some _ ->
async {
do! r.Table(Table.Page)
.Get(pageId)
.Delete()
.RunResultAsync conn
}
|> (Async.RunSynchronously >> ignore)
| _ -> ()

View File

@@ -0,0 +1,225 @@
module MyWebLog.Data.RethinkDB.Post
open MyWebLog.Entities
open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Shorthand to select all published posts for a web log
let private publishedPosts (webLogId : string) =
r.Table(Table.Post)
.GetAll(r.Array (webLogId, PostStatus.Published)).OptArg("index", "WebLogAndStatus")
.Without("Revisions")
// This allows us to count comments without retrieving them all
.Merge(ReqlFunction1 (fun p ->
upcast r.HashMap(
"Comments", r.Table(Table.Comment)
.GetAll(p.["id"]).OptArg("index", "PostId")
.Pluck("id")
.CoerceTo("array"))))
/// Shorthand to sort posts by published date, slice for the given page, and return a list
let private toPostList conn pageNbr nbrPerPage (filter : ReqlExpr) =
async {
return!
filter
.OrderBy(r.Desc "PublishedOn")
.Slice((pageNbr - 1) * nbrPerPage, pageNbr * nbrPerPage)
.RunResultAsync<Post list> conn
}
|> Async.RunSynchronously
/// Shorthand to get a newer or older post
let private adjacentPost conn (post : Post) (theFilter : ReqlExpr -> obj) (sort : obj) =
async {
let! post =
(publishedPosts post.WebLogId)
.Filter(theFilter)
.OrderBy(sort)
.Limit(1)
.RunResultAsync<Post list> conn
return List.tryHead post
}
|> Async.RunSynchronously
/// Find a newer post
let private newerPost conn post theFilter = adjacentPost conn post theFilter <| r.Asc "PublishedOn"
/// Find an older post
let private olderPost conn post theFilter = adjacentPost conn post theFilter <| r.Desc "PublishedOn"
/// Get a page of published posts
let findPageOfPublishedPosts conn webLogId pageNbr nbrPerPage =
publishedPosts webLogId
|> toPostList conn pageNbr nbrPerPage
/// Get a page of published posts assigned to a given category
let findPageOfCategorizedPosts conn webLogId (categoryId : string) pageNbr nbrPerPage =
(publishedPosts webLogId)
.Filter(ReqlFunction1 (fun p -> upcast p.["CategoryIds"].Contains categoryId))
|> toPostList conn pageNbr nbrPerPage
/// Get a page of published posts tagged with a given tag
let findPageOfTaggedPosts conn webLogId (tag : string) pageNbr nbrPerPage =
(publishedPosts webLogId)
.Filter(ReqlFunction1 (fun p -> upcast p.["Tags"].Contains tag))
|> toPostList conn pageNbr nbrPerPage
/// Try to get the next newest post from the given post
let tryFindNewerPost conn post = newerPost conn post (fun p -> upcast p.["PublishedOn"].Gt post.PublishedOn)
/// Try to get the next newest post assigned to the given category
let tryFindNewerCategorizedPost conn (categoryId : string) post =
newerPost conn post (fun p -> upcast p.["PublishedOn"].Gt(post.PublishedOn)
.And(p.["CategoryIds"].Contains categoryId))
/// Try to get the next newest tagged post from the given tagged post
let tryFindNewerTaggedPost conn (tag : string) post =
newerPost conn post (fun p -> upcast p.["PublishedOn"].Gt(post.PublishedOn).And(p.["Tags"].Contains tag))
/// Try to get the next oldest post from the given post
let tryFindOlderPost conn post = olderPost conn post (fun p -> upcast p.["PublishedOn"].Lt post.PublishedOn)
/// Try to get the next oldest post assigned to the given category
let tryFindOlderCategorizedPost conn (categoryId : string) post =
olderPost conn post (fun p -> upcast p.["PublishedOn"].Lt(post.PublishedOn)
.And(p.["CategoryIds"].Contains categoryId))
/// Try to get the next oldest tagged post from the given tagged post
let tryFindOlderTaggedPost conn (tag : string) post =
olderPost conn post (fun p -> upcast p.["PublishedOn"].Lt(post.PublishedOn).And(p.["Tags"].Contains tag))
/// Get a page of all posts in all statuses
let findPageOfAllPosts conn (webLogId : string) pageNbr nbrPerPage =
// FIXME: sort unpublished posts by their last updated date
async {
// .orderBy(r.desc(r.branch(r.row("Status").eq("Published"), r.row("PublishedOn"), r.row("UpdatedOn"))))
return!
r.Table(Table.Post)
.GetAll(webLogId).OptArg("index", "WebLogId")
.OrderBy(r.Desc (ReqlFunction1 (fun p ->
upcast r.Branch (p.["Status"].Eq("Published"), p.["PublishedOn"], p.["UpdatedOn"]))))
.Slice((pageNbr - 1) * nbrPerPage, pageNbr * nbrPerPage)
.RunResultAsync<Post list> conn
}
|> Async.RunSynchronously
/// Try to find a post by its Id and web log Id
let tryFindPost conn webLogId postId : Post option =
async {
let! p =
r.Table(Table.Post)
.Get(postId)
.RunAtomAsync<Post> conn
return
match box p with
| null -> None
| pst ->
let post : Post = unbox pst
match post.WebLogId = webLogId with true -> Some post | _ -> None
}
|> Async.RunSynchronously
/// Try to find a post by its permalink
let tryFindPostByPermalink conn webLogId permalink =
async {
let! post =
r.Table(Table.Post)
.GetAll(r.Array (webLogId, permalink)).OptArg("index", "Permalink")
.Filter(ReqlFunction1 (fun p -> upcast p.["Status"].Eq PostStatus.Published))
.Without("Revisions")
.Merge(ReqlFunction1 (fun p ->
upcast r.HashMap(
"Categories", r.Table(Table.Category)
.GetAll(r.Args p.["CategoryIds"])
.Without("Children")
.OrderBy("Name")
.CoerceTo("array")).With(
"Comments", r.Table(Table.Comment)
.GetAll(p.["id"]).OptArg("index", "PostId")
.OrderBy("PostedOn")
.CoerceTo("array"))))
.RunResultAsync<Post list> conn
return List.tryHead post
}
|> Async.RunSynchronously
/// Try to find a post by its prior permalink
let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) =
async {
let! post =
r.Table(Table.Post)
.GetAll(webLogId).OptArg("index", "WebLogId")
.Filter(ReqlFunction1 (fun p ->
upcast p.["PriorPermalinks"].Contains(permalink).And(p.["Status"].Eq PostStatus.Published)))
.Without("Revisions")
.RunResultAsync<Post list> conn
return List.tryHead post
}
|> Async.RunSynchronously
/// Get a set of posts for RSS
let findFeedPosts conn webLogId nbr : (Post * User option) list =
let tryFindUser userId =
async {
let! u =
r.Table(Table.User)
.Get(userId)
.RunAtomAsync<User> conn
return match box u with null -> None | user -> Some <| unbox user
}
|> Async.RunSynchronously
(publishedPosts webLogId)
.Merge(ReqlFunction1 (fun post ->
upcast r.HashMap(
"Categories", r.Table(Table.Category)
.GetAll(r.Args post.["CategoryIds"])
.OrderBy("Name")
.Pluck("id", "Name")
.CoerceTo("array"))))
|> toPostList conn 1 nbr
|> List.map (fun post -> post, tryFindUser post.AuthorId)
/// Add a post
let addPost conn post =
async {
do! r.Table(Table.Post)
.Insert(post)
.RunResultAsync conn
}
|> (Async.RunSynchronously >> ignore)
/// Update a post
let updatePost conn (post : Post) =
async {
do! r.Table(Table.Post)
.Get(post.Id)
.Replace( { post with Categories = []
Comments = [] } )
.RunResultAsync conn
}
|> (Async.RunSynchronously >> ignore)
/// Save a post
let savePost conn (post : Post) =
match post.Id with
| "new" ->
let newPost = { post with Id = string <| System.Guid.NewGuid() }
async {
do! r.Table(Table.Post)
.Insert(newPost)
.RunResultAsync conn
}
|> Async.RunSynchronously
newPost.Id
| _ ->
async {
do! r.Table(Table.Post)
.Get(post.Id)
.Replace( { post with Categories = []
Comments = [] } )
.RunResultAsync conn
}
|> Async.RunSynchronously
post.Id

View File

@@ -0,0 +1,48 @@
namespace MyWebLog.Data.RethinkDB
open MyWebLog.Data
open RethinkDb.Driver.Net
/// RethinkDB implementation of myWebLog data persistence
type RethinkMyWebLogData(conn : IConnection, cfg : DataConfig) =
interface IMyWebLogData with
member __.SetUp = fun () -> SetUp.startUpCheck cfg
member __.AllCategories = Category.getAllCategories conn
member __.CategoryById = Category.tryFindCategory conn
member __.CategoryBySlug = Category.tryFindCategoryBySlug conn
member __.AddCategory = Category.addCategory conn
member __.UpdateCategory = Category.updateCategory conn
member __.UpdateChildren = Category.updateChildren conn
member __.DeleteCategory = Category.deleteCategory conn
member __.PageById = Page.tryFindPageById conn
member __.PageByPermalink = Page.tryFindPageByPermalink conn
member __.AllPages = Page.findAllPages conn
member __.AddPage = Page.addPage conn
member __.UpdatePage = Page.updatePage conn
member __.DeletePage = Page.deletePage conn
member __.PageOfPublishedPosts = Post.findPageOfPublishedPosts conn
member __.PageOfCategorizedPosts = Post.findPageOfCategorizedPosts conn
member __.PageOfTaggedPosts = Post.findPageOfTaggedPosts conn
member __.NewerPost = Post.tryFindNewerPost conn
member __.NewerCategorizedPost = Post.tryFindNewerCategorizedPost conn
member __.NewerTaggedPost = Post.tryFindNewerTaggedPost conn
member __.OlderPost = Post.tryFindOlderPost conn
member __.OlderCategorizedPost = Post.tryFindOlderCategorizedPost conn
member __.OlderTaggedPost = Post.tryFindOlderTaggedPost conn
member __.PageOfAllPosts = Post.findPageOfAllPosts conn
member __.PostById = Post.tryFindPost conn
member __.PostByPermalink = Post.tryFindPostByPermalink conn
member __.PostByPriorPermalink = Post.tryFindPostByPriorPermalink conn
member __.FeedPosts = Post.findFeedPosts conn
member __.AddPost = Post.addPost conn
member __.UpdatePost = Post.updatePost conn
member __.LogOn = User.tryUserLogOn conn
member __.SetUserPassword = User.setUserPassword conn
member __.WebLogByUrlBase = WebLog.tryFindWebLogByUrlBase conn
member __.DashboardCounts = WebLog.findDashboardCounts conn

View File

@@ -0,0 +1,100 @@
module MyWebLog.Data.RethinkDB.SetUp
open RethinkDb.Driver.Ast
open System
let private r = RethinkDb.Driver.RethinkDB.R
let private logStep step = Console.Out.WriteLine (sprintf "[myWebLog] %s" step)
let private logStepStart text = Console.Out.Write (sprintf "[myWebLog] %s..." text)
let private logStepDone () = Console.Out.WriteLine (" done.")
/// Ensure the myWebLog database exists
let private checkDatabase (cfg : DataConfig) =
async {
logStep "|> Checking database"
let! dbs = r.DbList().RunResultAsync<string list> cfg.Conn
match List.contains cfg.Database dbs with
| true -> ()
| _ -> logStepStart (sprintf " %s database not found - creating" cfg.Database)
do! r.DbCreate(cfg.Database).RunResultAsync cfg.Conn
logStepDone ()
}
/// Ensure all required tables exist
let private checkTables cfg =
async {
logStep "|> Checking tables"
let! tables = r.Db(cfg.Database).TableList().RunResultAsync<string list> cfg.Conn
[ Table.Category; Table.Comment; Table.Page; Table.Post; Table.User; Table.WebLog ]
|> List.filter (fun tbl -> not (List.contains tbl tables))
|> List.iter (fun tbl -> logStepStart (sprintf " Creating table %s" tbl)
async { do! (r.TableCreate tbl).RunResultAsync cfg.Conn } |> Async.RunSynchronously
logStepDone ())
}
/// Shorthand to get the table
let private tbl cfg table = r.Db(cfg.Database).Table table
/// Create the given index
let private createIndex cfg table (index : string * (ReqlExpr -> obj) option) =
async {
let idxName, idxFunc = index
logStepStart (sprintf """ Creating index "%s" on table %s""" idxName table)
do! (match idxFunc with
| Some f -> (tbl cfg table).IndexCreate(idxName, f)
| None -> (tbl cfg table).IndexCreate(idxName))
.RunResultAsync cfg.Conn
logStepDone ()
}
/// Ensure that the given indexes exist, and create them if required
let private ensureIndexes cfg (indexes : (string * (string * (ReqlExpr -> obj) option) list) list) =
let ensureForTable (tblName, idxs) =
async {
let! idx = (tbl cfg tblName).IndexList().RunResultAsync<string list> cfg.Conn
idxs
|> List.filter (fun (idxName, _) -> not (List.contains idxName idx))
|> List.map (fun index -> createIndex cfg tblName index)
|> List.iter Async.RunSynchronously
}
|> Async.RunSynchronously
indexes
|> List.iter ensureForTable
/// Create an index on web log Id and the given field
let private webLogField (name : string) : (ReqlExpr -> obj) option =
Some <| fun row -> upcast r.Array(row.["WebLogId"], row.[name])
/// Ensure all the required indexes exist
let private checkIndexes cfg =
logStep "|> Checking indexes"
[ Table.Category, [ "WebLogId", None
"Slug", webLogField "Slug"
]
Table.Comment, [ "PostId", None
]
Table.Page, [ "WebLogId", None
"Permalink", webLogField "Permalink"
]
Table.Post, [ "WebLogId", None
"WebLogAndStatus", webLogField "Status"
"Permalink", webLogField "Permalink"
]
Table.User, [ "UserName", None
]
Table.WebLog, [ "UrlBase", None
]
]
|> ensureIndexes cfg
/// Start up checks to ensure the database, tables, and indexes exist
let startUpCheck cfg =
async {
logStep "Database Start Up Checks Starting"
do! checkDatabase cfg
do! checkTables cfg
checkIndexes cfg
logStep "Database Start Up Checks Complete"
}
|> Async.RunSynchronously

View File

@@ -0,0 +1,21 @@
/// Constants for tables used in myWebLog
[<RequireQualifiedAccess>]
module MyWebLog.Data.RethinkDB.Table
/// The Category table
let Category = "Category"
/// The Comment table
let Comment = "Comment"
/// The Page table
let Page = "Page"
/// The Post table
let Post = "Post"
/// The WebLog table
let WebLog = "WebLog"
/// The User table
let User = "User"

View File

@@ -0,0 +1,31 @@
module MyWebLog.Data.RethinkDB.User
open MyWebLog.Entities
open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Log on a user
// NOTE: The significant length of a RethinkDB index is 238 - [PK size]; as we're storing 1,024 characters of password,
// including it in an index does not get any performance gain, and would unnecessarily bloat the index. See
// http://rethinkdb.com/docs/secondary-indexes/java/ for more information.
let tryUserLogOn conn (email : string) (passwordHash : string) =
async {
let! user =
r.Table(Table.User)
.GetAll(email).OptArg("index", "UserName")
.Filter(ReqlFunction1 (fun u -> upcast u.["PasswordHash"].Eq passwordHash))
.RunResultAsync<User list> conn
return user |> List.tryHead
}
|> Async.RunSynchronously
/// Set a user's password
let setUserPassword conn (email : string) (passwordHash : string) =
async {
do! r.Table(Table.User)
.GetAll(email).OptArg("index", "UserName")
.Update(dict [ "PasswordHash", passwordHash ])
.RunResultAsync conn
}
|> Async.RunSynchronously

View File

@@ -0,0 +1,39 @@
module MyWebLog.Data.RethinkDB.WebLog
open MyWebLog.Entities
open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Detemine the web log by the URL base
let tryFindWebLogByUrlBase conn (urlBase : string) =
async {
let! cursor =
r.Table(Table.WebLog)
.GetAll(urlBase).OptArg("index", "UrlBase")
.Merge(ReqlFunction1 (fun w ->
upcast r.HashMap(
"PageList", r.Table(Table.Page)
.GetAll(w.G("id")).OptArg("index", "WebLogId")
.Filter(ReqlFunction1 (fun pg -> upcast pg.["ShowInPageList"].Eq true))
.OrderBy("Title")
.Pluck("Title", "Permalink")
.CoerceTo("array"))))
.RunCursorAsync<WebLog> conn
return cursor |> Seq.tryHead
}
|> Async.RunSynchronously
/// Get counts for the admin dashboard
let findDashboardCounts conn (webLogId : string) =
async {
return!
r.Expr(
r.HashMap(
"Pages", r.Table(Table.Page ).GetAll(webLogId).OptArg("index", "WebLogId").Count()).With(
"Posts", r.Table(Table.Post ).GetAll(webLogId).OptArg("index", "WebLogId").Count()).With(
"Categories", r.Table(Table.Category).GetAll(webLogId).OptArg("index", "WebLogId").Count()))
.RunResultAsync<DashboardCounts> conn
}
|> Async.RunSynchronously