Version 2.1 #41

Merged
danieljsummers merged 123 commits from version-2.1 into main 2024-03-27 00:13:28 +00:00
11 changed files with 65 additions and 54 deletions
Showing only changes of commit 13994a29e7 - Show all commits

View File

@ -5,9 +5,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BitBadger.Documents.Postgres" Version="3.0.0-rc-1" /> <PackageReference Include="BitBadger.Documents.Postgres" Version="3.0.0-rc-2" />
<PackageReference Include="BitBadger.Documents.Sqlite" Version="3.0.0-rc-1" /> <PackageReference Include="BitBadger.Documents.Sqlite" Version="3.0.0-rc-2" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" /> <PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />

View File

@ -81,15 +81,26 @@ type PostgresCategoryData(log: ILogger) =
let! children = Find.byContains<Category> Table.Category {| ParentId = catId |} let! children = Find.byContains<Category> Table.Category {| ParentId = catId |}
let hasChildren = not (List.isEmpty children) let hasChildren = not (List.isEmpty children)
if hasChildren then if hasChildren then
let! _ = if cat.ParentId.IsSome then
Configuration.dataSource () let! _ =
|> Sql.fromDataSource Configuration.dataSource ()
|> Sql.executeTransactionAsync |> Sql.fromDataSource
[ Query.Patch.byId Table.Category, |> Sql.executeTransactionAsync
children [ Query.Patch.byId Table.Category,
|> List.map (fun child -> children
[ idParam child.Id; jsonParam "@data" {| ParentId = cat.ParentId |} ]) ] |> List.map (fun child ->
() [ idParam child.Id; jsonParam "@data" {| ParentId = cat.ParentId |} ]) ]
()
else
let! _ =
Configuration.dataSource ()
|> Sql.fromDataSource
|> Sql.executeTransactionAsync
[ Query.RemoveFields.byId Table.Category,
children
|> List.map (fun child ->
[ idParam child.Id; fieldNameParam [ nameof Category.Empty.ParentId ] ]) ]
()
// Delete the category off all posts where it is assigned // Delete the category off all posts where it is assigned
let! posts = let! posts =
Custom.list Custom.list

View File

@ -53,7 +53,7 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
SELECT COUNT(DISTINCT data ->> '{nameof Post.Empty.Id}') SELECT COUNT(DISTINCT data ->> '{nameof Post.Empty.Id}')
FROM {Table.Post} FROM {Table.Post}
WHERE {Document.Query.whereByWebLog} WHERE {Document.Query.whereByWebLog}
AND {Query.whereByField (nameof Post.Empty.Status) EQ $"'{string Published}'"} AND {Query.whereByField (Field.EQ (nameof Post.Empty.Status) "") $"'{string Published}'"}
AND {catSql}""" AND {catSql}"""
let! postCount = conn.customScalar query (webLogParam webLogId :: catParams) toCount let! postCount = conn.customScalar query (webLogParam webLogId :: catParams) toCount
return it.Id, int postCount return it.Id, int postCount
@ -79,17 +79,12 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
match! findById catId webLogId with match! findById catId webLogId with
| Some cat -> | Some cat ->
// Reassign any children to the category's parent category // Reassign any children to the category's parent category
let! children = conn.countByField Table.Category parentIdField EQ (string catId) let! children = conn.countByField Table.Category (Field.EQ parentIdField (string catId))
if children > 0L then if children > 0L then
let parent = Field.EQ parentIdField (string catId)
match cat.ParentId with match cat.ParentId with
| Some _ -> | Some _ -> do! conn.patchByField Table.Category parent {| ParentId = cat.ParentId |}
do! conn.patchByField Table.Category parentIdField EQ (string catId) {| ParentId = cat.ParentId |} | None -> do! conn.removeFieldsByField Table.Category parent [ parentIdField ]
| None ->
do! conn.customNonQuery
$"""UPDATE {Table.Category}
SET data = json_remove(data, '$.ParentId')
WHERE {Query.whereByField parentIdField EQ "@field"}"""
[ fieldParam (string catId) ]
// Delete the category off all posts where it is assigned, and the category itself // Delete the category off all posts where it is assigned, and the category itself
let catIdField = nameof Post.Empty.CategoryIds let catIdField = nameof Post.Empty.CategoryIds
let! posts = let! posts =

View File

