/// 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