299 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Forth
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Forth
		
	
	
	
	
	
| [<RequireQualifiedAccess>]
 | |
| module MyWebLog.Data
 | |
| 
 | |
| /// Table names
 | |
| [<RequireQualifiedAccess>]
 | |
| module private 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 web log table
 | |
|     let WebLog = "WebLog"
 | |
| 
 | |
|     /// The web log user table
 | |
|     let WebLogUser = "WebLogUser"
 | |
| 
 | |
|     /// A list of all tables
 | |
|     let all = [ Category; Comment; Page; Post; WebLog; WebLogUser ]
 | |
| 
 | |
| 
 | |
| /// Functions to assist with retrieving data
 | |
| [<AutoOpen>]
 | |
| module Helpers =
 | |
|     
 | |
|     open RethinkDb.Driver
 | |
|     open RethinkDb.Driver.Net
 | |
|     open System.Threading.Tasks
 | |
| 
 | |
|     /// Shorthand for the ReQL starting point
 | |
|     let r = RethinkDB.R
 | |
| 
 | |
|     /// Verify that the web log ID matches before returning an item
 | |
|     let verifyWebLog<'T> webLogId (prop : 'T -> WebLogId) (f : IConnection -> Task<'T option>) =
 | |
|         fun conn -> task {
 | |
|             match! f conn with Some it when (prop it) = webLogId -> return Some it | _ -> return None
 | |
|         }
 | |
| 
 | |
|     
 | |
| open RethinkDb.Driver.FSharp
 | |
| open Microsoft.Extensions.Logging
 | |
| 
 | |
| module Startup =
 | |
|     
 | |
|     /// Ensure field indexes exist, as well as special indexes for selected tables
 | |
|     let private ensureIndexes (log : ILogger) conn table fields = task {
 | |
|         let! indexes = rethink<string list> { withTable table; indexList; result; withRetryOnce conn }
 | |
|         for field in fields do
 | |
|             match indexes |> List.contains field with
 | |
|             | true -> ()
 | |
|             | false ->
 | |
|                 log.LogInformation($"Creating index {table}.{field}...")
 | |
|                 let! _ = rethink { withTable table; indexCreate field; write; withRetryOnce conn }
 | |
|                 ()
 | |
|         // Post and page need index by web log ID and permalink
 | |
|         match [ Table.Page; Table.Post ] |> List.contains table with
 | |
|         | true ->
 | |
|             match indexes |> List.contains "permalink" with
 | |
|             | true -> ()
 | |
|             | false ->
 | |
|                 log.LogInformation($"Creating index {table}.permalink...")
 | |
|                 let! _ =
 | |
|                     rethink {
 | |
|                         withTable table
 | |
|                         indexCreate "permalink" (fun row -> r.Array(row.G "webLogId", row.G "permalink"))
 | |
|                         write
 | |
|                         withRetryOnce conn
 | |
|                     }
 | |
|                 ()
 | |
|         | false -> ()
 | |
|         // Users log on with e-mail
 | |
|         match Table.WebLogUser = table with
 | |
|         | true ->
 | |
|             match indexes |> List.contains "logOn" with
 | |
|             | true -> ()
 | |
|             | false ->
 | |
|                 log.LogInformation($"Creating index {table}.logOn...")
 | |
|                 let! _ =
 | |
|                     rethink {
 | |
|                         withTable table
 | |
|                         indexCreate "logOn" (fun row -> r.Array(row.G "webLogId", row.G "email"))
 | |
|                         write
 | |
|                         withRetryOnce conn
 | |
|                     }
 | |
|                 ()
 | |
|         | false -> ()
 | |
|     }
 | |
| 
 | |
|     /// Ensure all necessary tables and indexes exist
 | |
|     let ensureDb (config : DataConfig) (log : ILogger) conn = task {
 | |
|         
 | |
|         let! dbs = rethink<string list> { dbList; result; withRetryOnce conn }
 | |
|         match dbs |> List.contains config.Database with
 | |
|         | true -> ()
 | |
|         | false ->
 | |
|             log.LogInformation($"Creating database {config.Database}...")
 | |
|             let! _ = rethink { dbCreate config.Database; write; withRetryOnce conn }
 | |
|             ()
 | |
|         
 | |
|         let! tables = rethink<string list> { tableList; result; withRetryOnce conn }
 | |
|         for tbl in Table.all do
 | |
|             match tables |> List.contains tbl with
 | |
|             | true -> ()
 | |
|             | false ->
 | |
|                 log.LogInformation($"Creating table {tbl}...")
 | |
|                 let! _ = rethink { tableCreate tbl; write; withRetryOnce conn }
 | |
|                 ()
 | |
| 
 | |
|         let makeIdx = ensureIndexes log conn
 | |
|         do! makeIdx Table.Category   [ "webLogId" ]
 | |
|         do! makeIdx Table.Comment    [ "postId" ]
 | |
|         do! makeIdx Table.Page       [ "webLogId"; "authorId" ]
 | |
|         do! makeIdx Table.Post       [ "webLogId"; "authorId" ]
 | |
|         do! makeIdx Table.WebLog     [ "urlBase" ]
 | |
|         do! makeIdx Table.WebLogUser [ "webLogId" ]
 | |
|     }
 | |
| 
 | |
| /// Functions to manipulate categories
 | |
| module Category =
 | |
|     
 | |
|     /// Count all categories for a web log
 | |
|     let countAll (webLogId : WebLogId) =
 | |
|         rethink<int> {
 | |
|             withTable Table.Category
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             count
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Count top-level categories for a web log
 | |
|     let countTopLevel (webLogId : WebLogId) =
 | |
|         rethink<int> {
 | |
|             withTable Table.Category
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             filter "parentId" None
 | |
|             count
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
| 
 | |
| /// Functions to manipulate pages
 | |
| module Page =
 | |
|     
 | |
|     /// Count all pages for a web log
 | |
|     let countAll (webLogId : WebLogId) =
 | |
|         rethink<int> {
 | |
|             withTable Table.Page
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             count
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Count listed pages for a web log
 | |
|     let countListed (webLogId : WebLogId) =
 | |
|         rethink<int> {
 | |
|             withTable Table.Page
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             filter "showInPageList" true
 | |
|             count
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Retrieve all pages for a web log
 | |
|     let findAll (webLogId : WebLogId) =
 | |
|         rethink<Page list> {
 | |
|             withTable Table.Page
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             without [ "priorPermalinks", "revisions" ]
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Find a page by its ID
 | |
|     let findById (pageId : PageId) webLogId =
 | |
|         rethink<Page> {
 | |
|             withTable Table.Page
 | |
|             get pageId
 | |
|             without [ "priorPermalinks", "revisions" ]
 | |
|             resultOption
 | |
|             withRetryDefault
 | |
|         }
 | |
|         |> verifyWebLog webLogId (fun it -> it.webLogId)
 | |
| 
 | |
|     /// Find a page by its permalink
 | |
|     let findByPermalink (permalink : Permalink) (webLogId : WebLogId) =
 | |
|         rethink<Page> {
 | |
|             withTable Table.Page
 | |
|             getAll [ r.Array (webLogId, permalink) ] (nameof permalink)
 | |
|             without [ "priorPermalinks", "revisions" ]
 | |
|             limit 1
 | |
|             resultOption
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Find a page by its ID (including permalinks and revisions)
 | |
|     let findByFullId (pageId : PageId) webLogId =
 | |
|         rethink<Page> {
 | |
|             withTable Table.Page
 | |
|             get pageId
 | |
|             resultOption
 | |
|             withRetryDefault
 | |
|         }
 | |
|         |> verifyWebLog webLogId (fun it -> it.webLogId)
 | |
| 
 | |
|     /// Find a list of pages (displayed in admin area)
 | |
|     let findPageOfPages (webLogId : WebLogId) pageNbr =
 | |
|         rethink<Page list> {
 | |
|             withTable Table.Page
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             without [ "priorPermalinks", "revisions" ]
 | |
|             orderBy "title"
 | |
|             skip ((pageNbr - 1) * 25)
 | |
|             limit 25
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
| /// Functions to manipulate posts
 | |
| module Post =
 | |
|     
 | |
|     /// Count posts for a web log by their status
 | |
|     let countByStatus (status : PostStatus) (webLogId : WebLogId) =
 | |
|         rethink<int> {
 | |
|             withTable Table.Post
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             filter "status" status
 | |
|             count
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Find a post by its permalink
 | |
|     let findByPermalink (permalink : Permalink) (webLogId : WebLogId) =
 | |
|         rethink<Post> {
 | |
|             withTable Table.Post
 | |
|             getAll [ r.Array(permalink, webLogId) ] (nameof permalink)
 | |
|             without [ "priorPermalinks", "revisions" ]
 | |
|             limit 1
 | |
|             resultOption
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Find posts to be displayed on a page
 | |
|     let findPageOfPublishedPosts (webLogId : WebLogId) pageNbr postsPerPage =
 | |
|         rethink<Post list> {
 | |
|             withTable Table.Post
 | |
|             getAll [ webLogId ] (nameof webLogId)
 | |
|             filter "status" Published
 | |
|             without [ "priorPermalinks", "revisions" ]
 | |
|             orderBy "publishedOn"
 | |
|             skip ((pageNbr - 1) * postsPerPage)
 | |
|             limit postsPerPage
 | |
|             result
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
| 
 | |
| /// Functions to manipulate web logs
 | |
| module WebLog =
 | |
|     
 | |
|     /// Retrieve web log details by the URL base
 | |
|     let findByHost (url : string) =
 | |
|         rethink<WebLog> {
 | |
|             withTable Table.WebLog
 | |
|             getAll [ url ] "urlBase"
 | |
|             limit 1
 | |
|             resultOption
 | |
|             withRetryDefault
 | |
|         }
 | |
| 
 | |
|     /// Update web log settings
 | |
|     let updateSettings (webLog : WebLog) =
 | |
|         rethink {
 | |
|             withTable Table.WebLog
 | |
|             get webLog.id
 | |
|             update [ 
 | |
|                 "name",         webLog.name
 | |
|                 "subtitle",     webLog.subtitle
 | |
|                 "defaultPage",  webLog.defaultPage
 | |
|                 "postsPerPage", webLog.postsPerPage
 | |
|                 "timeZone",     webLog.timeZone
 | |
|                 ]
 | |
|             write
 | |
|             withRetryDefault
 | |
|             ignoreResult
 | |
|         }
 |