@ -239,7 +239,7 @@ module Document =
/// Fragment to add a web log ID condition to a WHERE clause (parameter @webLogId) /// Fragment to add a web log ID condition to a WHERE clause (parameter @webLogId)
let whereByWebLog = let whereByWebLog =
Query.whereByField "WebLogId" EQ "@webLogId" Query.whereByField (Field.EQ "WebLogId" "") "@webLogId"
/// A SELECT query to count documents for a given web log ID /// A SELECT query to count documents for a given web log ID
let countByWebLog table = let countByWebLog table =
@ -255,7 +255,7 @@ module Document =
/// Count documents for the given web log ID /// Count documents for the given web log ID
let countByWebLog table (webLogId: WebLogId) conn = backgroundTask { let countByWebLog table (webLogId: WebLogId) conn = backgroundTask {
let! count = Count.byField table "WebLogId" EQ (string webLogId) conn let! count = Count.byField table (Field.EQ "WebLogId" (string webLogId)) conn
return int count return int count
} }
@ -265,7 +265,7 @@ module Document =
/// Find documents for the given web log /// Find documents for the given web log
let findByWebLog<'TDoc> table (webLogId: WebLogId) conn = let findByWebLog<'TDoc> table (webLogId: WebLogId) conn =
Find.byField<'TDoc> table "WebLogId" EQ (string webLogId) conn Find.byField<'TDoc> table (Field.EQ "WebLogId" (string webLogId)) conn
/// Functions to support revisions /// Functions to support revisions

View File

@ -53,7 +53,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
let countListed webLogId = let countListed webLogId =
log.LogTrace "Page.countListed" log.LogTrace "Page.countListed"
conn.customScalar conn.customScalar
$"""{Document.Query.countByWebLog Table.Page} AND {Query.whereByField pgListName EQ "true"}""" $"""{Document.Query.countByWebLog Table.Page} AND {Query.whereByField (Field.EQ pgListName "") "true"}"""
[ webLogParam webLogId ] [ webLogParam webLogId ]
(toCount >> int) (toCount >> int)
@ -88,9 +88,10 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
/// Find a page by its permalink for the given web log /// Find a page by its permalink for the given web log
let findByPermalink (permalink: Permalink) webLogId = let findByPermalink (permalink: Permalink) webLogId =
log.LogTrace "Page.findByPermalink" log.LogTrace "Page.findByPermalink"
let linkParam = Field.EQ linkName (string permalink)
conn.customSingle conn.customSingle
$"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField linkName EQ "@link"}""" $"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField linkParam "@link"}"""
[ webLogParam webLogId; SqliteParameter("@link", string permalink) ] (addFieldParam "@link" linkParam [ webLogParam webLogId ])
fromData<Page> fromData<Page>
/// Find the current permalink within a set of potential prior permalinks for the given web log /// Find the current permalink within a set of potential prior permalinks for the given web log
@ -116,7 +117,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
let findListed webLogId = let findListed webLogId =
log.LogTrace "Page.findListed" log.LogTrace "Page.findListed"
conn.customList conn.customList
$"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField pgListName EQ "true"} $"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField (Field.EQ pgListName "") "true"}
ORDER BY LOWER({titleField})""" ORDER BY LOWER({titleField})"""
[ webLogParam webLogId ] [ webLogParam webLogId ]
(fun rdr -> { fromData<Page> rdr with Text = "" }) (fun rdr -> { fromData<Page> rdr with Text = "" })

View File

@ -34,7 +34,8 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
let postByWebLog = Document.Query.selectByWebLog Table.Post let postByWebLog = Document.Query.selectByWebLog Table.Post
/// The SELECT statement to retrieve published posts with a web log ID parameter /// The SELECT statement to retrieve published posts with a web log ID parameter
let publishedPostByWebLog = $"""{postByWebLog} AND {Query.whereByField statName EQ $"'{string Published}'"}""" let publishedPostByWebLog =
$"""{postByWebLog} AND {Query.whereByField (Field.EQ statName "") $"'{string Published}'"}"""
/// Update a post's revisions /// Update a post's revisions
let updatePostRevisions (postId: PostId) oldRevs newRevs = let updatePostRevisions (postId: PostId) oldRevs newRevs =
@ -46,9 +47,10 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// Count posts in a status for the given web log /// Count posts in a status for the given web log
let countByStatus (status: PostStatus) webLogId = let countByStatus (status: PostStatus) webLogId =
log.LogTrace "Post.countByStatus" log.LogTrace "Post.countByStatus"
let statParam = Field.EQ statName (string status)
conn.customScalar conn.customScalar
$"""{Document.Query.countByWebLog Table.Post} AND {Query.whereByField statName EQ "@status"}""" $"""{Document.Query.countByWebLog Table.Post} AND {Query.whereByField statParam "@status"}"""
[ webLogParam webLogId; SqliteParameter("@status", string status) ] (addFieldParam "@status" statParam [ webLogParam webLogId ])
(toCount >> int) (toCount >> int)
/// Find a post by its ID for the given web log (excluding revisions) /// Find a post by its ID for the given web log (excluding revisions)
@ -59,9 +61,10 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// Find a post by its permalink for the given web log (excluding revisions) /// Find a post by its permalink for the given web log (excluding revisions)
let findByPermalink (permalink: Permalink) webLogId = let findByPermalink (permalink: Permalink) webLogId =
log.LogTrace "Post.findByPermalink" log.LogTrace "Post.findByPermalink"
let linkParam = Field.EQ linkName (string permalink)
conn.customSingle conn.customSingle
$"""{Document.Query.selectByWebLog Table.Post} AND {Query.whereByField linkName EQ "@link"}""" $"""{Document.Query.selectByWebLog Table.Post} AND {Query.whereByField linkParam "@link"}"""
[ webLogParam webLogId; SqliteParameter("@link", string permalink) ] (addFieldParam "@link" linkParam [ webLogParam webLogId ])
fromData<Post> fromData<Post>
/// Find a complete post by its ID for the given web log /// Find a complete post by its ID for the given web log
@ -82,7 +85,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
do! conn.customNonQuery do! conn.customNonQuery
$"""DELETE FROM {Table.PostRevision} WHERE post_id = @id; $"""DELETE FROM {Table.PostRevision} WHERE post_id = @id;
DELETE FROM {Table.PostComment} DELETE FROM {Table.PostComment}
WHERE {Query.whereByField (nameof Comment.Empty.PostId) EQ "@id"}; WHERE {Query.whereByField (Field.EQ (nameof Comment.Empty.PostId) "") "@id"};
{Query.Delete.byId Table.Post}""" {Query.Delete.byId Table.Post}"""
[ idParam postId ] [ idParam postId ]
return true return true

