diff --git a/src/MyWebLog.Data/Converters.fs b/src/MyWebLog.Data/Converters.fs index d93f8de..4d9aa3d 100644 --- a/src/MyWebLog.Data/Converters.fs +++ b/src/MyWebLog.Data/Converters.fs @@ -9,6 +9,14 @@ module Json = open Newtonsoft.Json + /// Converter for the type + type AccessLevelConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: AccessLevel, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: AccessLevel, _: bool, _: JsonSerializer) = + (string >> AccessLevel.Parse) reader.Value + /// Converter for the type type AutoHtmxTypeConverter() = inherit JsonConverter() @@ -195,7 +203,8 @@ module Json = /// Configure a serializer to use these converters (and other settings) let configure (ser: JsonSerializer) = // Our converters - [ AutoHtmxTypeConverter() :> JsonConverter + [ AccessLevelConverter() :> JsonConverter + AutoHtmxTypeConverter() CategoryIdConverter() CommentIdConverter() CommentStatusConverter() diff --git a/src/MyWebLog.Data/Postgres/PostgresPersonData.fs b/src/MyWebLog.Data/Postgres/PostgresPersonData.fs index 28ae298..1e9fd90 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPersonData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPersonData.fs @@ -22,7 +22,7 @@ type PostgresPersonData(log: ILogger) = Find.byFieldsOrdered Table.Person All - [ Field.In (nameof Person.Empty.Id) (assignments |> List.map _.PersonId) ] + [ Field.In (nameof Person.Empty.Id) (assignments |> List.map (fun it -> string it.PersonId)) ] [ Field.Named (nameof Person.Empty.Name) ] /// Find a person by their ID for the given web log @@ -46,7 +46,7 @@ type PostgresPersonData(log: ILogger) = /// Find all persons for the given web log let findByWebLog webLogId = log.LogTrace "Person.findByWebLog" - Find.byContainsOrdered Table.Person (webLogContains webLogId) [ Field.Named (nameof Person.Empty.Name) ] + Find.byContainsOrdered Table.Person (webLogDoc webLogId) [ Field.Named (nameof Person.Empty.Name) ] /// Restore persons from a backup diff --git a/src/MyWebLog.Data/SQLite/SQLitePersonData.fs b/src/MyWebLog.Data/SQLite/SQLitePersonData.fs index 04ed271..2477084 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePersonData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePersonData.fs @@ -20,7 +20,7 @@ type SQLitePersonData(conn: SqliteConnection, log: ILogger) = log.LogTrace "Person.findByAssignment" conn.findByFieldsOrdered Table.Person All - [ Field.In "PersonId" (assignments |> List.map _.PersonId) ] + [ Field.In (nameof Person.Empty.Id) (assignments |> List.map (fun it -> string it.PersonId)) ] [ Field.Named (nameof Person.Empty.Name) ] /// Find a person by their ID diff --git a/src/MyWebLog.Domain/PodcastTypes.fs b/src/MyWebLog.Domain/PodcastTypes.fs index 68dd9e8..39025cf 100644 --- a/src/MyWebLog.Domain/PodcastTypes.fs +++ b/src/MyWebLog.Domain/PodcastTypes.fs @@ -414,7 +414,7 @@ type PersonId = /// Create a new person ID /// A new person ID - static member Create = + static member Create() = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-')[..21] |> PersonId /// diff --git a/src/MyWebLog.Domain/SupportTypes.fs b/src/MyWebLog.Domain/SupportTypes.fs index 84da758..57ec358 100644 --- a/src/MyWebLog.Domain/SupportTypes.fs +++ b/src/MyWebLog.Domain/SupportTypes.fs @@ -88,7 +88,9 @@ type AccessLevel = /// If the string is not valid static member Parse level = match level with + // Legacy name; superseded by Author on PersonRole | "Author" -> ContentAuthor + | "ContentAuthor" -> ContentAuthor | "Editor" -> Editor | "WebLogAdmin" -> WebLogAdmin | "Administrator" -> Administrator @@ -97,7 +99,7 @@ type AccessLevel = /// override this.ToString() = match this with - | ContentAuthor -> "Author" + | ContentAuthor -> "ContentAuthor" | Editor -> "Editor" | WebLogAdmin -> "WebLogAdmin" | Administrator -> "Administrator" diff --git a/src/MyWebLog.Tests/Data/ConvertersTests.fs b/src/MyWebLog.Tests/Data/ConvertersTests.fs index 6283a3c..bb9acf1 100644 --- a/src/MyWebLog.Tests/Data/ConvertersTests.fs +++ b/src/MyWebLog.Tests/Data/ConvertersTests.fs @@ -6,6 +6,20 @@ open MyWebLog open MyWebLog.Converters.Json open Newtonsoft.Json +/// Unit tests for the AutoHtmxTypeConverter type +let accessLevelConverterTests = testList "AccessLevelConverter" [ + let opts = JsonSerializerSettings() + opts.Converters.Add(AccessLevelConverter()) + test "succeeds when serializing" { + let after = JsonConvert.SerializeObject(Editor, opts) + Expect.equal after "\"Editor\"" "Access Level serialized incorrectly" + } + test "succeeds when deserializing" { + let after = JsonConvert.DeserializeObject("\"Author\"", opts) + Expect.equal after ContentAuthor "Access Level deserialized incorrectly" + } +] + /// Unit tests for the AutoHtmxTypeConverter type let autoHtmxTypeConverterTests = testList "AutoHtmxTypeConverter" [ let opts = JsonSerializerSettings() @@ -324,6 +338,7 @@ open NodaTime.Serialization.JsonNet let configureTests = test "Json.configure succeeds" { let has typ (converter: JsonConverter) = converter.GetType() = typ let ser = configure (JsonSerializer.Create()) + Expect.hasCountOf ser.Converters 1u (has typeof) "Access Level converter not found" Expect.hasCountOf ser.Converters 1u (has typeof) "Auto htmx type converter not found" Expect.hasCountOf ser.Converters 1u (has typeof) "Category ID converter not found" Expect.hasCountOf ser.Converters 1u (has typeof) "Comment ID converter not found" @@ -354,6 +369,7 @@ let configureTests = test "Json.configure succeeds" { /// All tests for the Data.Converters file let all = testList "Converters" [ + accessLevelConverterTests autoHtmxTypeConverterTests categoryIdConverterTests commentIdConverterTests diff --git a/src/MyWebLog.Tests/Data/PersonDataTests.fs b/src/MyWebLog.Tests/Data/PersonDataTests.fs new file mode 100644 index 0000000..ad8c37c --- /dev/null +++ b/src/MyWebLog.Tests/Data/PersonDataTests.fs @@ -0,0 +1,104 @@ +/// Integration tests for implementations +module PersonDataTests + +open System +open Expecto +open MyWebLog +open MyWebLog.Data + +/// The ID of the root web log +let private rootId = CategoryDataTests.rootId + +/// The person ID for "Crackpot" +let private crackpotPersonId = PersonId "Q-6ElUj4cEyg5_wQeMpH_A" + +/// The person ID for "Buzzkill" (RIP, JCD) +let private buzzkillPersonId = PersonId "a8M4gN58R0evXnJEt9j95w" + +let ``Add succeeds`` (data: IData) = task { + let person = + { Id = PersonId "person-x" + WebLogId = WebLogId "web-log-x" + Name = "Anonny Moose" + Url = None + ImageUrl = Some "sunglasses.jpg" } + do! data.Person.Add person + let! stored = data.Person.FindById person.Id person.WebLogId + Expect.isSome stored "The added person should have been retrieved" + let it = stored.Value + Expect.equal it.Id person.Id "ID not saved properly" + Expect.equal it.WebLogId person.WebLogId "Web Log ID not saved properly" + Expect.equal it.Name person.Name "Name not saved properly" + Expect.equal it.Url person.Url "URL not saved properly" + Expect.equal it.ImageUrl person.ImageUrl "Image URL not saved properly" +} + +let ``FindByAssignment succeeds when assignments are found`` (data: IData) = task { + let assignments = + [ { PersonId = crackpotPersonId; Role = Producer }; { PersonId = buzzkillPersonId; Role = Producer } ] + let! people = data.Person.FindByAssignment assignments + Expect.hasLength people 2 "There should have been two people returned" + Expect.equal (people |> List.head).Name "Buzzkill" "The list was not sorted correctly" +} + +let ``FindByAssignment succeeds when no assignments are found`` (data: IData) = task { + let assignments = + [ { PersonId = PersonId "missing"; Role = Guest } + { PersonId = PersonId "still-missing"; Role = GuestHost } ] + let! people = data.Person.FindByAssignment assignments + Expect.hasLength people 0 "There should not have been any people returned" +} + +let ``FindById succeeds when a person is found`` (data: IData) = task { + let! person = data.Person.FindById buzzkillPersonId rootId + Expect.isSome person "There should have been a person returned" + let it = person.Value + Expect.equal it.Id buzzkillPersonId "ID retrieved incorrectly" + Expect.equal it.WebLogId rootId "Web Log ID retrieved incorrectly" + Expect.equal it.Name "Buzzkill" "Name retrieved incorrectly" + Expect.isNone it.Url "URL should have been blank" + Expect.isNone it.ImageUrl "Image URL should have been blank" +} + +let ``FindById succeeds when a person is not found (incorrect weblog)`` (data: IData) = task { + let! person = data.Person.FindById crackpotPersonId (WebLogId "incorrect") + Expect.isNone person "There should not have been a person returned" +} + +let ``FindById succeeds when a person is not found (bad person ID)`` (data: IData) = task { + let! person = data.Person.FindById (PersonId "bad") rootId + Expect.isNone person "There should not have been a person returned" +} + +let ``FindByWebLog succeeds when people are found`` (data: IData) = task { + let! people = data.Person.FindByWebLog rootId + Expect.hasLength people 2 "There should have been 2 people returned" + Expect.equal (people |> List.head).Name "Buzzkill" "The people were not sorted correctly" +} + +let ``FindByWebLog succeeds when no people are found`` (data: IData) = task { + let! people = data.Person.FindByWebLog (WebLogId "no-folks") + Expect.hasLength people 0 "There should have been no people returned" +} + +let ``Update succeeds when person is updated`` (data: IData) = task { + let! person = data.Person.FindById crackpotPersonId rootId + Expect.isSome person "The original person should have been returned" + do! data.Person.Update { person.Value with Name = "Crackpot!" } + let! updated = data.Person.FindById crackpotPersonId rootId + Expect.isSome updated "The updated person should have been returned" + Expect.equal updated.Value.Name "Crackpot!" "Name was not updated" + // Put the name back + do! data.Person.Update person.Value +} + +let ``Update succeeds when person is not updated`` (data: IData) = task { + let! person = data.Person.FindById crackpotPersonId rootId + Expect.isSome person "The original person should have been returned" + do! data.Person.Update { person.Value with Name = "New Name"; WebLogId = WebLogId "invalid" } + let! updated = data.Person.FindById crackpotPersonId rootId + Expect.isSome updated "The updated person should have been returned" + Expect.equal updated.Value.Name "Crackpot" "The update should not have occurred" +} + +// TODO: write delete tests when they cover podcasts and episodes as well diff --git a/src/MyWebLog.Tests/Data/PostDataTests.fs b/src/MyWebLog.Tests/Data/PostDataTests.fs index 20f23ed..43bdd8f 100644 --- a/src/MyWebLog.Tests/Data/PostDataTests.fs +++ b/src/MyWebLog.Tests/Data/PostDataTests.fs @@ -1,6 +1,4 @@ -/// -/// Integration tests for implementations -/// +/// Integration tests for implementations module PostDataTests open System @@ -39,6 +37,12 @@ let private testPost1Published = Instant.FromDateTimeOffset(DateTimeOffset.Parse /// The category IDs for "Spitball" (parent) and "Moonshot" let private testCatIds = [ CategoryId "jw6N69YtTEWVHAO33jHU-w"; CategoryId "ScVpyu1e7UiP7bDdge3ZEw" ] +/// The person ID for "Crackpot" +let private crackpotPersonId = PersonId "Q-6ElUj4cEyg5_wQeMpH_A" + +/// The person ID for "Buzzkill" (RIP, JCD) +let private buzzkillPersonId = PersonId "a8M4gN58R0evXnJEt9j95w" + /// Ensure that a list of posts has text for each post let private ensureHasText (posts: Post list) = for post in posts do Expect.isNotEmpty post.Text $"Text should not be blank (post ID {post.Id})" @@ -129,6 +133,11 @@ let ``FindById succeeds when a post is found`` (data: IData) = task { Expect.equal ep.SeasonDescription (Some "The First Season") "Episode season description is incorrect" Expect.equal ep.EpisodeNumber (Some 1.) "Episode number is incorrect" Expect.equal ep.EpisodeDescription (Some "The first episode ever!") "Episode description is incorrect" + Expect.isSome ep.People "There should have been people on the episode" + let ppl = ep.People.Value + Expect.hasLength ppl 2 "There should have been 2 people on the episode" + Expect.contains ppl { PersonId = crackpotPersonId; Role = CoHost } "Crackpot not found on episode" + Expect.contains ppl { PersonId = buzzkillPersonId; Role = VoiceActor } "Buzzkill not found on episode" Expect.equal it.Metadata [ { Name = "Density"; Value = "Non-existent" }; { Name = "Intensity"; Value = "Low" } ] diff --git a/src/MyWebLog.Tests/Data/PostgresDataTests.fs b/src/MyWebLog.Tests/Data/PostgresDataTests.fs index 690559f..715aa42 100644 --- a/src/MyWebLog.Tests/Data/PostgresDataTests.fs +++ b/src/MyWebLog.Tests/Data/PostgresDataTests.fs @@ -224,6 +224,40 @@ let private pageTests = testList "Page" [ ] ] +/// Integration tests for the Person implementation in PostgreSQL +let private personTests = testList "Person" [ + testTask "Add succeeds" { + do! PersonDataTests.``Add succeeds`` (mkData ()) + } + testTask "FindByAssignment succeeds when assignments are found" { + do! PersonDataTests.``FindByAssignment succeeds when assignments are found`` (mkData ()) + } + testTask "FindByAssignment succeeds when no assignments are found" { + do! PersonDataTests.``FindByAssignment succeeds when no assignments are found`` (mkData ()) + } + testTask "FindById succeeds when a person is found" { + do! PersonDataTests.``FindById succeeds when a person is found`` (mkData ()) + } + testTask "FindById succeeds when a person is not found (incorrect weblog)" { + do! PersonDataTests.``FindById succeeds when a person is not found (incorrect weblog)`` (mkData ()) + } + testTask "FindById succeeds when a person is not found (bad person ID)" { + do! PersonDataTests.``FindById succeeds when a person is not found (bad person ID)`` (mkData ()) + } + testTask "FindByWebLog succeeds when people are found" { + do! PersonDataTests.``FindByWebLog succeeds when people are found`` (mkData ()) + } + testTask "FindByWebLog succeeds when no people are found" { + do! PersonDataTests.``FindByWebLog succeeds when no people are found`` (mkData ()) + } + testTask "Update succeeds when person is updated" { + do! PersonDataTests.``Update succeeds when person is updated`` (mkData ()) + } + testTask "Update succeeds when person is not updated" { + do! PersonDataTests.``Update succeeds when person is not updated`` (mkData ()) + } +] + /// Integration tests for the Post implementation in PostgreSQL let private postTests = testList "Post" [ testTask "Add succeeds" { @@ -711,6 +745,7 @@ let all = [ environmentSetUp categoryTests pageTests + personTests postTests tagMapTests themeTests diff --git a/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs b/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs index 19cf1cf..7710d24 100644 --- a/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs +++ b/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs @@ -218,6 +218,40 @@ let private pageTests = testList "Page" [ ] ] +/// Integration tests for the Post implementation in RethinkDB +let private personTests = testList "Person" [ + testTask "Add succeeds" { + do! PersonDataTests.``Add succeeds`` data.Value + } + testTask "FindByAssignment succeeds when assignments are found" { + do! PersonDataTests.``FindByAssignment succeeds when assignments are found`` data.Value + } + testTask "FindByAssignment succeeds when no assignments are found" { + do! PersonDataTests.``FindByAssignment succeeds when no assignments are found`` data.Value + } + testTask "FindById succeeds when a person is found" { + do! PersonDataTests.``FindById succeeds when a person is found`` data.Value + } + testTask "FindById succeeds when a person is not found (incorrect weblog)" { + do! PersonDataTests.``FindById succeeds when a person is not found (incorrect weblog)`` data.Value + } + testTask "FindById succeeds when a person is not found (bad person ID)" { + do! PersonDataTests.``FindById succeeds when a person is not found (bad person ID)`` data.Value + } + testTask "FindByWebLog succeeds when people are found" { + do! PersonDataTests.``FindByWebLog succeeds when people are found`` data.Value + } + testTask "FindByWebLog succeeds when no people are found" { + do! PersonDataTests.``FindByWebLog succeeds when no people are found`` data.Value + } + testTask "Update succeeds when person is updated" { + do! PersonDataTests.``Update succeeds when person is updated`` data.Value + } + testTask "Update succeeds when person is not updated" { + do! PersonDataTests.``Update succeeds when person is not updated`` data.Value + } +] + /// Integration tests for the Post implementation in RethinkDB let private postTests = testList "Post" [ testTask "Add succeeds" { @@ -693,6 +727,7 @@ let all = [ environmentSetUp categoryTests pageTests + personTests postTests tagMapTests themeTests diff --git a/src/MyWebLog.Tests/Data/SQLiteDataTests.fs b/src/MyWebLog.Tests/Data/SQLiteDataTests.fs index 50e74bf..53fd20f 100644 --- a/src/MyWebLog.Tests/Data/SQLiteDataTests.fs +++ b/src/MyWebLog.Tests/Data/SQLiteDataTests.fs @@ -326,6 +326,60 @@ let private pageTests = testList "Page" [ ] ] +/// Integration tests for the Person implementation in SQLite +let private personTests = testList "Person" [ + testTask "Add succeeds" { + let data = mkData () + try do! PersonDataTests.``Add succeeds`` data + finally dispose data + } + testTask "FindByAssignment succeeds when assignments are found" { + let data = mkData () + try do! PersonDataTests.``FindByAssignment succeeds when assignments are found`` data + finally dispose data + } + testTask "FindByAssignment succeeds when no assignments are found" { + let data = mkData () + try do! PersonDataTests.``FindByAssignment succeeds when no assignments are found`` data + finally dispose data + } + testTask "FindById succeeds when a person is found" { + let data = mkData () + try do! PersonDataTests.``FindById succeeds when a person is found`` data + finally dispose data + } + testTask "FindById succeeds when a person is not found (incorrect weblog)" { + let data = mkData () + try do! PersonDataTests.``FindById succeeds when a person is not found (incorrect weblog)`` data + finally dispose data + } + testTask "FindById succeeds when a person is not found (bad person ID)" { + let data = mkData () + try do! PersonDataTests.``FindById succeeds when a person is not found (bad person ID)`` data + finally dispose data + } + testTask "FindByWebLog succeeds when people are found" { + let data = mkData () + try do! PersonDataTests.``FindByWebLog succeeds when people are found`` data + finally dispose data + } + testTask "FindByWebLog succeeds when no people are found" { + let data = mkData () + try do! PersonDataTests.``FindByWebLog succeeds when no people are found`` data + finally dispose data + } + testTask "Update succeeds when person is updated" { + let data = mkData () + try do! PersonDataTests.``Update succeeds when person is updated`` data + finally dispose data + } + testTask "Update succeeds when person is not updated" { + let data = mkData () + try do! PersonDataTests.``Update succeeds when person is not updated`` data + finally dispose data + } +] + /// Integration tests for the Post implementation in SQLite let private postTests = testList "Post" [ testTask "Add succeeds" { @@ -1043,6 +1097,7 @@ let all = [ environmentSetUp categoryTests pageTests + personTests postTests tagMapTests themeTests diff --git a/src/MyWebLog.Tests/Data/WebLogDataTests.fs b/src/MyWebLog.Tests/Data/WebLogDataTests.fs index 3dcbd85..3c7646d 100644 --- a/src/MyWebLog.Tests/Data/WebLogDataTests.fs +++ b/src/MyWebLog.Tests/Data/WebLogDataTests.fs @@ -1,6 +1,4 @@ -/// -/// Integration tests for implementations -/// +/// Integration tests for implementations module WebLogDataTests open System @@ -122,6 +120,11 @@ let ``FindById succeeds when a web log is found`` (data: IData) = task { Expect.equal pod.FundingUrl (Some "https://example.com/support-us") "Podcast funding URL is incorrect" Expect.equal pod.FundingText (Some "Support Our Work") "Podcast funding text is incorrect" Expect.equal pod.Medium (Some Newsletter) "Podcast medium is incorrect" + Expect.isSome pod.People "There should have been people associated with the podcast" + let ppl = pod.People.Value + Expect.hasLength ppl 2 "There should have been 2 people associatged with the podcast" + Expect.contains ppl { PersonId = PersonId "Q-6ElUj4cEyg5_wQeMpH_A"; Role = Host } "Crackpot not found" + Expect.contains ppl { PersonId = PersonId "a8M4gN58R0evXnJEt9j95w"; Role = Host } "Buzzkill not found :(" } let ``FindById succeeds when a web log is not found`` (data: IData) = task { diff --git a/src/MyWebLog.Tests/Domain/SupportTypesTests.fs b/src/MyWebLog.Tests/Domain/SupportTypesTests.fs index 217dccd..4470a3f 100644 --- a/src/MyWebLog.Tests/Domain/SupportTypesTests.fs +++ b/src/MyWebLog.Tests/Domain/SupportTypesTests.fs @@ -33,6 +33,10 @@ let accessLevelTests = testList "AccessLevel" [ test "succeeds for \"Author\"" { Expect.equal (AccessLevel.Parse "Author") AccessLevel.ContentAuthor "Author not parsed correctly" } + test "succeeds for \"ContentAuthor\"" { + Expect.equal + (AccessLevel.Parse "ContentAuthor") AccessLevel.ContentAuthor "ContentAuthor not parsed correctly" + } test "succeeds for \"Editor\"" { Expect.equal (AccessLevel.Parse "Editor") Editor "Editor not parsed correctly" } @@ -49,7 +53,7 @@ let accessLevelTests = testList "AccessLevel" [ ] testList "ToString" [ test "ContentAuthor succeeds" { - Expect.equal (string AccessLevel.ContentAuthor) "Author" "ContentAuthor string incorrect" + Expect.equal (string AccessLevel.ContentAuthor) "ContentAuthor" "ContentAuthor string incorrect" } test "Editor succeeds" { Expect.equal (string Editor) "Editor" "Editor string incorrect" diff --git a/src/MyWebLog.Tests/MyWebLog.Tests.fsproj b/src/MyWebLog.Tests/MyWebLog.Tests.fsproj index f5bdaf5..182b8b9 100644 --- a/src/MyWebLog.Tests/MyWebLog.Tests.fsproj +++ b/src/MyWebLog.Tests/MyWebLog.Tests.fsproj @@ -13,6 +13,7 @@ + diff --git a/src/MyWebLog.Tests/root-weblog.json b/src/MyWebLog.Tests/root-weblog.json index b9f6e15..cb3e89b 100644 --- a/src/MyWebLog.Tests/root-weblog.json +++ b/src/MyWebLog.Tests/root-weblog.json @@ -36,7 +36,17 @@ "PodcastGuid": "10fd7f79-c719-4e1d-9da7-10405dd4fd96", "FundingUrl": "https://example.com/support-us", "FundingText": "Support Our Work", - "Medium": "newsletter" + "Medium": "newsletter", + "People": [ + { + "PersonId": "Q-6ElUj4cEyg5_wQeMpH_A", + "Role": "Host" + }, + { + "PersonId": "a8M4gN58R0evXnJEt9j95w", + "Role": "Host" + } + ] } } ] @@ -200,6 +210,19 @@ ] } ], + "People": [ + { + "Id": "Q-6ElUj4cEyg5_wQeMpH_A", + "WebLogId": "uSitJEuD3UyzWC9jgOHc8g", + "Name": "Crackpot", + "Url": "bio.html" + }, + { + "Id": "a8M4gN58R0evXnJEt9j95w", + "WebLogId": "uSitJEuD3UyzWC9jgOHc8g", + "Name": "Buzzkill" + } + ], "Posts": [ { "Id": "RCsCU2puYEmkpzotoi8p4g", @@ -260,7 +283,17 @@ "SeasonNumber": 1, "SeasonDescription": "The First Season", "EpisodeNumber": 1.0, - "EpisodeDescription": "The first episode ever!" + "EpisodeDescription": "The first episode ever!", + "People": [ + { + "PersonId": "Q-6ElUj4cEyg5_wQeMpH_A", + "Role": "Co-Host" + }, + { + "PersonId": "a8M4gN58R0evXnJEt9j95w", + "Role": "Voice Actor" + } + ] }, "Metadata": [ { diff --git a/src/MyWebLog/Maintenance.fs b/src/MyWebLog/Maintenance.fs index c49c5d5..109dd2c 100644 --- a/src/MyWebLog/Maintenance.fs +++ b/src/MyWebLog/Maintenance.fs @@ -229,6 +229,9 @@ module Backup = /// The pages for this web log (containing only the most recent revision) Pages: Page list + /// The person entries for this web log + People: Person list + /// The posts for this web log (containing only the most recent revision) Posts: Post list @@ -249,6 +252,7 @@ module Backup = let categoryCount = List.length archive.Categories let tagMapCount = List.length archive.TagMappings let pageCount = List.length archive.Pages + let personCount = List.length archive.People let postCount = List.length archive.Posts let uploadCount = List.length archive.Uploads @@ -263,6 +267,7 @@ module Backup = printfn $""" - {categoryCount} categor{plural categoryCount "y" "ies"}""" printfn $""" - {tagMapCount} tag mapping{plural tagMapCount "" "s"}""" printfn $""" - {pageCount} page{plural pageCount "" "s"}""" + printfn $""" - {personCount} people""" printfn $""" - {postCount} post{plural postCount "" "s"}""" printfn $""" - {uploadCount} uploaded file{plural uploadCount "" "s"}""" @@ -283,6 +288,9 @@ module Backup = printfn "- Exporting pages..." let! pages = data.Page.FindFullByWebLog webLog.Id + printfn "- Exporting people..." + let! people = data.Person.FindByWebLog webLog.Id + printfn "- Exporting posts..." let! posts = data.Post.FindFullByWebLog webLog.Id @@ -298,6 +306,7 @@ module Backup = Categories = categories TagMappings = tagMaps Pages = pages |> List.map (fun p -> { p with Revisions = List.truncate 1 p.Revisions }) + People = people Posts = posts |> List.map (fun p -> { p with Revisions = List.truncate 1 p.Revisions }) Uploads = uploads |> List.map EncodedUpload.fromUpload } @@ -319,15 +328,17 @@ module Backup = return { archive with Archive.WebLog.UrlBase = defaultArg newUrlBase webLog.UrlBase } | Some _ -> // Err'body gets new IDs... - let newWebLogId = WebLogId.Create() - let newCatIds = archive.Categories |> List.map (fun cat -> cat.Id, CategoryId.Create() ) |> dict - let newMapIds = archive.TagMappings |> List.map (fun tm -> tm.Id, TagMapId.Create() ) |> dict - let newPageIds = archive.Pages |> List.map (fun page -> page.Id, PageId.Create() ) |> dict - let newPostIds = archive.Posts |> List.map (fun post -> post.Id, PostId.Create() ) |> dict - let newUserIds = archive.Users |> List.map (fun user -> user.Id, WebLogUserId.Create()) |> dict - let newUpIds = archive.Uploads |> List.map (fun up -> up.Id, UploadId.Create() ) |> dict + let newWebLogId = WebLogId.Create() + let newCatIds = archive.Categories |> List.map (fun cat -> cat.Id, CategoryId.Create() ) |> dict + let newMapIds = archive.TagMappings |> List.map (fun tm -> tm.Id, TagMapId.Create() ) |> dict + let newPageIds = archive.Pages |> List.map (fun page -> page.Id, PageId.Create() ) |> dict + let newPersonIds = archive.People |> List.map (fun psn -> psn.Id, PersonId.Create() ) |> dict + let newPostIds = archive.Posts |> List.map (fun post -> post.Id, PostId.Create() ) |> dict + let newUserIds = archive.Users |> List.map (fun user -> user.Id, WebLogUserId.Create()) |> dict + let newUpIds = archive.Uploads |> List.map (fun up -> up.Id, UploadId.Create() ) |> dict return { archive with + // TODO: person IDs should be updated in podcasts and episodes WebLog = { archive.WebLog with Id = newWebLogId; UrlBase = Option.get newUrlBase } Users = archive.Users |> List.map (fun u -> { u with Id = newUserIds[u.Id]; WebLogId = newWebLogId }) @@ -341,6 +352,9 @@ module Backup = Id = newPageIds[page.Id] WebLogId = newWebLogId AuthorId = newUserIds[page.AuthorId] }) + People = archive.People + |> List.map (fun person -> + { person with Id = newPersonIds[person.Id]; WebLogId = newWebLogId }) Posts = archive.Posts |> List.map (fun post -> { post with @@ -381,6 +395,9 @@ module Backup = if isInteractive then printfn "- Restoring pages..." if not (List.isEmpty restore.Pages) then do! data.Page.Restore restore.Pages + if isInteractive then printfn "- Restoring people..." + if not (List.isEmpty restore.People) then do! data.Person.Restore restore.People + if isInteractive then printfn "- Restoring posts..." if not (List.isEmpty restore.Posts) then do! data.Post.Restore restore.Posts