/// Utility functions for manipulating data
[]
module internal MyWebLog.Data.Utils
open MyWebLog
open MyWebLog.ViewModels
/// Create a category hierarchy from the given list of categories
/// The categories from which the list should be generated
/// The ID of the parent category for this list
/// The base URL to use in slugs for categories at this level
/// The names of parent categories for this level
/// An array of DisplayCategory instances sorted alphabetically by parent category
let rec orderByHierarchy (cats: Category list) parentId slugBase parentNames = seq {
for cat in cats |> List.filter (fun c -> c.ParentId = parentId) do
let fullSlug = (match slugBase with Some it -> $"{it}/" | None -> "") + cat.Slug
{ Id = string cat.Id
Slug = fullSlug
Name = cat.Name
Description = cat.Description
ParentNames = Array.ofList parentNames
// Post counts are filled on a second pass
PostCount = 0 }
yield! orderByHierarchy cats (Some cat.Id) (Some fullSlug) ([ cat.Name ] |> List.append parentNames)
}
/// Get lists of items removed from and added to the given lists
/// The type of items in the list
/// The return type of the comparision function
/// The prior list
/// The current list
/// The function to use when comparing items in the list
/// A tuple with fst being added items and snd being removed items
let diffLists<'T, 'U when 'U: equality> oldItems newItems (f: 'T -> 'U) =
let diff compList = fun item -> not (compList |> List.exists (fun other -> f item = f other))
List.filter (diff newItems) oldItems, List.filter (diff oldItems) newItems
/// Find the revisions added and removed
/// The previous revisions
/// The current revisions
/// A tuple with fst being added revisions and snd being removed revisions
let diffRevisions (oldRevs: Revision list) newRevs =
diffLists oldRevs newRevs (fun rev -> $"{rev.AsOf.ToUnixTimeTicks()}|{rev.Text}")
open MyWebLog.Converters
open Newtonsoft.Json
/// Serialize an object to JSON
/// The type of the item being serialized
/// The JSON serializer whose settings should be used
/// The item to be serialized
/// A string with the given object serialized to JSON
let serialize<'T> ser (item: 'T) =
JsonConvert.SerializeObject(item, Json.settings ser)
/// Deserialize a JSON string
/// The type of the item being deserialized
/// The JSON serializer whose settings should be used
/// The string with the JSON representation of the item
/// The item deserialized from JSON
let deserialize<'T> (ser: JsonSerializer) value =
JsonConvert.DeserializeObject<'T>(value, Json.settings ser)
open BitBadger.Documents
/// Create a document serializer using the given JsonSerializer
/// The JSON.NET serializer on which the document serializer should be based
/// A document serializer instance
let createDocumentSerializer ser =
{ new IDocumentSerializer with
member _.Serialize<'T>(it: 'T) : string = serialize ser it
member _.Deserialize<'T>(it: string) : 'T = deserialize ser it
}
/// Data migration utilities
module Migration =
open Microsoft.Extensions.Logging
/// The current database version
let currentDbVersion = "v3"
/// Log a migration step
/// The logger to which the message should be logged
/// The migration being run
/// The log message
let logStep<'T> (log: ILogger<'T>) migration message =
log.LogInformation $"Migrating %s{migration}: %s{message}"
/// Notify the user that a backup/restore is required to migrate
/// The logger to which the message should be logged
/// The old (current) version of the database
/// The new (application) version required
/// All web logs contained in the database
let backupAndRestoreRequired log oldVersion newVersion webLogs =
logStep log $"%s{oldVersion} to %s{newVersion}" "Requires Using Action"
[ "** MANUAL DATABASE UPGRADE REQUIRED **"; ""
$"The data structure changed between {oldVersion} and {newVersion}."
"To migrate your data:"
$" - Use a {oldVersion} executable to back up each web log"
" - Drop all tables from the database"
" - Use this executable to restore each backup"; ""
"Commands to back up all web logs:"
yield! webLogs |> List.map (fun (url, slug) -> $"./myWebLog backup %s{url} {oldVersion}.%s{slug}.json") ]
|> String.concat "\n"
|> log.LogWarning
log.LogCritical "myWebLog will now exit"
exit 1 |> ignore