View File

@ -28,10 +28,10 @@ type SQLiteTagMapData(conn: SqliteConnection, log: ILogger) =
/// Find a tag mapping by its URL value for the given web log /// Find a tag mapping by its URL value for the given web log
let findByUrlValue (urlValue: string) webLogId = let findByUrlValue (urlValue: string) webLogId =
log.LogTrace "TagMap.findByUrlValue" log.LogTrace "TagMap.findByUrlValue"
let urlParam = Field.EQ (nameof TagMap.Empty.UrlValue) urlValue
conn.customSingle conn.customSingle
$"""{Document.Query.selectByWebLog Table.TagMap} $"""{Document.Query.selectByWebLog Table.TagMap} AND {Query.whereByField urlParam "@urlValue"}"""
AND {Query.whereByField (nameof TagMap.Empty.UrlValue) EQ "@urlValue"}""" (addFieldParam "@urlValue" urlParam [ webLogParam webLogId ])
[ webLogParam webLogId; SqliteParameter("@urlValue", urlValue) ]
fromData<TagMap> fromData<TagMap>
/// Get all tag mappings for the given web log /// Get all tag mappings for the given web log

View File

@ -23,25 +23,25 @@ type SQLiteWebLogData(conn: SqliteConnection, log: ILogger) =
/// Delete a web log by its ID /// Delete a web log by its ID
let delete webLogId = let delete webLogId =
log.LogTrace "WebLog.delete" log.LogTrace "WebLog.delete"
let subQuery table = let webLogMatches = Query.whereByField (Field.EQ "WebLogId" "") "@webLogId"
$"""(SELECT data ->> 'Id' FROM {table} WHERE {Query.whereByField "WebLogId" EQ "@webLogId"}""" let subQuery table = $"(SELECT data ->> 'Id' FROM {table} WHERE {webLogMatches}"
Custom.nonQuery Custom.nonQuery
$"""DELETE FROM {Table.PostComment} WHERE data ->> 'PostId' IN {subQuery Table.Post}; $"""DELETE FROM {Table.PostComment} WHERE data ->> 'PostId' IN {subQuery Table.Post};
DELETE FROM {Table.PostRevision} WHERE post_id IN {subQuery Table.Post}; DELETE FROM {Table.PostRevision} WHERE post_id IN {subQuery Table.Post};
DELETE FROM {Table.PageRevision} WHERE page_id IN {subQuery Table.Page}; DELETE FROM {Table.PageRevision} WHERE page_id IN {subQuery Table.Page};
DELETE FROM {Table.Post} WHERE {Query.whereByField "WebLogId" EQ "@webLogId"}; DELETE FROM {Table.Post} WHERE {webLogMatches};
DELETE FROM {Table.Page} WHERE {Query.whereByField "WebLogId" EQ "@webLogId"}; DELETE FROM {Table.Page} WHERE {webLogMatches};
DELETE FROM {Table.Category} WHERE {Query.whereByField "WebLogId" EQ "@webLogId"}; DELETE FROM {Table.Category} WHERE {webLogMatches};
DELETE FROM {Table.TagMap} WHERE {Query.whereByField "WebLogId" EQ "@webLogId"}; DELETE FROM {Table.TagMap} WHERE {webLogMatches};
DELETE FROM {Table.Upload} WHERE web_log_id = @webLogId; DELETE FROM {Table.Upload} WHERE web_log_id = @webLogId;
DELETE FROM {Table.WebLogUser} WHERE {Query.whereByField "WebLogId" EQ "@webLogId"}; DELETE FROM {Table.WebLogUser} WHERE {webLogMatches};
DELETE FROM {Table.WebLog} WHERE {Query.whereById "@webLogId"}""" DELETE FROM {Table.WebLog} WHERE {Query.whereById "@webLogId"}"""
[ webLogParam webLogId ] [ webLogParam webLogId ]
/// Find a web log by its host (URL base) /// Find a web log by its host (URL base)
let findByHost (url: string) = let findByHost (url: string) =
log.LogTrace "WebLog.findByHost" log.LogTrace "WebLog.findByHost"
conn.findFirstByField<WebLog> Table.WebLog (nameof WebLog.Empty.UrlBase) EQ url conn.findFirstByField<WebLog> Table.WebLog (Field.EQ (nameof WebLog.Empty.UrlBase) url)
/// Find a web log by its ID /// Find a web log by its ID
let findById webLogId = let findById webLogId =

View File

@ -20,8 +20,8 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
log.LogTrace "WebLogUser.delete" log.LogTrace "WebLogUser.delete"
match! findById userId webLogId with match! findById userId webLogId with
| Some _ -> | Some _ ->
let! pageCount = conn.countByField Table.Page (nameof Page.Empty.AuthorId) EQ (string userId) let! pageCount = conn.countByField Table.Page (Field.EQ (nameof Page.Empty.AuthorId) (string userId))
let! postCount = conn.countByField Table.Post (nameof Post.Empty.AuthorId) EQ (string userId) let! postCount = conn.countByField Table.Post (Field.EQ (nameof Post.Empty.AuthorId) (string userId))
if pageCount + postCount > 0 then if pageCount + postCount > 0 then
return Error "User has pages or posts; cannot delete" return Error "User has pages or posts; cannot delete"
else else
@ -33,10 +33,11 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
/// Find a user by their e-mail address for the given web log /// Find a user by their e-mail address for the given web log
let findByEmail (email: string) webLogId = let findByEmail (email: string) webLogId =
log.LogTrace "WebLogUser.findByEmail" log.LogTrace "WebLogUser.findByEmail"
let emailParam = Field.EQ (nameof WebLogUser.Empty.Email) email
conn.customSingle conn.customSingle
$"""{Document.Query.selectByWebLog Table.WebLogUser} $"""{Document.Query.selectByWebLog Table.WebLogUser}
AND {Query.whereByField (nameof WebLogUser.Empty.Email) EQ "@email"}""" AND {Query.whereByField emailParam "@email"}"""
[ webLogParam webLogId; sqlParam "@email" email ] (addFieldParam "@email" emailParam [ webLogParam webLogId ])
fromData<WebLogUser> fromData<WebLogUser>
/// Get all users for the given web log /// Get all users for the given web log

View File

@ -7,10 +7,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Markdig" Version="0.31.0" /> <PackageReference Include="Markdig" Version="0.34.0" />
<PackageReference Include="Markdown.ColorCode" Version="1.0.4" /> <PackageReference Include="Markdown.ColorCode" Version="1.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NodaTime" Version="3.1.9" /> <PackageReference Include="NodaTime" Version="3.1.10" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -26,8 +26,8 @@
<PackageReference Include="BitBadger.AspNetCore.CanonicalDomains" Version="1.0.0" /> <PackageReference Include="BitBadger.AspNetCore.CanonicalDomains" Version="1.0.0" />
<PackageReference Include="DotLiquid" Version="2.2.692" /> <PackageReference Include="DotLiquid" Version="2.2.692" />
<PackageReference Include="Giraffe" Version="6.2.0" /> <PackageReference Include="Giraffe" Version="6.2.0" />
<PackageReference Include="Giraffe.Htmx" Version="1.9.4" /> <PackageReference Include="Giraffe.Htmx" Version="1.9.10" />
<PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.9.4" /> <PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.9.10" />
<PackageReference Include="NeoSmart.Caching.Sqlite" Version="6.1.0" /> <PackageReference Include="NeoSmart.Caching.Sqlite" Version="6.1.0" />
<PackageReference Include="RethinkDB.DistributedCache" Version="1.0.0-rc1" /> <PackageReference Include="RethinkDB.DistributedCache" Version="1.0.0-rc1" />
<PackageReference Include="System.ServiceModel.Syndication" Version="7.0.0" /> <PackageReference Include="System.ServiceModel.Syndication" Version="7.0.0" />