From c38bc9a90c38f6111e6a25dd5f1e2204deaff0c3 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:18:54 -0800 Subject: [PATCH 01/97] Update to .NET Standard 2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .NET Standard 2.1 is no longer compatible with .NET Framework—but OnTopic 5.0.0 won't be either, so this is an expected limitation. Moving forward, OnTopic will be targeting .NET Core 3.x and .NET 5.x, both of which are compatible with .NET Standard 2.1. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 7239048..4205cab 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -2,7 +2,7 @@ OnTopic.Data.Transfer - netstandard2.0 + netstandard2.1 9.0 enable latest From 1136f1dfff0a7a61328e6f33dd1046fef35fcdd7 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:19:46 -0800 Subject: [PATCH 02/97] Updated to OnTopic 5.0.0 release candidate We'll need to upgrade to the final version of OnTopic 5.0.0 once it releases. Until then, however, this allows us to begin integration testing and development of new features dependent on OnTopic 5.0.0 capabilities, such as Topic References. --- OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj | 2 +- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index c309c54..f9e416a 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -20,7 +20,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 4205cab..d378ef9 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 6fa8f55deefaed6a58fa17253e0b2761ef5c269f Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:30:53 -0800 Subject: [PATCH 03/97] Updated calls to `Topic.Relationships` to use `GetValues()`, `SetValue()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In OnTopic 5.0.0, the legacy entry points to the `TopicRelationshipMultiMap`—as exposed by `Topic.Relationships`—have been changed from `GetTopics()` and `SetTopic()` to `GetValues()` and `SetValue()`. While not as specific, this generalization of the nomenclature unifies the terminology across `Topic.Attributes`, `Topic.References`, and `Topic.Relationships`, and is more consistent with e.g. `TryGetValue()` used by the .NET Framework for similar types of operations. --- OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs | 2 +- OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs | 12 ++++++------ OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 12 ++++++------ OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 3a6c024..a7c8534 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -86,7 +86,7 @@ public void Export_TopicWithRelationships_ExcludesExternalReferences() { var topic = TopicFactory.Create("Test", "Container", rootTopic); var relatedTopic = TopicFactory.Create("Related", "Container", rootTopic); - topic.Relationships.SetTopic("Related", relatedTopic); + topic.Relationships.SetValue("Related", relatedTopic); var topicData = topic.Export(); diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index 22481dd..94311b4 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -186,9 +186,9 @@ public void ImportAsReplace_TopicDataWithRelationships_DeletesOrphanedRelationsh Key = "Related" }; - topic.Relationships.SetTopic("Related", relatedTopic1); - topic.Relationships.SetTopic("Related", relatedTopic2); - topic.Relationships.SetTopic("Cousin", relatedTopic3); + topic.Relationships.SetValue("Related", relatedTopic1); + topic.Relationships.SetValue("Related", relatedTopic2); + topic.Relationships.SetValue("Cousin", relatedTopic3); topicData.Relationships.Add(relationshipData); relationshipData.Relationships.Add(relatedTopic1.GetUniqueKey()); @@ -200,9 +200,9 @@ public void ImportAsReplace_TopicDataWithRelationships_DeletesOrphanedRelationsh } ); - Assert.AreEqual(relatedTopic1, topic.Relationships.GetTopics("Related")?.FirstOrDefault()); - Assert.AreEqual(1, topic.Relationships.GetTopics("Related").Count); - Assert.AreEqual(0, topic.Relationships.GetTopics("Cousin").Count); + Assert.AreEqual(relatedTopic1, topic.Relationships.GetValues("Related")?.FirstOrDefault()); + Assert.AreEqual(1, topic.Relationships.GetValues("Related").Count); + Assert.AreEqual(0, topic.Relationships.GetValues("Cousin").Count); } diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 198c92f..e7f77e3 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -96,7 +96,7 @@ public void Export_TopicWithRelationships_MapsRelationshipDataCollection() { var topic = TopicFactory.Create("Test", "Container", rootTopic); var relatedTopic = TopicFactory.Create("Related", "Container", rootTopic); - topic.Relationships.SetTopic("Related", relatedTopic); + topic.Relationships.SetValue("Related", relatedTopic); var topicData = rootTopic.Export( new() { @@ -129,7 +129,7 @@ public void ExportWithExternalReferences_TopicWithRelationships_ExcludesExternal var topic = TopicFactory.Create("Test", "Container", rootTopic); var relatedTopic = TopicFactory.Create("Related", "Container", rootTopic); - topic.Relationships.SetTopic("Related", relatedTopic); + topic.Relationships.SetValue("Related", relatedTopic); var topicData = topic.Export( new() { @@ -530,7 +530,7 @@ public void Import_TopicDataWithRelationships_MapsRelationshipCollection() { topic.Import(topicData); - Assert.AreEqual(relatedTopic, topic.Relationships.GetTopics("Related")?.FirstOrDefault()); + Assert.AreEqual(relatedTopic, topic.Relationships.GetValues("Related")?.FirstOrDefault()); } @@ -557,15 +557,15 @@ public void Import_TopicDataWithRelationships_MaintainsExisting() { Key = "Related" }; - topic.Relationships.SetTopic("Related", relatedTopic1); + topic.Relationships.SetValue("Related", relatedTopic1); topicData.Relationships.Add(relationshipData); relationshipData.Relationships.Add(relatedTopic2.GetUniqueKey()); topic.Import(topicData); - Assert.AreEqual(relatedTopic1, topic.Relationships.GetTopics("Related")?.FirstOrDefault()); - Assert.AreEqual(relatedTopic2, topic.Relationships.GetTopics("Related")?.LastOrDefault()); + Assert.AreEqual(relatedTopic1, topic.Relationships.GetValues("Related")?.FirstOrDefault()); + Assert.AreEqual(relatedTopic2, topic.Relationships.GetValues("Related")?.LastOrDefault()); } diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 6127d07..db01547 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -194,7 +194,7 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import //Wire up relationships else { - source.Relationships.SetTopic(key, target); + source.Relationships.SetValue(key, target); } } @@ -344,7 +344,7 @@ List> unresolvedRelationships foreach (var relatedTopicKey in relationship.Relationships) { var relatedTopic = topic.GetByUniqueKey(relatedTopicKey); if (relationship.Key is not null && relatedTopic is not null) { - topic.Relationships.SetTopic(relationship.Key, relatedTopic); + topic.Relationships.SetValue(relationship.Key, relatedTopic); } else { unresolvedRelationships.Add(new(topic, relationship.Key!, relatedTopicKey)); @@ -370,7 +370,7 @@ topic.ContentType is not "List" && options.DeleteUnmatchedChildren //Update records based on the source collection foreach (var childTopicData in topicData.Children) { - var childTopic = topic?.Children.GetTopic(childTopicData.Key); + var childTopic = topic?.Children.GetValue(childTopicData.Key); if (childTopic is null) { childTopic = TopicFactory.Create(childTopicData.Key, childTopicData.ContentType, topic); } From d06399e93ddedebb88ae650821d18be3109f3c25 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:31:11 -0800 Subject: [PATCH 04/97] Updated order of parameters for `TopicFactory.Create()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The order of parameters in `TopicFactory.Create()` was changed to map to the constructor of `Topic`. Notably, this switched the order of `parent` and `id`. This usually only affects `ITopicRepository` implementations and unit tests, since most day-to-day operations won't ever be assigning an `Id`—which, consequently, is a good reason for the `Id` to go last. (Aside, there was previously an overload that allowed the `parent` to be defined without the `id`; as part of this change, that overload was merged.) --- OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs | 4 ++-- OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index 94311b4..a66371d 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -309,8 +309,8 @@ public void ImportAsReplace_TopicDataWithNestedTopic_DeletedOrphanedChildren() { var topic = TopicFactory.Create("Test", "Container"); var nestedTopics = TopicFactory.Create("Nested", "List", topic); - _ = TopicFactory.Create("Nested1", "Page", 1, nestedTopics); - var nestedTopic2 = TopicFactory.Create("Nested2", "Page", 2, nestedTopics); + _ = TopicFactory.Create("Nested1", "Page", nestedTopics, 1); + var nestedTopic2 = TopicFactory.Create("Nested2", "Page", nestedTopics, 2); var topicData = new TopicData() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index e7f77e3..4e5f00e 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -175,8 +175,8 @@ public void Export_TopicWithNestedTopics_ExcludeNestedTopics() { public void Export_ExcludesReservedAttributes() { var topic = TopicFactory.Create("Topic", "Container", 5); - _ = TopicFactory.Create("ChildA", "Container", 6, topic); - _ = TopicFactory.Create("ChildB", "Container", 7, topic); + _ = TopicFactory.Create("ChildA", "Container", topic, 6); + _ = TopicFactory.Create("ChildB", "Container", topic, 7); //Manually setting using non-standard casing to evaluate case insensitivity topic.Attributes.SetValue("parentId", "5"); @@ -315,7 +315,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var derivedTopic = TopicFactory.Create("Derived", "Container", 5, rootTopic); + var derivedTopic = TopicFactory.Create("Derived", "Container", rootTopic, 5); var topicData = new TopicData() { Key = topic.Key, @@ -388,7 +388,7 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var derivedTopic = TopicFactory.Create("Derived", "Container", 5, rootTopic); + var derivedTopic = TopicFactory.Create("Derived", "Container", rootTopic, 5); topic.DerivedTopic = derivedTopic; @@ -682,7 +682,7 @@ public void Import_TopicDataWithTopicPointer_MapsTopicID() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Topic", "Container", rootTopic); - var siblingTopic = TopicFactory.Create("SiblingTopic", "Container", 5, rootTopic); + var siblingTopic = TopicFactory.Create("SiblingTopic", "Container", rootTopic, 5); var topicData = new TopicData() { Key = topic.Key, From a01a216608066912d5e6220047397facfff78d8a Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:34:03 -0800 Subject: [PATCH 05/97] Updated reference to `AttributeValue` to new `AttributeRecord` In OnTopic 5.0.0, the name of the `AttributeValue` type is changed to `AttributeRecord` as part of a broader effort to unify the naming conventions for combining both data (i.e., `Key`, `Value`) and metadata (i.e., `IsDirty`, `LastModified`). This is also used for the new `TopicReferenceRecord` type. Further, it mitigates the awkward `AttributeValue.Value` syntax. --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index db01547..4b8023a 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -144,7 +144,7 @@ childTopic.ContentType is "List" /*------------------------------------------------------------------------------------------------------------------------ | Get attribute value \-----------------------------------------------------------------------------------------------------------------------*/ - string? getAttributeValue(AttributeValue attribute) => + string? getAttributeValue(AttributeRecord attribute) => options.TranslateTopicPointers && attribute.Key.EndsWith("ID", StringComparison.InvariantCultureIgnoreCase)? GetUniqueKey(topic, attribute.Value, options) : attribute.Value; From 55e952b5c76a04c3fc229bb38bf5b43cf4371b01 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:40:24 -0800 Subject: [PATCH 06/97] Updated references to `DerivedTopic` to new `BaseTopic` The semantics of `Topic.DerivedTopic` were not just confusing, but wrong: The "derived topic" referred to the topic that was being derived _from_,, which wasn't captured by the identifier. The new `BaseTopic` better captures that. --- .../TopicExtensionsTest.cs | 22 +++++++++---------- .../Interchange/TopicExtensions.cs | 6 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 4e5f00e..0f71467 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -49,13 +49,13 @@ public void Export_DerivedTopic_MapsDerivedTopicKey() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var derivedTopic = TopicFactory.Create("Derived", "Container", rootTopic); - topic.DerivedTopic = derivedTopic; + var baseTopic = TopicFactory.Create("Derived", "Container", rootTopic); + topic.BaseTopic = baseTopic; var topicData = topic.Export(); Assert.IsNotNull(topicData); - Assert.AreEqual(topic.DerivedTopic.GetUniqueKey(), topicData.DerivedTopicKey); + Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.DerivedTopicKey); } @@ -326,9 +326,9 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { topic.Import(topicData); - Assert.IsNotNull(topic.DerivedTopic); + Assert.IsNotNull(topic.BaseTopic); Assert.AreEqual("5", topic.Attributes.GetValue("TopicID")); - Assert.AreEqual(derivedTopic, topic.DerivedTopic); + Assert.AreEqual(derivedTopic, topic.BaseTopic); } @@ -371,8 +371,8 @@ public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { var childTopic = topic.Children.FirstOrDefault(); - Assert.IsNotNull(childTopic.DerivedTopic); - Assert.AreEqual(relatedTopicData.Key, childTopic.DerivedTopic?.Key); + Assert.IsNotNull(childTopic.BaseTopic); + Assert.AreEqual(relatedTopicData.Key, childTopic.BaseTopic?.Key); } @@ -388,9 +388,9 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var derivedTopic = TopicFactory.Create("Derived", "Container", rootTopic, 5); + var baseTopic = TopicFactory.Create("Derived", "Container", rootTopic, 5); - topic.DerivedTopic = derivedTopic; + topic.BaseTopic = baseTopic; var topicData = new TopicData() { Key = topic.Key, @@ -401,9 +401,9 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { topic.Import(topicData); - Assert.IsNotNull(topic.DerivedTopic); + Assert.IsNotNull(topic.BaseTopic); Assert.AreEqual("5", topic.Attributes.GetValue("TopicID")); - Assert.AreEqual(derivedTopic, topic.DerivedTopic); + Assert.AreEqual(baseTopic, topic.BaseTopic); } diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 4b8023a..59f7b18 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -74,7 +74,7 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - DerivedTopicKey = topic.DerivedTopic?.GetUniqueKey() + DerivedTopicKey = topic.BaseTopic?.GetUniqueKey() }; /*------------------------------------------------------------------------------------------------------------------------ @@ -189,7 +189,7 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import //Wire up derived topics if (key.Equals("DerivedTopic", StringComparison.OrdinalIgnoreCase)) { - source.DerivedTopic = target; + source.BaseTopic = target; } //Wire up relationships @@ -263,7 +263,7 @@ List> unresolvedRelationships if (topicData.DerivedTopicKey?.Length > 0) { var target = topic.GetByUniqueKey(topicData.DerivedTopicKey); if (target is not null) { - topic.DerivedTopic = target; + topic.BaseTopic = target; } else { unresolvedRelationships.Add(new(topic, "DerivedTopic", topicData.DerivedTopicKey)); From 462671b4237feb0ff43bb2efe969ea7e73eb6274 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:47:38 -0800 Subject: [PATCH 07/97] Accommodate new `KeyValuesItem` syntax As of OnTopic 5.0.0, the `Topic.Relationships` type is a new `TopicRelationshipsMultiMap`, which implements an `IReadOnlyDictionary<>` of `KeyValuesItem`s. As part of this, the name of each relationships is renamed from `Name` to `Key` (which is more consistent with `relationshipKey`) and the values are nested into a new `Values` property. --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 59f7b18..96bd503 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -105,9 +105,9 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options \-----------------------------------------------------------------------------------------------------------------------*/ foreach (var relationship in topic.Relationships) { var relationshipData = new RelationshipData() { - Key = relationship.Name, + Key = relationship.Key, }; - foreach (var relatedTopic in relationship) { + foreach (var relatedTopic in relationship.Values) { if ( options.IncludeExternalReferences || relatedTopic.GetUniqueKey().StartsWith(options.ExportScope, StringComparison.InvariantCultureIgnoreCase) From ca7ce2622893c52a091457300e2aece428596f11 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:49:14 -0800 Subject: [PATCH 08/97] Accommodate new `Topic.Relationships.Clear()` requirements The new `Clear()` method _requires_ a specific `relationshipKey`, which it uses to clear an individual collection, instead of _removing_ all relationships. This provides improved tracking, thus allowing those relationships to be deleted on `ITopicRepository.Save()`, whereas previously there was no way of tracking a relationship that had been removed entirely. (I.e., if there is a relationship named e.g. 'Related', and it's empty, then `ITopicRepository.Save()` knows to delete all topics in that relationship. If the `Related` relationship is removed entirely, it doesn't evaluate that relationship at all.) --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 96bd503..eaf26a8 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -336,7 +336,9 @@ List> unresolvedRelationships //First delete any unmatched records, if appropriate if (options.DeleteUnmatchedRelationships) { - topic.Relationships.Clear(); + foreach (var relationship in topic.Relationships) { + topic.Relationships.Clear(relationship.Key); + } } //Update records based on the source collection From 2bb413adb57bc45907e0ef99faa5458bf30785c8 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:52:05 -0800 Subject: [PATCH 09/97] Prefer `Collection` over `List` for publicly exposed types As part of OnTopic 5.0.0, we're adhering to CA1002, which advises using `Collection<>` over `List<>` for externally visible types. This provides a familiar and consistent API that's easier to work with. --- OnTopic.Data.Transfer/RelationshipData.cs | 3 ++- OnTopic.Data.Transfer/TopicData.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/OnTopic.Data.Transfer/RelationshipData.cs b/OnTopic.Data.Transfer/RelationshipData.cs index aba878b..8d012df 100644 --- a/OnTopic.Data.Transfer/RelationshipData.cs +++ b/OnTopic.Data.Transfer/RelationshipData.cs @@ -4,6 +4,7 @@ | Project Topics Library \=============================================================================================================================*/ using System.Collections.Generic; +using System.Collections.ObjectModel; namespace OnTopic.Data.Transfer { @@ -35,7 +36,7 @@ public class RelationshipData { /// /// Gets a collection of unique keys associated with related entities. /// - public List Relationships { get; set; } = new(); + public Collection Relationships { get; set; } = new(); } //Class } //Namespace \ No newline at end of file diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index b5fd266..5ebbf3d 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -3,7 +3,7 @@ | Client Ignia, LLC | Project Topics Library \=============================================================================================================================*/ -using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; namespace OnTopic.Data.Transfer { @@ -89,7 +89,7 @@ public class TopicData { /// Provides a collection of objects representing the children of the associated . /// - public List Children { get; set; } = new(); + public Collection Children { get; set; } = new(); } //Class } //Namespace \ No newline at end of file From 4b29cb9985c9e93d85e0dd46d17755111411fca9 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 12:59:32 -0800 Subject: [PATCH 10/97] Fixed date comparison in unit test In OnTopic 5.0.0, the default `LastModified` value is now set (correctly) using `DateTime.UtcNow` instead of `DateTime.Now`, thus avoiding potential discrepancies with a computer's timezone. Generally, servers are set to use UTC anyway, so this doesn't make a difference. Development machines, however, will typically use local time, thus creating a discrepancy in the test. --- OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index a66371d..0e7ce70 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -71,7 +71,7 @@ public void ImportAsMerge_TopicDataWithAttributes_UpdatesOlderValues() { new() { Key = "Attribute", Value = "New Value", - LastModified = DateTime.Now.AddTicks(1) + LastModified = DateTime.UtcNow.AddTicks(300) } ); From 92ad86cefd832778030d9939e5c693b43bc349ac Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 13:07:29 -0800 Subject: [PATCH 11/97] Fixed attribute check for `TopicID` in unit tests In OnTopic 5.0.0, the `BaseTopic` (previously `DerivedTopic`) is no longer stored in the `Topic.Attributes` collection as a `TopicID` attribute. Instead, it is now stored in the strongly typed `Topic.References` collection. As such, checking the `TopicID` is no longer expected to be a valid check. However, the existing equality check against `baseTopic` continues to work. While I was at it, I renamed the key for base topics from `Derived` to `Base`. This doesn't impact the tests, but is more intuitive as a name for human readers of the test or its debugging data. --- OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 0f71467..2eef045 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -49,7 +49,7 @@ public void Export_DerivedTopic_MapsDerivedTopicKey() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var baseTopic = TopicFactory.Create("Derived", "Container", rootTopic); + var baseTopic = TopicFactory.Create("Base", "Container", rootTopic); topic.BaseTopic = baseTopic; var topicData = topic.Export(); @@ -315,20 +315,19 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var derivedTopic = TopicFactory.Create("Derived", "Container", rootTopic, 5); + var baseTopic = TopicFactory.Create("Base", "Container", rootTopic, 5); var topicData = new TopicData() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - DerivedTopicKey = derivedTopic.GetUniqueKey() + DerivedTopicKey = baseTopic.GetUniqueKey() }; topic.Import(topicData); Assert.IsNotNull(topic.BaseTopic); - Assert.AreEqual("5", topic.Attributes.GetValue("TopicID")); - Assert.AreEqual(derivedTopic, topic.BaseTopic); + Assert.AreEqual(baseTopic, topic.BaseTopic); } @@ -388,7 +387,7 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); - var baseTopic = TopicFactory.Create("Derived", "Container", rootTopic, 5); + var baseTopic = TopicFactory.Create("Base", "Container", rootTopic, 5); topic.BaseTopic = baseTopic; @@ -402,7 +401,6 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { topic.Import(topicData); Assert.IsNotNull(topic.BaseTopic); - Assert.AreEqual("5", topic.Attributes.GetValue("TopicID")); Assert.AreEqual(baseTopic, topic.BaseTopic); } From 22b98661dc49ed39406c06885b701d31b97f19ef Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 13:19:20 -0800 Subject: [PATCH 12/97] Renamed `DerivedTopicKey` to `BaseTopicKey` This aligns with the change of `Topic.DerivedTopic` to `Topic.BaseTopic` in OnTopic 5.0.0 (55e952b). This will break backward compatibility with previous JSON files. That will be addressed in a subsequent update. --- .../DeserializationTest.cs | 12 ++++++------ OnTopic.Data.Transfer.Tests/SerializationTest.cs | 6 +++--- .../TopicExtensionsTest.cs | 16 ++++++++-------- .../Interchange/TopicExtensions.cs | 8 ++++---- OnTopic.Data.Transfer/TopicData.cs | 4 ++-- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 0dff45f..1c7feb6 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -38,7 +38,7 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { $"\"Key\":\"{sourceData.Key}\"," + $"\"UniqueKey\":\"{sourceData.UniqueKey}\"," + $"\"ContentType\":\"{sourceData.ContentType}\"," + - $"\"DerivedTopicKey\":null," + + $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -49,7 +49,7 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { Assert.AreEqual(sourceData.Key, topicData.Key); Assert.AreEqual(sourceData.UniqueKey, topicData.UniqueKey); Assert.AreEqual(sourceData.ContentType, topicData.ContentType); - Assert.AreEqual(sourceData.DerivedTopicKey, topicData.DerivedTopicKey); + Assert.AreEqual(sourceData.BaseTopicKey, topicData.BaseTopicKey); Assert.AreEqual(0, topicData.Relationships.Count); Assert.AreEqual(0, topicData.Attributes.Count); Assert.AreEqual(0, topicData.Children.Count); @@ -148,7 +148,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{sourceTopicData.Key}\"," + $"\"UniqueKey\":\"{sourceTopicData.UniqueKey}\"," + $"\"ContentType\":\"{sourceTopicData.ContentType}\"," + - $"\"DerivedTopicKey\":null," + + $"\"BaseTopicKey\":null," + $"\"Attributes\":[" + $"{{" + $"\"Key\":\"{sourceAttributeData.Key}\"," + @@ -167,7 +167,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{sourceChildTopicData.Key}\"," + $"\"UniqueKey\":\"{sourceChildTopicData.UniqueKey}\"," + $"\"ContentType\":\"{sourceChildTopicData.ContentType}\"," + - $"\"DerivedTopicKey\":null," + + $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -184,7 +184,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Assert.AreEqual(sourceTopicData.Key, topicData.Key); Assert.AreEqual(sourceTopicData.UniqueKey, topicData.UniqueKey); Assert.AreEqual(sourceTopicData.ContentType, topicData.ContentType); - Assert.AreEqual(sourceTopicData.DerivedTopicKey, topicData.DerivedTopicKey); + Assert.AreEqual(sourceTopicData.BaseTopicKey, topicData.BaseTopicKey); Assert.AreEqual(1, sourceTopicData.Relationships.Count); Assert.AreEqual(1, sourceTopicData.Attributes.Count); Assert.AreEqual(1, sourceTopicData.Children.Count); @@ -200,7 +200,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Assert.AreEqual(sourceChildTopicData.Key, childTopicData.Key); Assert.AreEqual(sourceChildTopicData.UniqueKey, childTopicData.UniqueKey); Assert.AreEqual(sourceChildTopicData.ContentType, childTopicData.ContentType); - Assert.AreEqual(sourceChildTopicData.DerivedTopicKey, childTopicData.DerivedTopicKey); + Assert.AreEqual(sourceChildTopicData.BaseTopicKey, childTopicData.BaseTopicKey); Assert.AreEqual(0, sourceChildTopicData.Relationships.Count); Assert.AreEqual(0, sourceChildTopicData.Children.Count); } diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 8bb1204..6bd5ed1 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -39,7 +39,7 @@ public void Serialize_TopicData_ReturnsExpectedResults() { $"\"Key\":\"{topicData.Key}\"," + $"\"UniqueKey\":\"{topicData.UniqueKey}\"," + $"\"ContentType\":\"{topicData.ContentType}\"," + - $"\"DerivedTopicKey\":null," + + $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -138,7 +138,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{topicData.Key}\"," + $"\"UniqueKey\":\"{topicData.UniqueKey}\"," + $"\"ContentType\":\"{topicData.ContentType}\"," + - $"\"DerivedTopicKey\":null," + + $"\"BaseTopicKey\":null," + $"\"Attributes\":[" + $"{{" + $"\"Key\":\"{attributeData.Key}\"," + @@ -157,7 +157,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{childTopicData.Key}\"," + $"\"UniqueKey\":\"{childTopicData.UniqueKey}\"," + $"\"ContentType\":\"{childTopicData.ContentType}\"," + - $"\"DerivedTopicKey\":null," + + $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 2eef045..0268d01 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -42,7 +42,7 @@ public void Export_BasicTopic_MapsProperties() { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with a and ensures that the is set correctly. + /// cref="TopicData.BaseTopicKey"/> is set correctly. /// [TestMethod] public void Export_DerivedTopic_MapsDerivedTopicKey() { @@ -55,7 +55,7 @@ public void Export_DerivedTopic_MapsDerivedTopicKey() { var topicData = topic.Export(); Assert.IsNotNull(topicData); - Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.DerivedTopicKey); + Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.BaseTopicKey); } @@ -307,7 +307,7 @@ public void Import_MismatchedContentType_MaintainsOriginalValue() { | TEST: IMPORT: DERIVED TOPIC KEY: MAPS DERIVED TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a and ensures that the with a and ensures that the is set correctly. /// [TestMethod] @@ -321,7 +321,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - DerivedTopicKey = baseTopic.GetUniqueKey() + BaseTopicKey = baseTopic.GetUniqueKey() }; topic.Import(topicData); @@ -335,7 +335,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { | TEST: IMPORT: DERIVED TOPIC KEY: MAPS NEWLY DERIVED TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a that points to a newly imported + /// Creates a with a that points to a newly imported /// topic that is later in the tree traversal, and ensures that the is set correctly. /// [TestMethod] @@ -354,7 +354,7 @@ public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { Key = "Child", UniqueKey = $"{topicData.UniqueKey}:Child", ContentType = "Container", - DerivedTopicKey = "Root:Test:Related" + BaseTopicKey = "Root:Test:Related" }; var relatedTopicData = new TopicData() { @@ -379,7 +379,7 @@ public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { | TEST: IMPORT: INVALID DERIVED TOPIC KEY: MAINTAINS EXISTING VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a that is invalid and ensures that the + /// Creates a with a that is invalid and ensures that the /// is not updated. /// [TestMethod] @@ -395,7 +395,7 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - DerivedTopicKey = "Root:InvalidKey" + BaseTopicKey = "Root:InvalidKey" }; topic.Import(topicData); diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index eaf26a8..4c8fdae 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -74,7 +74,7 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - DerivedTopicKey = topic.BaseTopic?.GetUniqueKey() + BaseTopicKey = topic.BaseTopic?.GetUniqueKey() }; /*------------------------------------------------------------------------------------------------------------------------ @@ -260,13 +260,13 @@ List> unresolvedRelationships topic.ContentType = topicData.ContentType; } - if (topicData.DerivedTopicKey?.Length > 0) { - var target = topic.GetByUniqueKey(topicData.DerivedTopicKey); + if (topicData.BaseTopicKey?.Length > 0) { + var target = topic.GetByUniqueKey(topicData.BaseTopicKey); if (target is not null) { topic.BaseTopic = target; } else { - unresolvedRelationships.Add(new(topic, "DerivedTopic", topicData.DerivedTopicKey)); + unresolvedRelationships.Add(new(topic, "DerivedTopic", topicData.BaseTopicKey)); } } diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 5ebbf3d..63fa048 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -57,12 +57,12 @@ public class TopicData { public string? ContentType { get; set; } /*========================================================================================================================== - | DERIVED TOPIC KEY + | BASE TOPIC KEY \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Gets the of a that the associated should derive from. /// - public string? DerivedTopicKey { get; set; } + public string? BaseTopicKey { get; set; } /*========================================================================================================================== | ATTRIBUTES From 808d80ac2e0fb0be74fd39e5cedf883999397bc4 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 13:26:03 -0800 Subject: [PATCH 13/97] Ensured `BaseTopicKey` is properly tested for deserialization Previously, all deserialization tests evaluated a `BaseTopicKey` of `null`. As we work with this property, we want to ensure it continues to be properly evaluated. --- OnTopic.Data.Transfer.Tests/DeserializationTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 1c7feb6..b728db9 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -31,14 +31,15 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { var sourceData = new TopicData() { Key = "Test", UniqueKey = "Root:Test", - ContentType = "Container" + ContentType = "Container", + BaseTopicKey = "Root:Meta:Test" }; var json = $"{{" + $"\"Key\":\"{sourceData.Key}\"," + $"\"UniqueKey\":\"{sourceData.UniqueKey}\"," + $"\"ContentType\":\"{sourceData.ContentType}\"," + - $"\"BaseTopicKey\":null," + + $"\"BaseTopicKey\":\"{sourceData.BaseTopicKey}\"," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + From 83675105329bf8e5585a21e67c842dbbced065d7 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 13:29:11 -0800 Subject: [PATCH 14/97] Introduced a test for evaluating the legacy `DerivedTopicKey` In OnTopic 5.0.0, the `Topic.DerivedTopic` was renamed to `Topic.BaseTopic` (55e952b). To maintain consistency, the `TopicData.DerivedTopicKey` was renamed to `TopicData.BaseTopicKey` (22b9866). But previously exported data will still have the legacy `DerivedTopicKey` property set in the JSON. This test determines whether that is correctly imported and associated with the new `BaseTopicKey` property. Note: This test will fail as of this commit, as there's currently no effort to handle this special requirement. Subsequent commits will resolve this issue. --- .../DeserializationTest.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index b728db9..1301734 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -57,6 +57,45 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { } + /*========================================================================================================================== + | TEST: DESERIALIZE: DERIVED TOPIC KEY: RETURNS EXPECTED RESULTS + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a json string with a legacy DerivedTopicKey and attempts to deserialize it as a + /// class. + /// + [TestMethod] + public void Deserialize_DeriedTopicKey_ReturnsExpectedResults() { + + var sourceData = new TopicData() { + Key = "Test", + UniqueKey = "Root:Test", + ContentType = "Container", + BaseTopicKey = "Root:Meta:Test" + }; + + var json = $"{{" + + $"\"Key\":\"{sourceData.Key}\"," + + $"\"UniqueKey\":\"{sourceData.UniqueKey}\"," + + $"\"ContentType\":\"{sourceData.ContentType}\"," + + $"\"DerivedTopicKey\":\"{sourceData.BaseTopicKey}\"," + + $"\"Attributes\":[]," + + $"\"Relationships\":[]," + + $"\"Children\":[]" + + $"}}"; + + var topicData = JsonSerializer.Deserialize(json); + + Assert.AreEqual(sourceData.Key, topicData.Key); + Assert.AreEqual(sourceData.UniqueKey, topicData.UniqueKey); + Assert.AreEqual(sourceData.ContentType, topicData.ContentType); + Assert.AreEqual(sourceData.BaseTopicKey, topicData.BaseTopicKey); + Assert.AreEqual(0, topicData.Relationships.Count); + Assert.AreEqual(0, topicData.Attributes.Count); + Assert.AreEqual(0, topicData.Children.Count); + + } + /*========================================================================================================================== | TEST: DESERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ From 4bbeedcfe1c19a590b7f392ec0e488886be4e7f3 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 15:39:13 -0800 Subject: [PATCH 15/97] Allow `BaseTopicKey` to be defined based on `DerivedTopicKey` In a previous commit, the `DerivedTopicKey` property was renamed to `BaseTopicKey` to maintain parity with OnTopic 5.0.0's change of `Topic.DerivedTopic` to `Topic.BaseTopic`. Unfortunately, however, that prevents previously exported JSON files from being properly imported. To mitigate that, I've reintroduced a (deprecated) `DerivedTopicKey` property, and set `BaseTopicKey`'s getter to fallback to it if `BaseTopicKey` isn't otherwise defined. This has the unfortunate side-effect of writing a null `DerivedTopicKey` property to the exported JSON. This can be mitigated by including the `IgnoreNullValues` on the `JsonSerializerOptions`, which we recommend doing, and will later add in to the OnTopic Editor's integration. Later, when we upgrade to .NET 5.x, we'll be able to handle this better by treating `DerivedTopicKey` as a private field with the `[JsonInclude]` attribute along with the `[JsonIgnore(JsonIgnoreCondition.WhenWritingDefault)]` attribute. But, for now, we want to maintain backward compatibility with .NET Core 3.x, and so those aren't yet available. --- OnTopic.Data.Transfer/TopicData.cs | 40 ++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 63fa048..6a88f4b 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -3,8 +3,11 @@ | Client Ignia, LLC | Project Topics Library \=============================================================================================================================*/ +using System; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; namespace OnTopic.Data.Transfer { @@ -25,6 +28,11 @@ namespace OnTopic.Data.Transfer { /// public class TopicData { + /*========================================================================================================================== + | PRIVATE VARIABLES + \-------------------------------------------------------------------------------------------------------------------------*/ + private string? _baseTopicKey; + /*========================================================================================================================== | KEY \-------------------------------------------------------------------------------------------------------------------------*/ @@ -60,9 +68,37 @@ public class TopicData { | BASE TOPIC KEY \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Gets the of a that the associated should derive from. + /// Gets the of a that the associated should derive from. + /// + #pragma warning disable CS0618 // Type or member is obsolete + public string? BaseTopicKey { + get => _baseTopicKey?? DerivedTopicKey; + set => _baseTopicKey = value; + } +#pragma warning restore CS0618 // Type or member is obsolete + + /*========================================================================================================================== + | DERIVED TOPIC KEY (DEPRECATED) + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Gets the of a that the associated should derive + /// from. /// - public string? BaseTopicKey { get; set; } + /// + /// + /// The is deprecated in favor of the new , but legacy data will + /// still reference it in the JSON. This property thus allows backward compatibility, while being marked as deprecated + /// to discourage callers from utilizing it. + /// + /// + /// Unfortunately, .NET 3.x doesn't permit a way to hide this from the public interface or from serialization. As such, + /// it will continue to pollute the interface and, potentially, the JSON output. Given this, it is recommended that + /// callers use to prevent this—and any other null properties—from + /// being written. + /// + /// + [Obsolete("The DerivedTopicKey has been renamed to BaseTopicKey.", false)] + public string? DerivedTopicKey { get; set; } /*========================================================================================================================== | ATTRIBUTES From fa83c868fb00ffec732c482d1810c84f8f9febc7 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 15:40:18 -0800 Subject: [PATCH 16/97] Ignore nulls in unit tests This is the preferred serialization setting that we recommend, in part due to legacy properties such as `DerivedTopicKey` (4bbeedc). --- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 6bd5ed1..89aa2e2 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -39,13 +39,12 @@ public void Serialize_TopicData_ReturnsExpectedResults() { $"\"Key\":\"{topicData.Key}\"," + $"\"UniqueKey\":\"{topicData.UniqueKey}\"," + $"\"ContentType\":\"{topicData.ContentType}\"," + - $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + $"}}"; - var json = JsonSerializer.Serialize(topicData); + var json = JsonSerializer.Serialize(topicData, new() { IgnoreNullValues = true }); Assert.AreEqual(expected, json); @@ -138,11 +137,9 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{topicData.Key}\"," + $"\"UniqueKey\":\"{topicData.UniqueKey}\"," + $"\"ContentType\":\"{topicData.ContentType}\"," + - $"\"BaseTopicKey\":null," + $"\"Attributes\":[" + $"{{" + $"\"Key\":\"{attributeData.Key}\"," + - $"\"Value\":null," + $"\"LastModified\":\"{attributeData.LastModified:o}\"" + $"}}"+ $"]," + @@ -157,7 +154,6 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{childTopicData.Key}\"," + $"\"UniqueKey\":\"{childTopicData.UniqueKey}\"," + $"\"ContentType\":\"{childTopicData.ContentType}\"," + - $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -165,7 +161,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"]" + $"}}"; - var json = JsonSerializer.Serialize(topicData); + var json = JsonSerializer.Serialize(topicData, new() { IgnoreNullValues = true }); Assert.AreEqual(expected, json); From aac6394b3796ff60e49aa3504c36d7d057c607b8 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 16:57:44 -0800 Subject: [PATCH 17/97] Introduced `References` collection on `TopicData` This can reuse the existing `AttributeDataCollection` and `AttributeData` item type since topic references get serializes with a string `Value` and are tracked via a `LastModified` value. In the future, we may want to update the name of the `AttributeDataCollection` and `AttributeData` classes to make them more generic, but we'll address that separately. (It doesn't impact either the serialization or the deserialization directly.) --- OnTopic.Data.Transfer/TopicData.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 6a88f4b..690f603 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -7,7 +7,6 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Text.Json; -using System.Text.Json.Serialization; namespace OnTopic.Data.Transfer { @@ -118,6 +117,15 @@ public string? BaseTopicKey { /// public RelationshipDataCollection Relationships { get; set; } = new(); + /*========================================================================================================================== + | TOPIC REFERENCES + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Provides a collection of representing the topic references from the associated object. + /// + public AttributeDataCollection References { get; set; } = new(); + /*========================================================================================================================== | CHILDREN \-------------------------------------------------------------------------------------------------------------------------*/ From bc45dc813d7df9afc2b5412147e426ae3398078e Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 16:59:03 -0800 Subject: [PATCH 18/97] Update unit tests to expect a serialized `References` collection This ensures that the serialization unit tests correctly expect a(n empty) `References` array, as per the introduction of the `References` collection (aac6394). --- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 89aa2e2..7eee7f0 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -41,6 +41,7 @@ public void Serialize_TopicData_ReturnsExpectedResults() { $"\"ContentType\":\"{topicData.ContentType}\"," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + + $"\"References\":[]," + $"\"Children\":[]" + $"}}"; @@ -149,6 +150,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Relationships\":[\"Root:Web\"]" + $"}}" + $"]," + + $"\"References\":[]," + $"\"Children\":[" + $"{{" + $"\"Key\":\"{childTopicData.Key}\"," + @@ -156,6 +158,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"ContentType\":\"{childTopicData.ContentType}\"," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + + $"\"References\":[]," + $"\"Children\":[]" + $"}}" + $"]" + From 79f64c17b07dc3c1d520789295168b77c20e7aa4 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:00:33 -0800 Subject: [PATCH 19/97] Introduce a new `DeleteUnmatchedReferences` option We have `DeleteUnmatched` options for `Attributes`, `Relationships`, `NestedTopics`, and `Children`. Now, with the introduction of `References`, we need a corresponding `DeleteUnmatchedReferences`, to extend that capability. --- .../Interchange/ImportOptions.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/OnTopic.Data.Transfer/Interchange/ImportOptions.cs b/OnTopic.Data.Transfer/Interchange/ImportOptions.cs index 8bae925..59b3d77 100644 --- a/OnTopic.Data.Transfer/Interchange/ImportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ImportOptions.cs @@ -28,6 +28,7 @@ public class ImportOptions { \-------------------------------------------------------------------------------------------------------------------------*/ private bool? _deleteUnmatchedAttributes; private bool? _deleteUnmatchedRelationships; + private bool? _deleteUnmatchedReferences; private bool? _deleteUnmatchedChildren; private bool? _deleteUnmatchedNestedTopics; private bool? _overwriteContentType; @@ -77,6 +78,21 @@ public bool DeleteUnmatchedRelationships { set => _deleteUnmatchedRelationships = value; } + /*========================================================================================================================== + | DELETE UNMATCHED REFERENCES + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Delete references in the target database which don't exist in the source graph. + /// + /// + /// This will delete any existing references that aren't also represented in the source + /// graph. + /// + public bool DeleteUnmatchedReferences { + get => _deleteUnmatchedReferences?? Strategy is ImportStrategy.Replace; + set => _deleteUnmatchedReferences = value; + } + /*========================================================================================================================== | DELETE UNMATCHED CHILDREN \-------------------------------------------------------------------------------------------------------------------------*/ From f156c25250508ae1f7708ce3655728957892858c Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:05:08 -0800 Subject: [PATCH 20/97] Handle topic references on `Export()` Write to the new `TopicData.References` collection (aac6394) during serialization via the `Export()` extension method. This is very similar to the handling of attributes, since they are both sources from a `TrackedRecordCollection` and both write to an `AttributeDataCollection`. --- .../Interchange/TopicExtensions.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 4c8fdae..27dad68 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -100,6 +100,25 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options ); } + /*------------------------------------------------------------------------------------------------------------------------ + | Set topic references + \-----------------------------------------------------------------------------------------------------------------------*/ + foreach (var reference in topic.References) { + if ( + !options.IncludeExternalReferences && + !(reference.Value?.GetUniqueKey().StartsWith(options.ExportScope, StringComparison.InvariantCultureIgnoreCase)?? true) + ) { + continue; + } + topicData.References.Add( + new() { + Key = reference.Key, + Value = reference.Value?.GetUniqueKey(), + LastModified = reference.LastModified + } + ); + } + /*------------------------------------------------------------------------------------------------------------------------ | Set relationships \-----------------------------------------------------------------------------------------------------------------------*/ From 6d5938bc8e0307c760c0cd5dbffdca5ade9027b0 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:06:45 -0800 Subject: [PATCH 21/97] Handle topic references on `Import()` Read from the new `TopicData.References` collection (aac6394) during deserialization via the `Import()` extension method. This is very similar to the handling of attributes, since they are both sourced from an `AttributeDataCollection` and both write to a `TrackedRecordCollection`. --- .../Interchange/TopicExtensions.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 27dad68..7ba8864 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -373,6 +373,36 @@ List> unresolvedRelationships } } + + /*------------------------------------------------------------------------------------------------------------------------ + | Set topic references + \-----------------------------------------------------------------------------------------------------------------------*/ + + //First delete any unmatched records, if appropriate + if (options.DeleteUnmatchedReferences) { + var unmatchedReferences = topic.References.Where(a1 => + !topicData.Attributes.Any(a2 => a1.Key == a2.Key) + ); + foreach (var reference in unmatchedReferences.ToArray()) { + topic.References.Remove(reference); + }; + } + + //Update records based on the source collection + foreach (var reference in topicData.References) { + if (useCustomMergeRules(reference)) continue; + var matchedReference = topic.References.FirstOrDefault(a => a.Key == reference.Key); + if (matchedReference is not null && isStrategy(ImportStrategy.Add)) continue; + if (matchedReference?.LastModified >= reference.LastModified && isStrategy(ImportStrategy.Merge)) continue; + var referencedTopic = topic.GetByUniqueKey(reference.Key); + if (reference.Value is null || referencedTopic != null) { + topic.References.SetValue( + reference.Key, + referencedTopic + ); + } + } + /*------------------------------------------------------------------------------------------------------------------------ | Recurse over children \-----------------------------------------------------------------------------------------------------------------------*/ From 6d22172732daa8f61b6bf47c283ce14e0da17d69 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:16:47 -0800 Subject: [PATCH 22/97] Generalized handling of unresolved associations Previously, the (local) `unresolvedRelationships` collection handled tracking of relationships that could not be resolved on the initial pass, and would attempt to wire them up in a subsequent pass. This has now been generalized to support both `Topic.Relationships` and `Topic.References`. To acknowledge that, the variable has been renamed to `unresolvedAssociations`. --- .../Interchange/TopicExtensions.cs | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 7ba8864..dc8bd37 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -184,24 +184,25 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import /*------------------------------------------------------------------------------------------------------------------------ | Establish cache \-----------------------------------------------------------------------------------------------------------------------*/ - var unresolvedRelationships = new List>(); + var unresolvedAssociations = new List>(); /*------------------------------------------------------------------------------------------------------------------------ | Handle first pass \-----------------------------------------------------------------------------------------------------------------------*/ - topic.Import(topicData, options, unresolvedRelationships); + topic.Import(topicData, options, unresolvedAssociations); /*------------------------------------------------------------------------------------------------------------------------ - | Attempt to resolve outstanding relationships + | Attempt to resolve outstanding assocations \-----------------------------------------------------------------------------------------------------------------------*/ - foreach (var relationship in unresolvedRelationships) { + foreach (var relationship in unresolvedAssociations) { - //Attempt to find the target relationship + //Attempt to find the target association var source = relationship.Item1; - var target = topic.GetByUniqueKey(relationship.Item3); - var key = relationship.Item2; + var isRelationship = relationship.Item2; + var key = relationship.Item3; + var target = topic.GetByUniqueKey(relationship.Item4); - //If the relationship STILL can't be resolved, skip it + //If the association STILL can't be resolved, skip it if (target is null) { continue; } @@ -212,10 +213,15 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import } //Wire up relationships - else { + else if (isRelationship) { source.Relationships.SetValue(key, target); } + //Wire up topic references + else { + source.References.SetValue(key, target); + } + } } @@ -238,13 +244,15 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import /// this via directly. /// /// - /// The source to operate off of. + /// The target to write data to. + /// The source to import data from. /// An optional object to specify import settings. + /// A list of associations that could not be resolved on the first private static void Import( this Topic topic, TopicData topicData, [NotNull]ImportOptions? options, - List> unresolvedRelationships + List> unresolvedAssociations ) { /*------------------------------------------------------------------------------------------------------------------------ @@ -285,7 +293,7 @@ List> unresolvedRelationships topic.BaseTopic = target; } else { - unresolvedRelationships.Add(new(topic, "DerivedTopic", topicData.BaseTopicKey)); + unresolvedAssociations.Add(new(topic, false, "DerivedTopic", topicData.BaseTopicKey)); } } @@ -368,7 +376,7 @@ List> unresolvedRelationships topic.Relationships.SetValue(relationship.Key, relatedTopic); } else { - unresolvedRelationships.Add(new(topic, relationship.Key!, relatedTopicKey)); + unresolvedAssociations.Add(new(topic, true, relationship.Key!, relatedTopicKey)); } } } @@ -401,6 +409,9 @@ List> unresolvedRelationships referencedTopic ); } + else { + unresolvedAssociations.Add(new(topic, false, reference.Key, reference.Value)); + } } /*------------------------------------------------------------------------------------------------------------------------ @@ -425,7 +436,7 @@ topic.ContentType is not "List" && options.DeleteUnmatchedChildren if (childTopic is null) { childTopic = TopicFactory.Create(childTopicData.Key, childTopicData.ContentType, topic); } - childTopic.Import(childTopicData, options, unresolvedRelationships); + childTopic.Import(childTopicData, options, unresolvedAssociations); } /*------------------------------------------------------------------------------------------------------------------------ From dc196e36a31442d3cc919b15b7ae7a62d0f842bf Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:42:47 -0800 Subject: [PATCH 23/97] Ensure `BaseTopic` can be imported via `TopicData.References` In OnTopic 5.0.0, the `BaseTopic` (previously `DerivedTopic`) is now handled via the (new) `Topic.References` feature. This prevents the need to specially account for it, as there's now first-class support for this _type_ of association, independent of the specifics of `BaseTopic`. With topic references now accounted for during the `Import()` process (6d5938b), there's also no need for specialty handling of `BaseTopic`, so long as it's passed via a `TopicData.References`. This test sets up an `AttributeData` with a key of `BaseTopic`, adds it to the `TopicData.References` collection, and ensures that it is successfully wired up. --- .../TopicExtensionsTest.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 0268d01..57d1792 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -331,6 +331,41 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { } + /*========================================================================================================================== + | TEST: IMPORT: BASE TOPIC: MAPS BASE TOPIC + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a with a with the of + /// BaseTopic in the collection, which points to a topic in the topic graph. + /// Ensures that it is correctly wired up. + /// + [TestMethod] + public void Import_BaseTopic_MapsBaseTopic() { + + var rootTopic = TopicFactory.Create("Root", "Container"); + var topic = TopicFactory.Create("Test", "Container", rootTopic); + var baseTopic = TopicFactory.Create("BaseTopic", "Container", rootTopic); + + var topicData = new TopicData() { + Key = topic.Key, + UniqueKey = topic.GetUniqueKey(), + ContentType = topic.ContentType + }; + + var referencedTopicData = new AttributeData() { + Key = "BaseTopic", + Value = $"{baseTopic.GetUniqueKey()}" + }; + + topicData.References.Add(referencedTopicData); + + topic.Import(topicData); + + Assert.IsNotNull(topic.BaseTopic); + Assert.AreEqual(baseTopic, topic.BaseTopic); + + } + /*========================================================================================================================== | TEST: IMPORT: DERIVED TOPIC KEY: MAPS NEWLY DERIVED TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ From be111d2b61136bd3dbf0f325db8717a16ff73f4a Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:43:56 -0800 Subject: [PATCH 24/97] Fixed alignment of `BaseTopicKey` in unit tests With the rename of `DerivedTopicKey` to `BaseTopicKey`, the alignment of the variables was off. While I was at it, I also updated one of the assignments to use the root value from the `TopicData` object, so as to not repeat string values. (This is consistent with how we handle this situation in other tests.) --- OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 57d1792..cc04833 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -321,7 +321,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - BaseTopicKey = baseTopic.GetUniqueKey() + BaseTopicKey = baseTopic.GetUniqueKey() }; topic.Import(topicData); @@ -389,7 +389,7 @@ public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { Key = "Child", UniqueKey = $"{topicData.UniqueKey}:Child", ContentType = "Container", - BaseTopicKey = "Root:Test:Related" + BaseTopicKey = $"{topicData.UniqueKey}:Related" }; var relatedTopicData = new TopicData() { @@ -430,7 +430,7 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - BaseTopicKey = "Root:InvalidKey" + BaseTopicKey = "Root:InvalidKey" }; topic.Import(topicData); From e2ba1994be2f5c62b3fec42c2329fe85c0e72516 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 17:47:23 -0800 Subject: [PATCH 25/97] Removed special handling of `DerivedTopic` in `unresolvedAssociations` If the `BaseTopicKey` is set, but the reference cannot be resolved, there's no need to provide special handling for this situation. If we store the key of the `unresolvedAssociations` as `BaseTopic`, then the standard handling of topic references will take care of it without any (further) special attention (6d22172). --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index dc8bd37..bbbb65c 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -207,11 +207,6 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import continue; } - //Wire up derived topics - if (key.Equals("DerivedTopic", StringComparison.OrdinalIgnoreCase)) { - source.BaseTopic = target; - } - //Wire up relationships else if (isRelationship) { source.Relationships.SetValue(key, target); @@ -293,7 +288,7 @@ List> unresolvedAssociations topic.BaseTopic = target; } else { - unresolvedAssociations.Add(new(topic, false, "DerivedTopic", topicData.BaseTopicKey)); + unresolvedAssociations.Add(new(topic, false, "BaseTopic", topicData.BaseTopicKey)); } } From 7c20dd0193bd3774ea20eab874518c9d79741663 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 18:29:06 -0800 Subject: [PATCH 26/97] Rely on `TopicData.References` to handle `BaseTopic` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For new exports, there's no need to set the `BaseTopicKey`; this will be handled automatically by the `TopicData.References`, since `Topic.BaseTopic` corresponds to a `Topic.References` entry. In fact, really, we only need the `BaseTopicKey` for legacy support—which calls into question the entire basis for introducing the `BaseTopicKey` to begin with (4705b29). We'll likely address that in a subsequent update. As part of this, I also updated the unit test to look for the `BaseTopic` inside of the `TopicData.References` collection, instead of within the `BaseTopicKey`. --- .../TopicExtensionsTest.cs | 15 +++++++-------- .../Interchange/TopicExtensions.cs | 3 +-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index cc04833..9245075 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -38,24 +38,23 @@ public void Export_BasicTopic_MapsProperties() { } /*========================================================================================================================== - | TEST: EXPORT: DERIVED TOPIC: MAPS DERIVED TOPIC KEY + | TEST: EXPORT: DERIVED TOPIC: MAPS REFERENCE DATA \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a and ensures that the is set correctly. + /// Creates a with a and ensures that a + /// item with a of BasedTopic is correctly set. /// [TestMethod] - public void Export_DerivedTopic_MapsDerivedTopicKey() { + public void Export_DerivedTopic_MapsReferenceData() { - var rootTopic = TopicFactory.Create("Root", "Container"); - var topic = TopicFactory.Create("Test", "Container", rootTopic); - var baseTopic = TopicFactory.Create("Base", "Container", rootTopic); + var topic = TopicFactory.Create("Test", "Container"); + var baseTopic = TopicFactory.Create("Base", "Container", topic); topic.BaseTopic = baseTopic; var topicData = topic.Export(); Assert.IsNotNull(topicData); - Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.BaseTopicKey); + Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.References.FirstOrDefault()?.Value); } diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index bbbb65c..15c87ba 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -73,8 +73,7 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options var topicData = new TopicData { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), - ContentType = topic.ContentType, - BaseTopicKey = topic.BaseTopic?.GetUniqueKey() + ContentType = topic.ContentType }; /*------------------------------------------------------------------------------------------------------------------------ From 1db08fbb30151f2e905050fef146fede4924b707 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 18:38:09 -0800 Subject: [PATCH 27/97] Unify `BaseTopicKey` handling with `TopicData.References` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `TopicData.References` is now the preferred way of handling `Topic.BaseTopic` (7c20dd0), the handling of `BaseTopicKey` is really only present for backward compatibility. Given that, instead of separately handling the `BaseTopicKey`, we should just use it to establish a `BaseTopic` entry in `TopicData.References`, if one doesn't already exist. This has a few benefits. First, it ensures that the `ImportOptions` are honored, such as honoring `ImportStrategy.Add`, if set, instead of always overwriting the `BaseTopic`—and, therefore, the associated entry in `Topic.References`. Second, it better accounts for unexpected scenarios where both the `BaseTopicKey` and a `BaseTopic` reference are set (in that case, the newer `BaseTopic` reference should always be preferred). Third, it centralizes the lookup of referenced topics to one location. --- .../Interchange/TopicExtensions.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 15c87ba..9beeebd 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -281,14 +281,13 @@ List> unresolvedAssociations topic.ContentType = topicData.ContentType; } - if (topicData.BaseTopicKey?.Length > 0) { - var target = topic.GetByUniqueKey(topicData.BaseTopicKey); - if (target is not null) { - topic.BaseTopic = target; - } - else { - unresolvedAssociations.Add(new(topic, false, "BaseTopic", topicData.BaseTopicKey)); - } + if (topicData.BaseTopicKey?.Length > 0 && !topicData.References.Contains("BaseTopic")) { + topicData.References.Add( + new() { + Key = "BaseTopic", + Value = topicData.BaseTopicKey + } + ); } /*------------------------------------------------------------------------------------------------------------------------ From 18483b8c969675979f2d603a7bb8049d7a1dce59 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:02:17 -0800 Subject: [PATCH 28/97] Update unit test to evaluate legacy `DerivedTopicKey` deserialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that `BaseTopicKey` is no longer set as part of `Export()`—and is therefore not expected from any OnTopic Data Transfer 3.0.0 library exports—the deserialization test that evaluates `BaseTopicKey` should, instead, explicitly evaluate `DerivedTopicKey` with the sole intention of verifying backward compatibility with the legacy JSON format from the OnTopic Data Transfer 2.x library. --- OnTopic.Data.Transfer.Tests/DeserializationTest.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 1301734..1c9dab7 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -65,20 +65,21 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { /// class. /// [TestMethod] - public void Deserialize_DeriedTopicKey_ReturnsExpectedResults() { + #pragma warning disable CS0618 // Type or member is obsolete + public void Deserialize_DerivedTopicKey_ReturnsExpectedResults() { var sourceData = new TopicData() { Key = "Test", UniqueKey = "Root:Test", ContentType = "Container", - BaseTopicKey = "Root:Meta:Test" + DerivedTopicKey = "Root:Meta:Test" }; var json = $"{{" + $"\"Key\":\"{sourceData.Key}\"," + $"\"UniqueKey\":\"{sourceData.UniqueKey}\"," + $"\"ContentType\":\"{sourceData.ContentType}\"," + - $"\"DerivedTopicKey\":\"{sourceData.BaseTopicKey}\"," + + $"\"DerivedTopicKey\":\"{sourceData.DerivedTopicKey}\"," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -89,12 +90,13 @@ public void Deserialize_DeriedTopicKey_ReturnsExpectedResults() { Assert.AreEqual(sourceData.Key, topicData.Key); Assert.AreEqual(sourceData.UniqueKey, topicData.UniqueKey); Assert.AreEqual(sourceData.ContentType, topicData.ContentType); - Assert.AreEqual(sourceData.BaseTopicKey, topicData.BaseTopicKey); + Assert.AreEqual(sourceData.DerivedTopicKey, topicData.DerivedTopicKey); Assert.AreEqual(0, topicData.Relationships.Count); Assert.AreEqual(0, topicData.Attributes.Count); Assert.AreEqual(0, topicData.Children.Count); } + #pragma warning restore CS0618 // Type or member is obsolete /*========================================================================================================================== | TEST: DESERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS From f1ddafe4892649e435ea7beb7e8a061ee05f4bcc Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:04:12 -0800 Subject: [PATCH 29/97] Update unit test to evaluate `Import()` of legacy `DerivedTopicKey` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that `BaseTopicKey` is no longer set as part of `Export()`—and is therefore not expected from any OnTopic Data Transfer 3.0.0 library exports—the `Import()` test that evaluates `BaseTopicKey` should, instead, explicitly evaluate `DerivedTopicKey` with the sole intention of verifying backward compatibility with the legacy data interchange format, as required for JSON exported from the OnTopic Data Transfer 2.x library. --- OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 9245075..d16da88 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -306,10 +306,11 @@ public void Import_MismatchedContentType_MaintainsOriginalValue() { | TEST: IMPORT: DERIVED TOPIC KEY: MAPS DERIVED TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a and ensures that the is set correctly. + /// Creates a with the legacy and ensures that the is set correctly. /// [TestMethod] + #pragma warning disable CS0618 // Type or member is obsolete public void Import_DerivedTopicKey_MapsDerivedTopic() { var rootTopic = TopicFactory.Create("Root", "Container"); @@ -320,7 +321,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType, - BaseTopicKey = baseTopic.GetUniqueKey() + DerivedTopicKey = baseTopic.GetUniqueKey() }; topic.Import(topicData); @@ -329,6 +330,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { Assert.AreEqual(baseTopic, topic.BaseTopic); } + #pragma warning restore CS0618 // Type or member is obsolete /*========================================================================================================================== | TEST: IMPORT: BASE TOPIC: MAPS BASE TOPIC From e420da6589cd6c6caeb8bf96272c3948639c5742 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:08:18 -0800 Subject: [PATCH 30/97] Update `Import()` tests for `BaseTopic` to use `TopicData.References` Most of the `Import()` tests for evaluating `BaseTopic` continued to set the `TopicData.BaseTopicKey`, instead of adding an `AttributeData` record to `TopicData.References`, as is now preferred (7c20dd0). With the exception of the sole test for evaluating backward compatibility (f1ddafe), these tests have been updated to use the new, preferred format. As part of this, the unit test names and descriptions have been updated to better reflect this functionality. --- .../TopicExtensionsTest.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index d16da88..f227659 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -368,11 +368,12 @@ public void Import_BaseTopic_MapsBaseTopic() { } /*========================================================================================================================== - | TEST: IMPORT: DERIVED TOPIC KEY: MAPS NEWLY DERIVED TOPIC + | TEST: IMPORT: BASE TOPIC: MAPS NEW BASE TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a that points to a newly imported - /// topic that is later in the tree traversal, and ensures that the is set correctly. + /// Creates a with a with the of + /// BaseTopic in the collection, which points to a newly imported topic that occurs + /// later in the tree, ensuring that the is set correctly. /// [TestMethod] public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { @@ -389,37 +390,42 @@ public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { var childTopicData = new TopicData() { Key = "Child", UniqueKey = $"{topicData.UniqueKey}:Child", - ContentType = "Container", - BaseTopicKey = $"{topicData.UniqueKey}:Related" + ContentType = "Container" }; - var relatedTopicData = new TopicData() { - Key = "Related", - UniqueKey = $"{topicData.UniqueKey}:Related", + var baseTopicData = new TopicData() { + Key = "BaseTopic", + UniqueKey = $"{topicData.UniqueKey}:BaseTopic", ContentType = "Container" }; + var baseTopicReference = new AttributeData() { + Key = "BaseTopic", + Value = $"{topicData.UniqueKey}:BaseTopic" + }; + topicData.Children.Add(childTopicData); - topicData.Children.Add(relatedTopicData); + topicData.Children.Add(baseTopicData); + childTopicData.References.Add(baseTopicReference); topic.Import(topicData); var childTopic = topic.Children.FirstOrDefault(); Assert.IsNotNull(childTopic.BaseTopic); - Assert.AreEqual(relatedTopicData.Key, childTopic.BaseTopic?.Key); + Assert.AreEqual(baseTopicData.Key, childTopic.BaseTopic?.Key); } /*========================================================================================================================== - | TEST: IMPORT: INVALID DERIVED TOPIC KEY: MAINTAINS EXISTING VALUE + | TEST: IMPORT: INVALID BASE TOPIC: MAINTAINS EXISTING VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with a that is invalid and ensures that the /// is not updated. /// [TestMethod] - public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { + public void Import_InvalidBaseTopic_MaintainsExistingValue() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); @@ -430,10 +436,16 @@ public void Import_InvalidDerivedTopicKey_MaintainsExistingValue() { var topicData = new TopicData() { Key = topic.Key, UniqueKey = topic.GetUniqueKey(), - ContentType = topic.ContentType, - BaseTopicKey = "Root:InvalidKey" + ContentType = topic.ContentType + }; + + var baseTopicReference = new AttributeData() { + Key = "BaseTopic", + Value = $"{topicData.UniqueKey}:BaseTopic" }; + topicData.References.Add(baseTopicReference); + topic.Import(topicData); Assert.IsNotNull(topic.BaseTopic); From 471b135154769bd8c62beb4d7955a8b368329b0e Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:13:33 -0800 Subject: [PATCH 31/97] Update Remove `BaseTopicKey` from deserialization tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that `BaseTopicKey` is no longer set as part of `Export()`—and is therefore not expected from any OnTopic Data Transfer 3.0.0 library exports—the deserialization test should no longer reference `BaseTopicKey`. Note: The exception to this is for unit tests evaluating backward compatibility with the legacy JSON format—but, in that case, they should instead reference `DerivedTopicKey` (18483b8). --- OnTopic.Data.Transfer.Tests/DeserializationTest.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 1c9dab7..6dcfd6c 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -31,15 +31,13 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { var sourceData = new TopicData() { Key = "Test", UniqueKey = "Root:Test", - ContentType = "Container", - BaseTopicKey = "Root:Meta:Test" + ContentType = "Container" }; var json = $"{{" + $"\"Key\":\"{sourceData.Key}\"," + $"\"UniqueKey\":\"{sourceData.UniqueKey}\"," + $"\"ContentType\":\"{sourceData.ContentType}\"," + - $"\"BaseTopicKey\":\"{sourceData.BaseTopicKey}\"," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -50,7 +48,6 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { Assert.AreEqual(sourceData.Key, topicData.Key); Assert.AreEqual(sourceData.UniqueKey, topicData.UniqueKey); Assert.AreEqual(sourceData.ContentType, topicData.ContentType); - Assert.AreEqual(sourceData.BaseTopicKey, topicData.BaseTopicKey); Assert.AreEqual(0, topicData.Relationships.Count); Assert.AreEqual(0, topicData.Attributes.Count); Assert.AreEqual(0, topicData.Children.Count); @@ -190,7 +187,6 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{sourceTopicData.Key}\"," + $"\"UniqueKey\":\"{sourceTopicData.UniqueKey}\"," + $"\"ContentType\":\"{sourceTopicData.ContentType}\"," + - $"\"BaseTopicKey\":null," + $"\"Attributes\":[" + $"{{" + $"\"Key\":\"{sourceAttributeData.Key}\"," + @@ -209,7 +205,6 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"\"Key\":\"{sourceChildTopicData.Key}\"," + $"\"UniqueKey\":\"{sourceChildTopicData.UniqueKey}\"," + $"\"ContentType\":\"{sourceChildTopicData.ContentType}\"," + - $"\"BaseTopicKey\":null," + $"\"Attributes\":[]," + $"\"Relationships\":[]," + $"\"Children\":[]" + @@ -226,7 +221,6 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Assert.AreEqual(sourceTopicData.Key, topicData.Key); Assert.AreEqual(sourceTopicData.UniqueKey, topicData.UniqueKey); Assert.AreEqual(sourceTopicData.ContentType, topicData.ContentType); - Assert.AreEqual(sourceTopicData.BaseTopicKey, topicData.BaseTopicKey); Assert.AreEqual(1, sourceTopicData.Relationships.Count); Assert.AreEqual(1, sourceTopicData.Attributes.Count); Assert.AreEqual(1, sourceTopicData.Children.Count); @@ -242,7 +236,6 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Assert.AreEqual(sourceChildTopicData.Key, childTopicData.Key); Assert.AreEqual(sourceChildTopicData.UniqueKey, childTopicData.UniqueKey); Assert.AreEqual(sourceChildTopicData.ContentType, childTopicData.ContentType); - Assert.AreEqual(sourceChildTopicData.BaseTopicKey, childTopicData.BaseTopicKey); Assert.AreEqual(0, sourceChildTopicData.Relationships.Count); Assert.AreEqual(0, sourceChildTopicData.Children.Count); } From 76a506d1744678f62805d16f37f8fec7cd1d0e12 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:15:49 -0800 Subject: [PATCH 32/97] Update TopicExtensions.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that `BaseTopicKey` is no longer set as part of `Export()` (7c20dd0)—and is therefore not expected from any OnTopic Data Transfer 3.0.0 library exports—the `Import()` extension method should no longer reference `BaseTopicKey`, but instead continue to look for the `DerivedTopicKey`. This is how this code was originally written, but it was updated to use `BaseTopicKey` when that was introduced (4705b29). --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 9beeebd..7d33e11 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -281,14 +281,16 @@ List> unresolvedAssociations topic.ContentType = topicData.ContentType; } - if (topicData.BaseTopicKey?.Length > 0 && !topicData.References.Contains("BaseTopic")) { + #pragma warning disable CS0618 // Type or member is obsolete + if (topicData.DerivedTopicKey?.Length > 0 && !topicData.References.Contains("BaseTopic")) { topicData.References.Add( new() { Key = "BaseTopic", - Value = topicData.BaseTopicKey + Value = topicData.DerivedTopicKey } ); } + #pragma warning restore CS0618 // Type or member is obsolete /*------------------------------------------------------------------------------------------------------------------------ | Set attributes From d9d6f7f781d944492692434b048ce4e456c81f14 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:22:24 -0800 Subject: [PATCH 33/97] Remove unnecessary `BaseTopicKey` In a previous feature, we introduced the `BaseTopicKey` as a replacement for the legacy `DerivedTopicKey` (4705b29). This was useful for maintaining backward compatibility while maintaining consistency with the new `Topic.BaseTopic` naming convention (55e952b). Now that we've fully implemented the new `Topic.References` capabilities into the `Export()` (f156c25) and `Import()` (6d5938b) methods, however, and rely primarily on those for handling `BaseTopic` references (7c20dd0, 76a506d), there's no need to maintain the `BaseTopicKey` property. In fact, doing so only introduces confusion in terms of how it relates to the `TopicData.References` version of `BaseTopic` (1db08fb). This effectively reverts most of the functionality introduced as part of the `feature/BaseTopicKey` branch (4705b29), with the exception that `DerivedTopicKey` continues to be marked as deprecated via the `[Obsolete()]` attribute. --- OnTopic.Data.Transfer/TopicData.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 690f603..367b542 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -27,11 +27,6 @@ namespace OnTopic.Data.Transfer { /// public class TopicData { - /*========================================================================================================================== - | PRIVATE VARIABLES - \-------------------------------------------------------------------------------------------------------------------------*/ - private string? _baseTopicKey; - /*========================================================================================================================== | KEY \-------------------------------------------------------------------------------------------------------------------------*/ @@ -63,19 +58,6 @@ public class TopicData { [NotNull, DisallowNull] public string? ContentType { get; set; } - /*========================================================================================================================== - | BASE TOPIC KEY - \-------------------------------------------------------------------------------------------------------------------------*/ - /// - /// Gets the of a that the associated should derive from. - /// - #pragma warning disable CS0618 // Type or member is obsolete - public string? BaseTopicKey { - get => _baseTopicKey?? DerivedTopicKey; - set => _baseTopicKey = value; - } -#pragma warning restore CS0618 // Type or member is obsolete - /*========================================================================================================================== | DERIVED TOPIC KEY (DEPRECATED) \-------------------------------------------------------------------------------------------------------------------------*/ @@ -93,7 +75,7 @@ public string? BaseTopicKey { /// Unfortunately, .NET 3.x doesn't permit a way to hide this from the public interface or from serialization. As such, /// it will continue to pollute the interface and, potentially, the JSON output. Given this, it is recommended that /// callers use to prevent this—and any other null properties—from - /// being written. + /// being written in the future. /// /// [Obsolete("The DerivedTopicKey has been renamed to BaseTopicKey.", false)] From 6da847fe44a58d2b9b0dcfb0aa9e10d9fece026c Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Thu, 11 Feb 2021 19:28:48 -0800 Subject: [PATCH 34/97] Updated outdated references to "derived topic" nomenclature While previous updates removed references to `Topic.DerivedTopic`, there remained some instance of string references to "derived topic", as well as unit tests referring to `DerivedTopic`, when they now operate off of `BaseTopic`. These should have been picked up in previous updates (e420da6, 55e952b) but were missed. --- OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 8 ++++---- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index f227659..af84274 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -38,14 +38,14 @@ public void Export_BasicTopic_MapsProperties() { } /*========================================================================================================================== - | TEST: EXPORT: DERIVED TOPIC: MAPS REFERENCE DATA + | TEST: EXPORT: BASE TOPIC: MAPS REFERENCE DATA \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with a and ensures that a /// item with a of BasedTopic is correctly set. /// [TestMethod] - public void Export_DerivedTopic_MapsReferenceData() { + public void Export_BaseTopic_MapsReferenceData() { var topic = TopicFactory.Create("Test", "Container"); var baseTopic = TopicFactory.Create("Base", "Container", topic); @@ -376,7 +376,7 @@ public void Import_BaseTopic_MapsBaseTopic() { /// later in the tree, ensuring that the is set correctly. /// [TestMethod] - public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { + public void Import_BaseTopic_MapsNewBaseTopic() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); @@ -422,7 +422,7 @@ public void Import_DerivedTopicKey_MapsNewlyDerivedTopic() { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with a that is invalid and ensures that the - /// is not updated. + /// is not updated. /// [TestMethod] public void Import_InvalidBaseTopic_MaintainsExistingValue() { diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 7d33e11..ba5ae22 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -227,15 +227,15 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import /// /// /// While traversing a topic graph with many new topics, scenarios emerge where the topic graph cannot be fully - /// reconstructed since the relationships and derived topics may refer to new topics that haven't yet been imported. To - /// mitigate that, this overload accepts and populates a cache of such relationships, so that they can be recreated + /// reconstructed since the relationships and referenced topics may refer to new topics that haven't yet been imported. + /// To mitigate that, this overload accepts and populates a cache of such associations, so that they can be recreated /// afterwards. /// /// /// This does not address the scenario where implicit topic pointers (i.e., attributes ending in Id) - /// cannot be resolved because the target topics haven't yet been saved—and, therefore, the cannot be translated to a . There isn't any obvious way to address - /// this via directly. + /// cannot be resolved because the target topics haven't yet been saved—and, therefore, the cannot be translated to a . There isn't any obvious way to address this via + /// directly. /// /// /// The target to write data to. From a1c3cc45b9c4056c33a195fcfa0537965cbd0742 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 11:35:30 -0800 Subject: [PATCH 35/97] Extend documentation on legacy helper functions These functions are only needed to maintain backward compatibility with the legacy `TranslateTopicPointers` functionality, which has since been superseded by the preferred `TopicData.References` functionality. I've extended the XML Doc remarks to better communicate this. --- .../Interchange/TopicExtensions.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index ba5ae22..4e6e7e0 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -463,6 +463,13 @@ bool useCustomMergeRules(AttributeData attribute) => /// Given a TopicID, lookup the topic in the topic graph and return the fully-qualified value. If no value can be /// found, the original TopicID is returned. /// + /// + /// Note that this function is exclusively required for maintaining backward compatibility the option/ With the release of OnTopic 5.0.0, and OnTopic Data Transfer 3.0.0, + /// implementers should prefer the use of . The method continues to be included primarily for backward compatibility with legacy database + /// configurations. + /// /// The source to operate off of. /// The to retrieve the for. /// An optional object to specify export settings. @@ -502,6 +509,13 @@ bool useCustomMergeRules(AttributeData attribute) => /// Given a UniqueKey, lookup the topic in the topic graph and return the TopicID. If no value can be /// found, the original UniqueKey is returned. /// + /// + /// Note that this function is exclusively required for maintaining backward compatibility with exported using the option, which was the default in + /// OnTopic Data Transfer 2.x. With the release of OnTopic 5.0.0, and OnTopic Data Transfer 3.0.0, implementers should + /// prefer the use of . The method continues to be + /// included primarily for backward compatibility with legacy JSON data. + /// /// The source to operate off of. /// The to retrieve the for. private static string? GetTopicId(Topic topic, string? uniqueKey) { From 02cc7e09a65ae9e134e4415e1c9e3c0b29154677 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 11:36:49 -0800 Subject: [PATCH 36/97] Clarified purpose of the legacy `TranslateTopicPointers` option The `TranslateTopicPointers` option has been superseded in most use by the new `TopicData.References` functionality (86528eb). I've extended the documentation of the option to better communicate that. --- .../Interchange/ExportOptions.cs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs index c1e2bcb..59ae7f5 100644 --- a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs @@ -103,27 +103,33 @@ public bool IncludeNestedTopics { /// /// /// - /// Well-known topic pointers such as (ParentID) and - /// (TopicID), as well as relationships, are translated from to . This ensures that the references can be repopulated on import even though the will be different in each database. + /// Strongly typed topic asociations, such as , , and —which includes —are all translated from from to . This ensures that the references can be repopulated on import even + /// though the will be different in each database. /// /// - /// In addition, however, there are attributes that behave like topic pointers, but aren't as formally defined. - /// These include those corresponding to the TopicList, TokenizedTopicList, and TopicReference - /// attribute types. By convention, these end with an ID (e.g., RootTopicID). Optionally, the export can - /// attempt to map these to a , thus allowing them to maintain referential integrity - /// between databases. + /// In addition, however, there may be attributes that behave like topic associations, but aren't formally stored + /// using the aforementioned structures. Notably, these were used by previous versions of OnTopic Editor to stored + /// values derived from the TopicList, TokenizedTopicList, and TopicReference attribute types. By + /// convention, those references were named with a suffix of ID (e.g., RootTopicID). Optionally, the + /// export can attempt to map these to a , thus allowing them to maintain referential + /// integrity between databases. + /// + /// + /// As of OnTopic 5.0.0, these references should be upgraded to , which are fully + /// supported by the OnTopic Data Transfer library via . As such, this option is only + /// needed for databases that continue to use the legacy format, potentially in order to maintain backward + /// compatibility. /// /// /// It is important to note that this can introduce false positives. For example, if a database includes an attribute /// referring to an external identifier, and whose name ends with Id, that value will be interpreted as a topic - /// reference assuming the value maps to an existing . As such, it may be desirable to disable - /// this option in some circumstances if it's known that there are false positives. + /// association, assuming the value maps to an existing . As such, this option should only be + /// enabled when it's known to be necessary to maintain backward compatibility. /// /// - public bool TranslateTopicPointers { get; set; } = true; - + public bool TranslateTopicPointers { get; set; } } //Class } //Namespace \ No newline at end of file From b67e87f9370e03f269a72fb70afafda17a4b3aad Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 13:51:46 -0800 Subject: [PATCH 37/97] Renamed `TranslateTopicPointers` to `TranslateLegacyTopicReferences` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The terminology for "topic pointers" was standardized as "topic references"—and what the `ExportOptions` refers to as `TopicPointers` are no longer the preferred method of modeling topic references. To help unify the vocabulary, while simultaneously clarifying that this is for legacy support, I've renamed this option to `TranslateLegacyTopicReferences`. --- OnTopic.Data.Transfer/Interchange/ExportOptions.cs | 8 ++++++-- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs index 59ae7f5..c92066e 100644 --- a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs @@ -3,6 +3,7 @@ | Client Ignia, LLC | Project Topics Library \=============================================================================================================================*/ +using System; using System.Diagnostics.CodeAnalysis; namespace OnTopic.Data.Transfer.Interchange { @@ -96,10 +97,10 @@ public bool IncludeNestedTopics { public bool IncludeChildTopics { get; set; } /*========================================================================================================================== - | TRANSLATE TOPIC POINTERS + | TRANSLATE LEGACY TOPIC REFERENCES \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Determines whether attributes that appear to be topic pointers should be mapped to their fully qualified unique key. + /// Determines whether attributes that appear to be topic reference should be mapped to its fully qualified unique key. /// /// /// @@ -129,6 +130,9 @@ public bool IncludeNestedTopics { /// enabled when it's known to be necessary to maintain backward compatibility. /// /// + public bool TranslateLegacyTopicReferences { get; set; } + + [Obsolete("The TranslateTopicPointers option has been renamed to TranslateLegacyTopicReferences", true)] public bool TranslateTopicPointers { get; set; } } //Class diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 4e6e7e0..d628374 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -163,7 +163,7 @@ childTopic.ContentType is "List" | Get attribute value \-----------------------------------------------------------------------------------------------------------------------*/ string? getAttributeValue(AttributeRecord attribute) => - options.TranslateTopicPointers && attribute.Key.EndsWith("ID", StringComparison.InvariantCultureIgnoreCase)? + options.TranslateLegacyTopicReferences && attribute.Key.EndsWith("ID", StringComparison.InvariantCultureIgnoreCase)? GetUniqueKey(topic, attribute.Value, options) : attribute.Value; @@ -465,9 +465,9 @@ bool useCustomMergeRules(AttributeData attribute) => /// /// /// Note that this function is exclusively required for maintaining backward compatibility the option/ With the release of OnTopic 5.0.0, and OnTopic Data Transfer 3.0.0, - /// implementers should prefer the use of . The method continues to be included primarily for backward compatibility with legacy database + /// ExportOptions.TranslateLegacyTopicReferences"/> option/ With the release of OnTopic 5.0.0, and OnTopic Data Transfer + /// 3.0.0, implementers should prefer the use of . The method continues to be included primarily for backward compatibility with legacy database /// configurations. /// /// The source to operate off of. From 37915a5088c19ed45911ed7f6339c8ca665ca0ba Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:02:08 -0800 Subject: [PATCH 38/97] Updated unit tests with new "legacy topic references" nomenclature While I renamed `TranslateTopicPointers` to `TranslateLegacyTopicReferences` (b67e87f), a number of unit test names were still referring to "topic pointers". These have been updated to instead refer to "legacy topic references". This is wordier, but more explicit and intuitive in terms of what it's referring to. --- .../ExportOptionsTest.cs | 10 ++-- .../TopicExtensionsTest.cs | 58 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index a7c8534..8acffb6 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -76,7 +76,7 @@ public void ExportWithNestedTopic_TopicWithNestedTopics_IncludesNestedTopics() { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with several and ensures that the collection does not include external references—i.e., relationships that point + /// cref="TopicData.Relationships"/> collection does not include external references—i.e., relationships that refer /// to s outside of the current export scope. /// [TestMethod] @@ -96,14 +96,14 @@ public void Export_TopicWithRelationships_ExcludesExternalReferences() { } /*========================================================================================================================== - | TEST: EXPORT WITH TOPIC POINTERS: EXTERNAL TOPIC POINTER: EXPORTS UNIQUE KEY + | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: EXTERNAL TOPIC REFERENCE: EXPORTS UNIQUE KEY \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that points to another topic. Confirms - /// that it is converted to a UniqueKey if valid, and otherwise left as is. + /// Creates a with an arbitrary that references another topic. Confirms + /// that it is converted to a if valid, and is otherwise left as is. /// [TestMethod] - public void ExportWithTopicPointers_ExternalTopicPointer_ExportsUniqueKey() { + public void ExportWithLegacyTopicReferences_ExternalTopicReference_ExportsUniqueKey() { var parentTopic = TopicFactory.Create("Root", "Container", 5); var topic = TopicFactory.Create("Topic", "Container", parentTopic); diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index af84274..09b29cb 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -116,10 +116,10 @@ public void Export_TopicWithRelationships_MapsRelationshipDataCollection() { | TEST: EXPORT WITH EXTERNAL REFERENCES: TOPIC WITH RELATIONSHIPS: INCLUDE EXTERNAL REFERENCES \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with several and ensures that the collection does include external references—i.e., relationships that point - /// to s outside of the current export scope—when permitted with the option. + /// Creates a with several and ensures that the collection does include external references—i.e., relationships that reference s outside of the current export scope—when permitted with the option. /// [TestMethod] public void ExportWithExternalReferences_TopicWithRelationships_ExcludesExternalReferences() { @@ -189,14 +189,15 @@ public void Export_ExcludesReservedAttributes() { } /*========================================================================================================================== - | TEST: EXPORT WITH TOPIC POINTERS: OUT OF SCOPE: SKIPS ATTRIBUTE + | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: OUT OF SCOPE: SKIPS REFERENCE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that points to a topic outside of the - /// . Confirms that it is ignored. + /// Creates a with an arbitrary that references a + /// outside of the . Confirms that the reference is maintained as an attribute, but + /// not added as a reference. /// [TestMethod] - public void ExportWithTopicPointers_OutOfScope_SkipsAttribute() { + public void ExportWithLegacyTopicReferences_OutOfScope_SkipsReference() { var parentTopic = TopicFactory.Create("Root", "Container", 5); var topic = TopicFactory.Create("Topic", "Container", parentTopic); @@ -212,14 +213,14 @@ public void ExportWithTopicPointers_OutOfScope_SkipsAttribute() { } /*========================================================================================================================== - | TEST: EXPORT WITH TOPIC POINTERS: MISSING TOPIC POINTER: SKIPS ATTRIBUTE + | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: MISSING TOPIC REFERENCE: SKIPS REFERENCE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that points to a missing . Confirms that the attribute is skipped. + /// Creates a with an arbitrary that references a missing . Confirms that the reference is maintained as an attribute, but not added as a reference. /// [TestMethod] - public void ExportWithTopicPointers_MissingTopicPointer_SkipsAttribute() { + public void ExportWithLegacyTopicReferences_MissingTopicReference_SkipsReference() { var topic = TopicFactory.Create("Topic", "Container"); @@ -234,14 +235,14 @@ public void ExportWithTopicPointers_MissingTopicPointer_SkipsAttribute() { } /*========================================================================================================================== - | TEST: EXPORT WITH TOPIC POINTERS: INVALID TOPIC POINTER: EXPORTS ORIGINAL VALUE + | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: INVALID TOPIC REFERENCE: EXPORTS ORIGINAL VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with an arbitrary that that contains a non-numeric (i.e., - /// invalid) topic pointers. Confirms that the original value is exported. + /// invalid) topic reference. Confirms that the original value is exported. /// [TestMethod] - public void ExportWithTopicPointers_InvalidTopicPointer_ExportsOriginalValue() { + public void ExportWithLegacyTopicReferences_InvalidTopicReference_ExportsOriginalValue() { var topic = TopicFactory.Create("Topic", "Container"); @@ -337,7 +338,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with a with the of - /// BaseTopic in the collection, which points to a topic in the topic graph. + /// BaseTopic in the collection, which references a topic in the topic graph. /// Ensures that it is correctly wired up. /// [TestMethod] @@ -372,8 +373,8 @@ public void Import_BaseTopic_MapsBaseTopic() { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with a with the of - /// BaseTopic in the collection, which points to a newly imported topic that occurs - /// later in the tree, ensuring that the is set correctly. + /// BaseTopic in the collection, which references a newly imported topic that + /// occurs later in the tree, ensuring that the is set correctly. /// [TestMethod] public void Import_BaseTopic_MapsNewBaseTopic() { @@ -717,14 +718,14 @@ public void Import_TopicDataWithChild_SkipsOrphanedChild() { } /*========================================================================================================================== - | TEST: IMPORT: TOPIC DATA WITH TOPIC POINTER: MAPS TOPIC ID + | TEST: IMPORT: TOPIC DATA WITH LEGACY TOPIC REFERENCE: MAPS TOPIC ID \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that points to another topic. Confirms - /// that it is converted to a TopicID if valid, and otherwise left as is. + /// Creates a with an arbitrary that references another topic. + /// Confirms that it is converted to a if valid, and otherwise left as is. /// [TestMethod] - public void Import_TopicDataWithTopicPointer_MapsTopicID() { + public void Import_TopicDataWithLegacyTopicReference_MapsTopicID() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Topic", "Container", rootTopic); @@ -750,14 +751,14 @@ public void Import_TopicDataWithTopicPointer_MapsTopicID() { } /*========================================================================================================================== - | TEST: IMPORT: TOPIC DATA WITH MISSING TOPIC POINTER: SKIPS ATTRIBUTE + | TEST: IMPORT: TOPIC DATA WITH MISSING LEGACY TOPIC REFERENCE: SKIPS ATTRIBUTE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that points to a missing topic. + /// Creates a with an arbitrary that references to a missing topic. /// Confirms that the attribute is skipped. /// [TestMethod] - public void Import_TopicDataWithMissingTopicPointer_SkipsAttribute() { + public void Import_TopicDataWithMissingLegacyTopicReference_SkipsAttribute() { var topic = TopicFactory.Create("Topic", "Container"); @@ -770,7 +771,7 @@ public void Import_TopicDataWithMissingTopicPointer_SkipsAttribute() { topicData.Attributes.Add( new() { Key = "SomeId", - Value = "Root:Missing:Topic:Pointer" + Value = "Root:Missing:Legacy:Topic:Reference" } ); @@ -781,14 +782,14 @@ public void Import_TopicDataWithMissingTopicPointer_SkipsAttribute() { } /*========================================================================================================================== - | TEST: IMPORT: TOPIC DATA WITH INVALID TOPIC POINTER: IMPORTS ORIGINAL VALUE + | TEST: IMPORT: TOPIC DATA WITH INVALID LEGACY TOPIC REFERENCE: IMPORTS ORIGINAL VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with an arbitrary that does not reference a topic key /// (i.e., it doesn't start with Root). Confirms that the original attribute value is imported. /// [TestMethod] - public void Import_TopicDataWithInvalidTopicPointer_ImportsOriginalValue() { + public void Import_TopicDataWithInvalidLegacyTopicReference_ImportsOriginalValue() { var topic = TopicFactory.Create("Topic", "Container"); @@ -811,6 +812,5 @@ public void Import_TopicDataWithInvalidTopicPointer_ImportsOriginalValue() { } - } //Class } //Namespace \ No newline at end of file From a471a7edfa7e1f05cf4c1081b1b5ff7c75f51f71 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:11:14 -0800 Subject: [PATCH 39/97] Renamed `IncludeExternalReferences` to `IncludeExternalAssocations` This is more consistent with the vocabulary established in OnTopic 5.0.0, and avoids ambiguity with the new `Topic.References` feature, which is only one type of reference. --- .../ExportOptionsTest.cs | 5 +++-- .../TopicExtensionsTest.cs | 10 +++++----- .../Interchange/ExportOptions.cs | 19 +++++++++++-------- .../Interchange/TopicExtensions.cs | 6 +++--- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 8acffb6..05d303a 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -110,9 +110,10 @@ public void ExportWithLegacyTopicReferences_ExternalTopicReference_ExportsUnique topic.Attributes.SetValue("SomeId", "5"); - var topicData = topic.Export( + var topicData = topic.Export( new() { - IncludeExternalReferences = true + IncludeExternalAssociations = true, + TranslateLegacyTopicReferences = true } ); diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 09b29cb..d9f723a 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -113,16 +113,16 @@ public void Export_TopicWithRelationships_MapsRelationshipDataCollection() { } /*========================================================================================================================== - | TEST: EXPORT WITH EXTERNAL REFERENCES: TOPIC WITH RELATIONSHIPS: INCLUDE EXTERNAL REFERENCES + | TEST: EXPORT WITH EXTERNAL ASSOCIATIONS: TOPIC WITH RELATIONSHIPS: INCLUDE EXTERNAL REFERENCES \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with several and ensures that the collection does include external references—i.e., relationships that reference collection does include external associations—i.e., relationships that reference s outside of the current export scope—when permitted with the option. + /// IncludeExternalAssociations"/> option. /// [TestMethod] - public void ExportWithExternalReferences_TopicWithRelationships_ExcludesExternalReferences() { + public void ExportWithExternalAssociations_TopicWithRelationships_ExcludesExternalReferences() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); @@ -132,7 +132,7 @@ public void ExportWithExternalReferences_TopicWithRelationships_ExcludesExternal var topicData = topic.Export( new() { - IncludeExternalReferences = true + IncludeExternalAssociations = true } ); diff --git a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs index c92066e..698bd09 100644 --- a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs @@ -27,20 +27,23 @@ public class ExportOptions { private bool _includeNestedTopics; /*========================================================================================================================== - | INCLUDE EXTERNAL REFERENCES + | INCLUDE EXTERNAL ASSOCIATIONS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Determines whether relationships pointing to s outside the scope of the export should be included. + /// Determines whether associations pointing to s outside the scope of the export should be included. /// /// - /// By default, only relationships which point to s within the currently scoped export will be - /// included as relationships. This avoids any potential issues matching topics when importing the data into an existing - /// topic graph. Optionally, however, callers may force all external references to be included, even if they aren't + /// By default, only associations which point to s within the currently scoped export will be included + /// as associations. This avoids any potential issues matching topics when importing the data into an existing topic + /// graph. Optionally, however, callers may force all external associations to be included, even if they aren't /// represented in the currently scoped export. This is only recommended for cases where the caller is confident that the /// external references will be available in the target database—as might be the case, for example, when combining the - /// exporting with other exports. It may also make when the purpose of the export is to act as a backup for a portion of a - /// topic graph, with the expected target being the same topic graph should a bulk-restore be required. + /// export with additional exports. It may also make sense when the purpose of the export is to act as a backup for a + /// portion of a topic graph, with the expected target being the same topic graph should a restore be required. /// + public bool IncludeExternalAssociations { get; set; } + + [Obsolete("The IncludeExternalReferences option has been renamed to IncludeExternalAssociations", true)] public bool IncludeExternalReferences { get; set; } /*========================================================================================================================== @@ -53,7 +56,7 @@ public class ExportOptions { /// The property is set internally by on the initial call—but not on any recursive calls. The value is set to the of /// the initial upon which is being called. - /// This is used in conjunction with ; when + /// This is used in conjunction with ; when /// is set to true, the is used to determine whether or not an external reference—such /// as a relationship—is within scope or not by comparing the relationship's to the /// . diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index d628374..47a4ea0 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -104,7 +104,7 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options \-----------------------------------------------------------------------------------------------------------------------*/ foreach (var reference in topic.References) { if ( - !options.IncludeExternalReferences && + !options.IncludeExternalAssociations && !(reference.Value?.GetUniqueKey().StartsWith(options.ExportScope, StringComparison.InvariantCultureIgnoreCase)?? true) ) { continue; @@ -127,7 +127,7 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options }; foreach (var relatedTopic in relationship.Values) { if ( - options.IncludeExternalReferences || + options.IncludeExternalAssociations || relatedTopic.GetUniqueKey().StartsWith(options.ExportScope, StringComparison.InvariantCultureIgnoreCase) ) { relationshipData.Relationships.Add(relatedTopic.GetUniqueKey()); @@ -486,7 +486,7 @@ bool useCustomMergeRules(AttributeData attribute) => | Establish scope \-----------------------------------------------------------------------------------------------------------------------*/ var uniqueKey = topic.GetRootTopic().FindFirst(t => t.Id == id)?.GetUniqueKey(); - var exportScope = options.IncludeExternalReferences? "Root" : options.ExportScope; + var exportScope = options.IncludeExternalAssociations? "Root" : options.ExportScope; /*------------------------------------------------------------------------------------------------------------------------ | Return in-scope values From 6d060d0f26f9ed0685899a7243af8422044236c5 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:20:01 -0800 Subject: [PATCH 40/97] Import legacy topic references as topic references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If legacy topic references exist—i.e., attributes whose `Key` ends in `Id` and who point to a `UniqueKey()`—then strip the `Id` extension and import them into the new `TopicData.References` collection. Do not do this if a topic reference with the new `Key` already exists. If an existing `Key` is found, or the value doesn't appear to be a `UniqueKey()`, then continue to import it into `TopicData.Attributes`. This is a change in behavior from the previous functionality, which would fail to import it at all if the value could not be translated. This change has three benefits. First, it unifies the legacy topic references with the new topic references. As this occurs automatically as part of the OnTopic 5.0.0 migration script, our assumption is that any JSON with these legacy topic references is from before that migration and, therefore, should be updated in the same way that the e.g. SQL data was updated. Second, it ensures that `unresolvedAssociations` are properly tracked, such that topic references that haven't yet been established (e.g., because they exist later in the topic graph that's being imported) will be picked up after `Import()` has finished importing the entire topic graph. Finally, it helps standardize the code for handling topic references, instead of having a separate process for handling legacy topic references and new topic references. This also allows us to get rid of the legacy `getAttributeValue()` and `GetTopicId()` helper functions, which were needed exclusively for the support of the legacy topic reference handling. On the net, this simplifies the code. --- .../Interchange/TopicExtensions.cs | 66 ++++++++----------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 47a4ea0..641a038 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -292,6 +292,33 @@ List> unresolvedAssociations } #pragma warning restore CS0618 // Type or member is obsolete + /*------------------------------------------------------------------------------------------------------------------------ + | Migrate legacy topic references + >------------------------------------------------------------------------------------------------------------------------- + | Previous versions of the OnTopic Data Transfer library identified attributes that ended with `Id` and had a value that + | mapped to a `topic.Id` and translated their values to `topic.GetUniqueKey()` so that they could be translated back to + | topic references. As of OnTopic 5 and OnTopic Data Transfer 3, this functionality is now formalized as `topic. + | References`. The following provides legacy support for this encoding by migrating these attributes to `topicData. + | References`, thus allowing them to be handled the same way as other topic references. + \-----------------------------------------------------------------------------------------------------------------------*/ + foreach (var attribute in topicData.Attributes.ToList()) { + if ( + attribute.Value is not null && + attribute.Key.EndsWith("ID", StringComparison.InvariantCultureIgnoreCase) && + attribute.Value.StartsWith("Root", StringComparison.InvariantCultureIgnoreCase) + ) { + var referenceKey = attribute.Key.Substring(0, attribute.Key.Length-2); + if (referenceKey is "Topic") { + referenceKey = "BaseTopic"; + } + if (!topicData.References.Contains(referenceKey)) { + attribute.Key = referenceKey; + topicData.Attributes.Remove(attribute); + topicData.References.Add(attribute); + } + } + } + /*------------------------------------------------------------------------------------------------------------------------ | Set attributes \-----------------------------------------------------------------------------------------------------------------------*/ @@ -315,7 +342,7 @@ List> unresolvedAssociations if (matchedAttribute?.LastModified >= attribute.LastModified && isStrategy(ImportStrategy.Merge)) continue; topic.Attributes.SetValue( attribute.Key, - getAttributeValue(attribute) + attribute.Value ); } @@ -446,14 +473,6 @@ bool useCustomMergeRules(AttributeData attribute) => (attribute.Key is "LastModified" && options!.LastModifiedStrategy is not LastModifiedImportStrategy.Inherit) || (attribute.Key is "LastModifiedBy" && options!.LastModifiedByStrategy is not LastModifiedImportStrategy.Inherit); - /*------------------------------------------------------------------------------------------------------------------------ - | Get attribute value - \-----------------------------------------------------------------------------------------------------------------------*/ - string? getAttributeValue(AttributeData attribute) => - attribute.Key.EndsWith("ID", StringComparison.InvariantCultureIgnoreCase)? - GetTopicId(topic, attribute.Value) : - attribute.Value; - } /*========================================================================================================================== @@ -502,34 +521,5 @@ bool useCustomMergeRules(AttributeData attribute) => } - /*========================================================================================================================== - | GET TOPIC ID - \-------------------------------------------------------------------------------------------------------------------------*/ - /// - /// Given a UniqueKey, lookup the topic in the topic graph and return the TopicID. If no value can be - /// found, the original UniqueKey is returned. - /// - /// - /// Note that this function is exclusively required for maintaining backward compatibility with exported using the option, which was the default in - /// OnTopic Data Transfer 2.x. With the release of OnTopic 5.0.0, and OnTopic Data Transfer 3.0.0, implementers should - /// prefer the use of . The method continues to be - /// included primarily for backward compatibility with legacy JSON data. - /// - /// The source to operate off of. - /// The to retrieve the for. - private static string? GetTopicId(Topic topic, string? uniqueKey) { - if (uniqueKey!.StartsWith("Root", StringComparison.InvariantCultureIgnoreCase)) { - var target = topic.GetByUniqueKey(uniqueKey); - if (target is not null and { IsNew: false }) { - return target.Id.ToString(CultureInfo.CurrentCulture); - } - else { - return null; - } - } - return uniqueKey; - } - } //Class } //Namespace \ No newline at end of file From 93750a859b1cdb1c5beb4a2aac2bf5dd9af4a006 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:20:52 -0800 Subject: [PATCH 41/97] Fixed bug in finding matched references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The query for finding matched references was comparing `Attributes` to `References`—which won't throw an error, but also won't return the expected results. --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 641a038..040630d 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -411,7 +411,7 @@ attribute.Value is not null && //First delete any unmatched records, if appropriate if (options.DeleteUnmatchedReferences) { var unmatchedReferences = topic.References.Where(a1 => - !topicData.Attributes.Any(a2 => a1.Key == a2.Key) + !topicData.References.Any(a2 => a1.Key == a2.Key) ); foreach (var reference in unmatchedReferences.ToArray()) { topic.References.Remove(reference); From 36e73e041a79e98e5882a2423f4c866465419a32 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:22:08 -0800 Subject: [PATCH 42/97] Removed unnecessary `useCustomMergeRules()` check The `useCustomMergeRules()` check handles specialty logic for handling the `LastModified` and `LastModifiedBy` attributes. As these don't exist in `TopicData.References`, the check serves no purpose. (It wasn't hurting anything, but it also didn't do anything outside of waste a couple of processing cycles.) --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 040630d..8639606 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -420,7 +420,6 @@ attribute.Value is not null && //Update records based on the source collection foreach (var reference in topicData.References) { - if (useCustomMergeRules(reference)) continue; var matchedReference = topic.References.FirstOrDefault(a => a.Key == reference.Key); if (matchedReference is not null && isStrategy(ImportStrategy.Add)) continue; if (matchedReference?.LastModified >= reference.LastModified && isStrategy(ImportStrategy.Merge)) continue; From 0e51687576ef94048808ff899c3bd7747d3c97bc Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:23:03 -0800 Subject: [PATCH 43/97] Simplified the logic for handling the `LastModifiedStrategy` While the `switch` statement is more consistent with the subsequent conditions, it can be reduced to a couple of lines by making it a simple `if` statement with an `or` clause. --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 8639606..1858a8f 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -351,13 +351,8 @@ attribute.Value is not null && \-----------------------------------------------------------------------------------------------------------------------*/ if (topic.Attributes.IsDirty()) { - switch (options.LastModifiedStrategy) { - case LastModifiedImportStrategy.Current: - topic.Attributes.SetValue("LastModified", DateTime.Now.ToString(CultureInfo.CurrentCulture)); - break; - case LastModifiedImportStrategy.System: - topic.Attributes.SetValue("LastModified", DateTime.Now.ToString(CultureInfo.CurrentCulture)); - break; + if (options.LastModifiedStrategy is LastModifiedImportStrategy.Current or LastModifiedImportStrategy.System) { + topic.Attributes.SetValue("LastModified", DateTime.Now.ToString(CultureInfo.CurrentCulture)); } switch (options.LastModifiedByStrategy) { From 487768e8faa770d1b964626c8bbe0a2e3fb29bad Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 14:29:58 -0800 Subject: [PATCH 44/97] Update unit tests to look for legacy topic references in `References` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legacy topic references are attributes whose `key` end with `Id` and `Value` refers to a `UniqueKey`. They were stored as attributes because there wasn't another way to establish a keyed reference to a single topic. That is no longer the case now that we have full support for `Topic.References`. As such, on `Import()`, these are now migrated to `TopicData.References` and imported into `Topic.References` (6d060d0). As part of this, legacy topic references that don't pass initial validation are now imported as attributes—where as, previously, they were skipped. This helps avoid accidental data loss, and especially for situations where the topic reference may have been a false positive. (A risk with using this option previously was that it might mistake attributes that just happen to fit the pattern, but were never intended as topic references.) To support this, the unit tests need to be updated to a) look for the legacy topic references in `Topic.References`, and b) expect that unmapped or orphaned topic references will continue to show up in `Topic.Attributes`, instead of being deleted. --- OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs | 2 +- .../TopicExtensionsTest.cs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 05d303a..647e15a 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -112,7 +112,7 @@ public void ExportWithLegacyTopicReferences_ExternalTopicReference_ExportsUnique var topicData = topic.Export( new() { - IncludeExternalAssociations = true, + IncludeExternalAssociations = true, TranslateLegacyTopicReferences = true } ); diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index d9f723a..1f89e09 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -184,7 +184,8 @@ public void Export_ExcludesReservedAttributes() { var topicData = topic.Export(); - Assert.AreEqual(0, topicData.Attributes.Count); + Assert.AreEqual(1, topicData.Attributes.Count); + Assert.AreEqual("8", topicData.Attributes.FirstOrDefault().Value); } @@ -207,8 +208,10 @@ public void ExportWithLegacyTopicReferences_OutOfScope_SkipsReference() { var topicData = topic.Export(); topicData.Attributes.TryGetValue("SomeId", out var someAttribute); + topicData.References.TryGetValue("Some", out var someReference); - Assert.IsNull(someAttribute); + Assert.IsNotNull(someAttribute); + Assert.IsNull(someReference); } @@ -229,8 +232,12 @@ public void ExportWithLegacyTopicReferences_MissingTopicReference_SkipsReference var topicData = topic.Export(); topicData.Attributes.TryGetValue("InitialBid", out var initialBidAttribute); + topicData.References.TryGetValue("InitialB", out var initialBReference); + topicData.References.TryGetValue("InitialB", out var initialBidReference); - Assert.IsNull(initialBidAttribute); + Assert.IsNotNull(initialBidAttribute); + Assert.IsNull(initialBReference); + Assert.IsNull(initialBidReference); } @@ -746,7 +753,7 @@ public void Import_TopicDataWithLegacyTopicReference_MapsTopicID() { topic.Import(topicData); - Assert.AreEqual("5", topic.Attributes.GetValue("SomeId")); + Assert.AreEqual(5, topic.References.GetValue("Some")?.Id?? -1); } From 9b2a1e226af5ac870e5d3ef191aaeef2a2676b27 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:38:34 -0800 Subject: [PATCH 45/97] Incorporate `References` into deserialize topic graph unit test This is a comprehensive unit test that evaluates deserializing a full topic graph in JSON format into a `TopicData` graph. --- .../DeserializationTest.cs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 6dcfd6c..9cadcc9 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -172,6 +172,11 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Key = "Test", LastModified = DateTime.Now }; + var sourceReferenceData = new AttributeData() { + Key = "Test", + Value = "Root:Reference", + LastModified = DateTime.Now + }; var sourceChildTopicData = new TopicData() { Key = "Child", UniqueKey = "Root:Test:Child", @@ -200,6 +205,13 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"\"Relationships\":[\"Root:Web\"]" + $"}}" + $"]," + + $"\"References\":[" + + $"{{" + + $"\"Key\":\"{sourceReferenceData.Key}\"," + + $"\"Value\":\"{sourceReferenceData.Value}\"," + + $"\"LastModified\":\"{sourceReferenceData.LastModified:o}\"" + + $"}}"+ + $"]," + $"\"Children\":[" + $"{{" + $"\"Key\":\"{sourceChildTopicData.Key}\"," + @@ -213,10 +225,11 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"}}"; - var topicData = JsonSerializer.Deserialize(json); - var relationshipData = topicData.Relationships.FirstOrDefault(); - var attributeData = topicData.Attributes.FirstOrDefault(); - var childTopicData = topicData.Children.FirstOrDefault(); + var topicData = JsonSerializer.Deserialize(json); + var relationshipData = topicData.Relationships.FirstOrDefault(); + var referenceData = topicData.References.FirstOrDefault(); + var attributeData = topicData.Attributes.FirstOrDefault(); + var childTopicData = topicData.Children.FirstOrDefault(); Assert.AreEqual(sourceTopicData.Key, topicData.Key); Assert.AreEqual(sourceTopicData.UniqueKey, topicData.UniqueKey); @@ -229,6 +242,10 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Assert.AreEqual(sourceRelationshipData.Relationships.Count, relationshipData.Relationships.Count); Assert.AreEqual(sourceRelationshipData.Relationships.FirstOrDefault(), relationshipData.Relationships.FirstOrDefault()); + Assert.AreEqual(sourceReferenceData.Key, referenceData.Key); + Assert.AreEqual(sourceReferenceData.Value, referenceData.Value); + Assert.AreEqual(sourceReferenceData.LastModified, referenceData.LastModified); + Assert.AreEqual(sourceAttributeData.Key, attributeData.Key); Assert.AreEqual(sourceAttributeData.Value, attributeData.Value); Assert.AreEqual(sourceAttributeData.LastModified, attributeData.LastModified); From 2cc87989f03ef6a05b136288c125f90729c3d881 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:39:57 -0800 Subject: [PATCH 46/97] Ensure that external topic references are excluded by default --- .../ExportOptionsTest.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 647e15a..7332a79 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -95,6 +95,30 @@ public void Export_TopicWithRelationships_ExcludesExternalReferences() { } + /*========================================================================================================================== + | TEST: EXPORT: TOPIC WITH REFERENCES: EXCLUDES EXTERNAL ASSOCIATIONS + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a with several and ensures that the collection does not include external associations�i.e., references that refer to s outside of the current export scope. + /// + [TestMethod] + public void Export_TopicWithReferences_ExcludesExternalAssociations() { + + var rootTopic = TopicFactory.Create("Root", "Container"); + var topic = TopicFactory.Create("Test", "Container", rootTopic); + var relatedTopic = TopicFactory.Create("Related", "Container", rootTopic); + + topic.References.SetValue("Related", relatedTopic); + + var topicData = topic.Export(); + + Assert.IsNotNull(topicData); + Assert.AreEqual(0, topicData.References.Count); + + } + /*========================================================================================================================== | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: EXTERNAL TOPIC REFERENCE: EXPORTS UNIQUE KEY \-------------------------------------------------------------------------------------------------------------------------*/ From 5cdaeb00f689198d89a399f4e67a7c634f24137f Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:40:54 -0800 Subject: [PATCH 47/97] Ensure that `Import()` with `Merge` skips newer `Topic.References` --- .../ImportOptionsTest.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index 0e7ce70..a56d456 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -162,6 +162,56 @@ public void ImportAsOverwrite_TopicDataWithAttributes_ReplacesNewerValue() { } + /*========================================================================================================================== + | TEST: IMPORT: TOPIC DATA WITH REFERENCES: SKIPS NEWER VALUES + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a with and ensures that the collection is set correctly. + /// + [TestMethod] + public void ImportAsMerge_TopicDataWithReferences_SkipsNewerValues() { + + var topic = TopicFactory.Create("Test", "Container"); + var referencedTopic1 = TopicFactory.Create("Referenced1", "Container", topic); + var referencedTopic2 = TopicFactory.Create("Referenced2", "Container", topic); + var topicData = new TopicData() { + Key = topic.Key, + UniqueKey = topic.GetUniqueKey(), + ContentType = topic.ContentType + }; + + topic.References.SetValue("Reference", referencedTopic1); + topic.References.SetValue("OldReference", referencedTopic2, null, DateTime.UtcNow.AddDays(-1)); + + topicData.References.Add( + new() { + Key = "Reference", + Value = referencedTopic2.GetUniqueKey(), + LastModified = DateTime.UtcNow.AddDays(-1) + } + ); + + topicData.References.Add( + new() { + Key = "OldReference", + Value = referencedTopic1.GetUniqueKey(), + LastModified = DateTime.UtcNow + } + ); + + topic.Import( + topicData, + new() { + Strategy = ImportStrategy.Merge + } + ); + + Assert.AreEqual(referencedTopic1, topic.References.GetValue("Reference")); + Assert.AreEqual(referencedTopic1, topic.References.GetValue("OldReference")); + + } + /*========================================================================================================================== | TEST: IMPORT AS REPLACE: TOPIC DATA WITH RELATIONSHIPS: DELETES ORPHANED RELATIONSHIPS \-------------------------------------------------------------------------------------------------------------------------*/ From c1764adbaa3424be051ec06d3a2b2c5ded25281a Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:42:37 -0800 Subject: [PATCH 48/97] Incorporate `References` into seralize topic graph unit test This is a comprehensive unit test that evaluates serializing a `TopicData` graph into a JSON output. --- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 7eee7f0..60d30f6 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -4,8 +4,6 @@ | Project Topics Library \=============================================================================================================================*/ using System; -using System.Linq; -using System.Runtime.Serialization; using System.Text.Json; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -119,6 +117,10 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { var relationshipData = new RelationshipData() { Key = "Test" }; + var referenceData = new AttributeData() { + Key = "Test", + LastModified = DateTime.Now + }; var attributeData = new AttributeData() { Key = "Test", LastModified = DateTime.Now @@ -131,6 +133,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { relationshipData.Relationships.Add("Root:Web"); topicData.Relationships.Add(relationshipData); + topicData.References.Add(referenceData); topicData.Attributes.Add(attributeData); topicData.Children.Add(childTopicData); @@ -150,7 +153,12 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Relationships\":[\"Root:Web\"]" + $"}}" + $"]," + - $"\"References\":[]," + + $"\"References\":[" + + $"{{" + + $"\"Key\":\"{referenceData.Key}\"," + + $"\"LastModified\":\"{referenceData.LastModified:o}\"" + + $"}}"+ + $"]," + $"\"Children\":[" + $"{{" + $"\"Key\":\"{childTopicData.Key}\"," + From aad408188feae31d16d480b30f1163b33961eb4c Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:43:06 -0800 Subject: [PATCH 49/97] Ensure basic support for `References` on `Export()` --- .../TopicExtensionsTest.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 1f89e09..ce54106 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -142,6 +142,37 @@ public void ExportWithExternalAssociations_TopicWithRelationships_ExcludesExtern } + /*========================================================================================================================== + | TEST: EXPORT: TOPIC WITH REFERENCES: MAPS REFERENCE DATA COLLECTION + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a with several and ensures that the collection is set correctly. + /// + [TestMethod] + public void Export_TopicWithReferences_MapsReferenceDataCollection() { + + var rootTopic = TopicFactory.Create("Root", "Container"); + var topic = TopicFactory.Create("Test", "Container", rootTopic); + var referencedTopic = TopicFactory.Create("Referenced", "Container", rootTopic); + + topic.References.SetValue("Referenced", referencedTopic); + + var topicData = rootTopic.Export( + new() { + IncludeChildTopics = true + } + ); + + var childTopicData = topicData.Children.FirstOrDefault()?? new TopicData(); + + Assert.IsNotNull(topicData); + Assert.IsNotNull(childTopicData); + Assert.AreEqual(1, childTopicData.References.Count); + Assert.AreEqual("Root:Referenced", childTopicData.References.FirstOrDefault().Value); + + } + /*========================================================================================================================== | TEST: EXPORT: TOPIC WITH NESTED TOPICS: EXCLUDES NESTED TOPICS \-------------------------------------------------------------------------------------------------------------------------*/ From 8287d45a4ffb2f4597c983bdd3e2a642fc7f1980 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:43:45 -0800 Subject: [PATCH 50/97] Ensure basic support for `References` on `Import()` --- .../TopicExtensionsTest.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index ce54106..83b2cdf 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -654,6 +654,37 @@ public void Import_TopicDataWithRelationships_MaintainsExisting() { } + /*========================================================================================================================== + | TEST: IMPORT: TOPIC DATA WITH REFERENCES: MAPS REFERENCE COLLECTION + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a with and ensures that the collection is set correctly. + /// + [TestMethod] + public void Import_TopicDataWithReferences_MapsReferenceCollection() { + + var rootTopic = TopicFactory.Create("Root", "Container"); + var topic = TopicFactory.Create("Test", "Container", rootTopic); + var referencedTopic = TopicFactory.Create("Referenced", "Container", rootTopic); + var topicData = new TopicData() { + Key = topic.Key, + UniqueKey = topic.GetUniqueKey(), + ContentType = topic.ContentType + }; + var referenData = new AttributeData() { + Key = "Referenced", + Value = referencedTopic.GetUniqueKey() + }; + + topicData.References.Add(referenData); + + topic.Import(topicData); + + Assert.AreEqual(referencedTopic, topic.References.GetValue("Referenced")); + + } + /*========================================================================================================================== | TEST: IMPORT: TOPIC DATA WITH CHILD: MAPS NEW TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ From d0cb983bd06c648263957014fab532752aad060f Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:44:05 -0800 Subject: [PATCH 51/97] Ensure merge support for `References` on `Import()` --- .../TopicExtensionsTest.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 83b2cdf..d3cfa18 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -685,6 +685,40 @@ public void Import_TopicDataWithReferences_MapsReferenceCollection() { } + /*========================================================================================================================== + | TEST: IMPORT: TOPIC DATA WITH REFERENCES: MAINTAINS EXISTING + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a with and ensures that the collection is set correctly. + /// + [TestMethod] + public void Import_TopicDataWithReferences_MaintainsExisting() { + + var rootTopic = TopicFactory.Create("Root", "Container"); + var topic = TopicFactory.Create("Test", "Container", rootTopic); + var referencedTopic1 = TopicFactory.Create("Referenced1", "Container", rootTopic); + var referencedTopic2 = TopicFactory.Create("Referenced2", "Container", rootTopic); + var topicData = new TopicData() { + Key = topic.Key, + UniqueKey = topic.GetUniqueKey(), + ContentType = topic.ContentType + }; + var referenceData = new AttributeData() { + Key = "Referenced", + Value = referencedTopic2.GetUniqueKey() + }; + + topic.References.SetValue("Referenced", referencedTopic1); + + topicData.References.Add(referenceData); + + topic.Import(topicData); + + Assert.AreEqual(referencedTopic1, topic.References.GetValue("Referenced")); + + } + /*========================================================================================================================== | TEST: IMPORT: TOPIC DATA WITH CHILD: MAPS NEW TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ From 1c6244ddfd2ada60fde7b3e5b221fa9353cddf73 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 15:45:14 -0800 Subject: [PATCH 52/97] Correct outdated nomenclature from `References` to `Associations` This relates to the corresponding update to the `ExportOptions` option, `IncludeExternalAssociations` (a471a7e). --- OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 7332a79..7c151a9 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -72,15 +72,15 @@ public void ExportWithNestedTopic_TopicWithNestedTopics_IncludesNestedTopics() { } /*========================================================================================================================== - | TEST: EXPORT: TOPIC WITH RELATIONSHIPS: EXCLUDES EXTERNAL REFERENCES + | TEST: EXPORT: TOPIC WITH RELATIONSHIPS: EXCLUDES EXTERNAL ASSOCIATIONS \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with several and ensures that the collection does not include external references—i.e., relationships that refer - /// to s outside of the current export scope. + /// cref="TopicData.Relationships"/> collection does not include external associations—i.e., relationships that + /// refer to s outside of the current export scope. /// [TestMethod] - public void Export_TopicWithRelationships_ExcludesExternalReferences() { + public void Export_TopicWithRelationships_ExcludesExternalAssociations() { var rootTopic = TopicFactory.Create("Root", "Container"); var topic = TopicFactory.Create("Test", "Container", rootTopic); @@ -100,7 +100,7 @@ public void Export_TopicWithRelationships_ExcludesExternalReferences() { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a with several and ensures that the collection does not include external associations�i.e., references that refer to collection does not include external associations—i.e., references that refer to s outside of the current export scope. /// [TestMethod] From e321358d9c814fec2cf82d2c00a8c808dedbd890 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 16:05:39 -0800 Subject: [PATCH 53/97] Rename `AttributeData` to `RecordData` This better maps to the new `TrackedRecord` class in OnTopic, which is the foundation for both `Topic.Attributes` and `Topic.References`, which corresponds to the use in the OnTopic Data Transfer Library, where `RecordData` is now the foundation for both `TopicData.Attributes` and `TopicData.References`. This includes updating unit test and variables associated with `RecordData` instances. --- .../DeserializationTest.cs | 20 ++++++------- .../SerializationTest.cs | 18 ++++++------ .../TopicExtensionsTest.cs | 28 +++++++++---------- .../AttributeDataCollection.cs | 10 +++---- .../Interchange/ImportStrategy.cs | 8 +++--- .../Interchange/LastModifiedImportStrategy.cs | 4 +-- .../Interchange/TopicExtensions.cs | 2 +- .../{AttributeData.cs => RecordData.cs} | 15 +++++----- OnTopic.Data.Transfer/TopicData.cs | 8 +++--- 9 files changed, 57 insertions(+), 56 deletions(-) rename OnTopic.Data.Transfer/{AttributeData.cs => RecordData.cs} (81%) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 9cadcc9..457ff4b 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -123,15 +123,15 @@ public void Deserialize_RelationshipData_ReturnsExpectedResults() { } /*========================================================================================================================== - | TEST: DESERIALIZE: ATTRIBUTE DATA: RETURNS EXPECTED RESULTS + | TEST: DESERIALIZE: RECORD DATA: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a json string and attempts to deserialize it as a class. + /// Creates a json string and attempts to deserialize it as a class. /// [TestMethod] - public void Deserialize_AttributeData_ReturnsExpectedResults() { + public void Deserialize_RecordData_ReturnsExpectedResults() { - var sourceData = new AttributeData() { + var sourceData = new RecordData() { Key = "Test", LastModified = DateTime.Now }; @@ -143,11 +143,11 @@ public void Deserialize_AttributeData_ReturnsExpectedResults() { $"}}"; - var attributeData = JsonSerializer.Deserialize(json); + var recordData = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, attributeData.Key); - Assert.AreEqual(sourceData.Value, attributeData.Value); - Assert.AreEqual(sourceData.LastModified, attributeData.LastModified); + Assert.AreEqual(sourceData.Key, recordData.Key); + Assert.AreEqual(sourceData.Value, recordData.Value); + Assert.AreEqual(sourceData.LastModified, recordData.LastModified); } @@ -168,11 +168,11 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { var sourceRelationshipData= new RelationshipData() { Key = "Test" }; - var sourceAttributeData = new AttributeData() { + var sourceAttributeData = new RecordData() { Key = "Test", LastModified = DateTime.Now }; - var sourceReferenceData = new AttributeData() { + var sourceReferenceData = new RecordData() { Key = "Test", Value = "Root:Reference", LastModified = DateTime.Now diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 60d30f6..644a3ad 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -75,26 +75,26 @@ public void Serialize_RelationshipData_ReturnsExpectedResults() { } /*========================================================================================================================== - | TEST: SERIALIZE: ATTRIBUTE DATA: RETURNS EXPECTED RESULTS + | TEST: SERIALIZE: RECORD DATA: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a , serializes it, and confirms the resulting JSON. + /// Creates a , serializes it, and confirms the resulting JSON. /// [TestMethod] - public void Serialize_AttributeData_ReturnsExpectedResults() { + public void Serialize_RecordData_ReturnsExpectedResults() { - var attributeData = new AttributeData() { + var recordData = new RecordData() { Key = "Test", LastModified = DateTime.Now }; var expected = $"{{" + - $"\"Key\":\"{attributeData.Key}\"," + + $"\"Key\":\"{recordData.Key}\"," + $"\"Value\":null," + - $"\"LastModified\":\"{attributeData.LastModified:o}\"" + + $"\"LastModified\":\"{recordData.LastModified:o}\"" + $"}}"; - var json = JsonSerializer.Serialize(attributeData); + var json = JsonSerializer.Serialize(recordData); Assert.AreEqual(expected, json); @@ -117,11 +117,11 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { var relationshipData = new RelationshipData() { Key = "Test" }; - var referenceData = new AttributeData() { + var referenceData = new RecordData() { Key = "Test", LastModified = DateTime.Now }; - var attributeData = new AttributeData() { + var attributeData = new RecordData() { Key = "Test", LastModified = DateTime.Now }; diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index d3cfa18..027c9bc 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -41,8 +41,8 @@ public void Export_BasicTopic_MapsProperties() { | TEST: EXPORT: BASE TOPIC: MAPS REFERENCE DATA \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a and ensures that a - /// item with a of BasedTopic is correctly set. + /// Creates a with a and ensures that a item + /// with a of BasedTopic is correctly set. /// [TestMethod] public void Export_BaseTopic_MapsReferenceData() { @@ -375,7 +375,7 @@ public void Import_DerivedTopicKey_MapsDerivedTopic() { | TEST: IMPORT: BASE TOPIC: MAPS BASE TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a with the of + /// Creates a with a with the of /// BaseTopic in the collection, which references a topic in the topic graph. /// Ensures that it is correctly wired up. /// @@ -392,7 +392,7 @@ public void Import_BaseTopic_MapsBaseTopic() { ContentType = topic.ContentType }; - var referencedTopicData = new AttributeData() { + var referencedTopicData = new RecordData() { Key = "BaseTopic", Value = $"{baseTopic.GetUniqueKey()}" }; @@ -410,7 +410,7 @@ public void Import_BaseTopic_MapsBaseTopic() { | TEST: IMPORT: BASE TOPIC: MAPS NEW BASE TOPIC \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a with the of + /// Creates a with a with the of /// BaseTopic in the collection, which references a newly imported topic that /// occurs later in the tree, ensuring that the is set correctly. /// @@ -438,7 +438,7 @@ public void Import_BaseTopic_MapsNewBaseTopic() { ContentType = "Container" }; - var baseTopicReference = new AttributeData() { + var baseTopicReference = new RecordData() { Key = "BaseTopic", Value = $"{topicData.UniqueKey}:BaseTopic" }; @@ -478,7 +478,7 @@ public void Import_InvalidBaseTopic_MaintainsExistingValue() { ContentType = topic.ContentType }; - var baseTopicReference = new AttributeData() { + var baseTopicReference = new RecordData() { Key = "BaseTopic", Value = $"{topicData.UniqueKey}:BaseTopic" }; @@ -672,12 +672,12 @@ public void Import_TopicDataWithReferences_MapsReferenceCollection() { UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType }; - var referenData = new AttributeData() { + var referenceData = new RecordData() { Key = "Referenced", Value = referencedTopic.GetUniqueKey() }; - topicData.References.Add(referenData); + topicData.References.Add(referenceData); topic.Import(topicData); @@ -704,7 +704,7 @@ public void Import_TopicDataWithReferences_MaintainsExisting() { UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType }; - var referenceData = new AttributeData() { + var referenceData = new RecordData() { Key = "Referenced", Value = referencedTopic2.GetUniqueKey() }; @@ -824,8 +824,8 @@ public void Import_TopicDataWithChild_SkipsOrphanedChild() { | TEST: IMPORT: TOPIC DATA WITH LEGACY TOPIC REFERENCE: MAPS TOPIC ID \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that references another topic. - /// Confirms that it is converted to a if valid, and otherwise left as is. + /// Creates a with an arbitrary that references another topic. Confirms + /// that it is converted to a if valid, and otherwise left as is. /// [TestMethod] public void Import_TopicDataWithLegacyTopicReference_MapsTopicID() { @@ -857,7 +857,7 @@ public void Import_TopicDataWithLegacyTopicReference_MapsTopicID() { | TEST: IMPORT: TOPIC DATA WITH MISSING LEGACY TOPIC REFERENCE: SKIPS ATTRIBUTE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that references to a missing topic. + /// Creates a with an arbitrary that references to a missing topic. /// Confirms that the attribute is skipped. /// [TestMethod] @@ -888,7 +888,7 @@ public void Import_TopicDataWithMissingLegacyTopicReference_SkipsAttribute() { | TEST: IMPORT: TOPIC DATA WITH INVALID LEGACY TOPIC REFERENCE: IMPORTS ORIGINAL VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that does not reference a topic key + /// Creates a with an arbitrary that does not reference a topic key /// (i.e., it doesn't start with Root). Confirms that the original attribute value is imported. /// [TestMethod] diff --git a/OnTopic.Data.Transfer/AttributeDataCollection.cs b/OnTopic.Data.Transfer/AttributeDataCollection.cs index 78e719e..a312cf3 100644 --- a/OnTopic.Data.Transfer/AttributeDataCollection.cs +++ b/OnTopic.Data.Transfer/AttributeDataCollection.cs @@ -12,21 +12,21 @@ namespace OnTopic.Data.Transfer { | CLASS: ATTRIBUTE DATA COLLECTION \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides a of + /// The class provides a of /// objects. /// - public class AttributeDataCollection: KeyedCollection { + public class AttributeDataCollection: KeyedCollection { /*========================================================================================================================== | OVERRIDE: GET KEY FOR ITEM \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Method must be overridden for the to identify the appropriate - /// from each object. + /// from each object. /// - /// The object from which to extract the key. + /// The object from which to extract the key. /// The key for the specified collection item. - protected override string GetKeyForItem(AttributeData item) { + protected override string GetKeyForItem(RecordData item) { Contract.Requires(item, "The item must be available in order to derive its key."); return item.Key?? ""; } diff --git a/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs b/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs index 700678a..2b63ad7 100644 --- a/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs +++ b/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs @@ -44,8 +44,8 @@ public enum ImportStrategy { | MERGE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Imports all records unless the is older than the target . + /// Imports all records unless the is older than the target . /// /// /// This is generally the preferred import strategy. It imports all new topics, relationships, and attributes, @@ -63,12 +63,12 @@ public enum ImportStrategy { | OVERWRITE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Imports all records, even if the is older than the target even if the is older than the target . /// /// /// This will effectively overwrite any attributes that already exist in the database, even if they were - /// modified after the source . For example, if the starts at + /// modified after the source . For example, if the starts at /// Root:Configuration, any modifications to out-of-the-box titles, descriptions, visibility, sort order, &c. /// will be overwritten with the new values. Any attribute values that it overwrites will still be recoverable as part of /// the attribute versioning schema, with the exception of , which is excluded from diff --git a/OnTopic.Data.Transfer/Interchange/LastModifiedImportStrategy.cs b/OnTopic.Data.Transfer/Interchange/LastModifiedImportStrategy.cs index c384460..ad76ce3 100644 --- a/OnTopic.Data.Transfer/Interchange/LastModifiedImportStrategy.cs +++ b/OnTopic.Data.Transfer/Interchange/LastModifiedImportStrategy.cs @@ -39,8 +39,8 @@ public enum LastModifiedImportStrategy { /// /// When is chosen, existing values will be retained if is /// selected, overwritten if or are selected, - /// and conditionally overwritten based on the date if is selected. + /// and conditionally overwritten based on the date if is selected. /// Inherit = 1, diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 1858a8f..b442e01 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -463,7 +463,7 @@ topic.ContentType is not "List" && options.DeleteUnmatchedChildren /*------------------------------------------------------------------------------------------------------------------------ | Is custom merge rules? \-----------------------------------------------------------------------------------------------------------------------*/ - bool useCustomMergeRules(AttributeData attribute) => + bool useCustomMergeRules(RecordData attribute) => (attribute.Key is "LastModified" && options!.LastModifiedStrategy is not LastModifiedImportStrategy.Inherit) || (attribute.Key is "LastModifiedBy" && options!.LastModifiedByStrategy is not LastModifiedImportStrategy.Inherit); diff --git a/OnTopic.Data.Transfer/AttributeData.cs b/OnTopic.Data.Transfer/RecordData.cs similarity index 81% rename from OnTopic.Data.Transfer/AttributeData.cs rename to OnTopic.Data.Transfer/RecordData.cs index c5ed2e6..d4dca78 100644 --- a/OnTopic.Data.Transfer/AttributeData.cs +++ b/OnTopic.Data.Transfer/RecordData.cs @@ -5,28 +5,29 @@ \=============================================================================================================================*/ using System; using System.Diagnostics.CodeAnalysis; +using OnTopic.Collections.Specialized; namespace OnTopic.Data.Transfer { /*============================================================================================================================ - | CLASS: ATTRIBUTE DATA + | CLASS: RECORD DATA \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides an intermediary data transfer object for facilitating the interchange of - /// objects with JSON data. + /// The class provides an intermediary data transfer object for facilitating the interchange of + /// objects with JSON data. /// /// /// Having a separate class for this serializing topic data introduces some overhead in converting the topic graph to and /// from objects, but in turn greatly simplifies how the serialization process works, and provides /// necessary flexibility in the import process to better account for merging data and handling potential conflicts. /// - public class AttributeData { + public class RecordData { /*========================================================================================================================== | KEY \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Gets or sets the key of the attribute. + /// Gets or sets the of the . /// [NotNull, DisallowNull] public string? Key { get; set; } @@ -35,7 +36,7 @@ public class AttributeData { | PROPERTY: VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Gets the current value of the attribute. + /// Gets or sets the of the . /// public string? Value { get; set; } @@ -43,7 +44,7 @@ public class AttributeData { | PROPERTY: LAST MODIFIED \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Gets or sets the the last time the instance was updated. + /// Gets or sets the the last time the instance was updated. /// public DateTime LastModified { get; set; } = DateTime.MinValue; diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 367b542..3cebede 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -85,8 +85,8 @@ public class TopicData { | ATTRIBUTES \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Provides a collection of representing the attributes from the associated object. + /// Provides a collection of representing the attributes from the associated + /// object. /// public AttributeDataCollection Attributes { get; set; } = new(); @@ -103,8 +103,8 @@ public class TopicData { | TOPIC REFERENCES \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Provides a collection of representing the topic references from the associated object. + /// Provides a collection of representing the topic references from the associated object. /// public AttributeDataCollection References { get; set; } = new(); From 85c234a50172a9efc7cd6920c914c368a5294875 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 16:10:19 -0800 Subject: [PATCH 54/97] Renamed `AttributeDataCollection` to `RecordDataCollection` This better maps to the new `TrackedRecordCollection<>` class in OnTopic, which is the foundation for both `Topic.Attributes` and `Topic.References`, and corresponds to the use in the OnTopic Data Transfer Library, where `RecordDataCollection` is now the foundation for both `TopicData.Attributes` and `TopicData.References`. --- ...AttributeDataCollection.cs => RecordDataCollection.cs} | 8 ++++---- OnTopic.Data.Transfer/TopicData.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) rename OnTopic.Data.Transfer/{AttributeDataCollection.cs => RecordDataCollection.cs} (80%) diff --git a/OnTopic.Data.Transfer/AttributeDataCollection.cs b/OnTopic.Data.Transfer/RecordDataCollection.cs similarity index 80% rename from OnTopic.Data.Transfer/AttributeDataCollection.cs rename to OnTopic.Data.Transfer/RecordDataCollection.cs index a312cf3..e6d1af5 100644 --- a/OnTopic.Data.Transfer/AttributeDataCollection.cs +++ b/OnTopic.Data.Transfer/RecordDataCollection.cs @@ -9,19 +9,19 @@ namespace OnTopic.Data.Transfer { /*============================================================================================================================ - | CLASS: ATTRIBUTE DATA COLLECTION + | CLASS: RECORD DATA COLLECTION \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides a of + /// The class provides a of /// objects. /// - public class AttributeDataCollection: KeyedCollection { + public class RecordDataCollection: KeyedCollection { /*========================================================================================================================== | OVERRIDE: GET KEY FOR ITEM \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Method must be overridden for the to identify the appropriate + /// Method must be overridden for the to identify the appropriate /// from each object. /// /// The object from which to extract the key. diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 3cebede..4eef795 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -88,7 +88,7 @@ public class TopicData { /// Provides a collection of representing the attributes from the associated /// object. /// - public AttributeDataCollection Attributes { get; set; } = new(); + public RecordDataCollection Attributes { get; set; } = new(); /*========================================================================================================================== | RELATIONSHIPS @@ -106,7 +106,7 @@ public class TopicData { /// Provides a collection of representing the topic references from the associated object. /// - public AttributeDataCollection References { get; set; } = new(); + public RecordDataCollection References { get; set; } = new(); /*========================================================================================================================== | CHILDREN From 17a94f3c6b1923b85bdeb94790ee2086571e5d9b Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 16:16:28 -0800 Subject: [PATCH 55/97] Updated documentation to reflect recent changes This includes the rename of `AttributeData` and `AttributeDataCollection` to `RecordData` and `RecordDataCollection`; the introduction of support for `TopicData.References`; and the rename of `IncludeExternalReferences` to `IncludeExternalAssociations`. It also includes a reference to the next version number that this release is targeting: 3.0.0. --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3f7bf29..4ac6fee 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,13 @@ Technically, the `Topic` class The `OnTopic.Data.Transfer` assembly includes three basic data transfer classes along side a custom collection class: - `TopicData`: Maps to the `Topic` class, and includes the following collections: - - `Children` (`List[TopicData]`) - - `Attributes` (`List[AttributeData]`) - - `AttributeData`: Maps to the `AttributeValue` class, and represents an individual attribute. + - `Children` (`Collection`) + - `Attributes` (`KeyedCollection`) + - `RecordData`: Maps to the `TrackedRecord` class, and represents an individual attribute. - `Relationships` ([`RelationshipDataCollection`](OnTopic.Data.Transfer/RelationshipDataCollection.cs)) - `RelationshipData`: Maps to the `NamedTopicCollection`, and represents a relationship key, as well as a list of references to related topics via their `Topic.GetUniqueKey()` value. + - `References` (`KeyedCollection`) + - `RecordData`: Maps to the `TrackedRecord` class, and represents an individual topic reference. ## Data Interchange The `OnTopic.Data.Transfer.Interchange` namespace includes extension methods for the `OnTopic.Topic` entity which allow a `TopicData` graph to be exported from a `Topic` graph—or imported back into one: @@ -37,7 +39,7 @@ The `OnTopic.Data.Transfer.Interchange` namespace includes extension methods for ### Export Options Optionally, the `Topic.Export()` extension method will accept an [`ExportOptions`](OnTopic.Data.Transfer/Interchange/ExportOptions.cs) object as argument in order to fine-tune the business logic for the export. This includes the following options: -- `IncludeExternalReferences`: Enables relationships to be exported, even if the topics they point to fall outside the scope of the current export. +- `IncludeExternalAssociations`: Enables associations—such as relationships and topic references—to be exported, even if the topics they point to fall outside the scope of the current export. - `IncludeNestedTopics`: Includes nested topic as part of the export. - `IncludeChildTopics`: Recursively includes _all_ child topics—including nested topics—as part of the export. Implies `IncludeNestedTopics`. @@ -53,6 +55,7 @@ In addition to the `Strategy` property, the `ImportOptions` also allows the beha - `DeleteUnmatchedAttributes`: Determines if unmatched attributes should be deleted. Defaults to `false` unless `ImportStrategy.Replace` is set. - `DeleteUnmatchedRelationships`: Determines if unmatched relationships should be deleted. Defaults to `false` unless `ImportStrategy.Replace` is set. +- `DeleteUnmatchedReferences`: Determines if unmatched topic references should be deleted. Defaults to `false` unless `ImportStrategy.Replace` is set. - `DeleteUnmatchedChildren`: Determines if unmatched children should be deleted. Defaults to `false` unless `ImportStrategy.Replace` is set. - `DeleteUnmatchedNestedTopics`: Determines if unmatched nested topics should be deleted. Defaults to `false` unless `ImportStrategy.Replace` is set. - `OverwriteContentType`: Determines if the `ContentType` on an existing `Topic` should be updated if the `TopicData` value is different. Defaults to `false` unless `ImportStrategy` is set to `Ovewrite` or `Replace`. @@ -88,7 +91,7 @@ Installation can be performed by providing a ` to the `OnTo - + ``` From 5693b5e1de896137c0d8282d090916569956407b Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 16:26:39 -0800 Subject: [PATCH 56/97] Fixed references to next version of OnTopic Data Transfer Library I somehow convinced myself that the next major version of the OnTopic Data Transfer Library was 3.0.0, not 2.0.0. Whoops! I've fixed references to these, as appropriate. --- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index b442e01..425d7b7 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -479,7 +479,7 @@ bool useCustomMergeRules(RecordData attribute) => /// /// Note that this function is exclusively required for maintaining backward compatibility the option/ With the release of OnTopic 5.0.0, and OnTopic Data Transfer - /// 3.0.0, implementers should prefer the use of . The . The method continues to be included primarily for backward compatibility with legacy database /// configurations. /// diff --git a/README.md b/README.md index 4ac6fee..5a82978 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Installation can be performed by providing a ` to the `OnTo - + ``` From 1556bf03093e4aa8ac1faacd5a85aa59be2ef96b Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Fri, 12 Feb 2021 16:27:04 -0800 Subject: [PATCH 57/97] Updated copyright data for package metadata It's now 2021, yo. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index d378ef9..c3d6cdc 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -14,7 +14,7 @@ Ignia OnTopic Libraries for supporting the import and export of OnTopic entities. - ©2020 Ignia, LLC + ©2021 Ignia, LLC bin\$(Configuration)\ Ignia From c66edce5186eef13e9035b5b0e33955c54e8c17a Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 11:46:01 -0800 Subject: [PATCH 58/97] Introduced new `UnresolvedAssociation` type The `UnresolvedAssociation` type models an unresolved association, including the association type, association key, source topic, and the unique key of the target topic. It's intended to formalize the criteria for tracking an unresolved association, as a replacement for the tuple currently used. This includes a new `AssociationType` enum that allows us to determine the type of association. --- .../Interchange/AssociationType.cs | 43 ++++++++ .../Interchange/UnresolvedAssociation.cs | 98 +++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 OnTopic.Data.Transfer/Interchange/AssociationType.cs create mode 100644 OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs diff --git a/OnTopic.Data.Transfer/Interchange/AssociationType.cs b/OnTopic.Data.Transfer/Interchange/AssociationType.cs new file mode 100644 index 0000000..85800cd --- /dev/null +++ b/OnTopic.Data.Transfer/Interchange/AssociationType.cs @@ -0,0 +1,43 @@ +/*============================================================================================================================== +| Author Ignia, LLC +| Client Ignia, LLC +| Project Topics Library +\=============================================================================================================================*/ + +namespace OnTopic.Data.Transfer.Interchange { + + /*============================================================================================================================ + | ENUM: ASSOCIATION TYPE + \---------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Enum that specifies the types of associations between topics that the OnTopic Data Transfer library neeeds to explicitly + /// track. + /// + public enum AssociationType { + + /*========================================================================================================================== + | NONE + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// No association type is selected. + /// + None = 0, + + /*========================================================================================================================== + | RELATIONSHIP + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// The association pertains to either or . + /// + Relationship = 1, + + /*========================================================================================================================== + | REFERENCE + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// The association pertains to either or . + /// + Reference = 2 + + } //Enum +} //Namespace \ No newline at end of file diff --git a/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs b/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs new file mode 100644 index 0000000..bb4b82c --- /dev/null +++ b/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs @@ -0,0 +1,98 @@ +/*============================================================================================================================== +| Author Ignia, LLC +| Client Ignia, LLC +| Project Topics Library +\=============================================================================================================================*/ +using System.Diagnostics.CodeAnalysis; +using OnTopic.Internal.Diagnostics; + +namespace OnTopic.Data.Transfer.Interchange { + + /*============================================================================================================================ + | CLASS: UNRESOLVED ASSOCIATION + \---------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Tracks unresolved associations so that they can be resolved later in the import process. + /// + /// + /// During the process, associations—such as + /// relationships or topic references—may be present in the graph that aren't yet available in the + /// graph. This can happen, for instance, because a instance is associated with + /// another instance that hasn't yet been imported, due to its location in the graph. The class helps address this by tracking the , + /// , , and needed to later create the + /// association. + /// + internal record UnresolvedAssociation { + + /*========================================================================================================================== + | CONSTRUCTOR + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a new instance of a record. + /// + /// The used to identify the association. + /// + /// The determines if the association is a relationship or a topic reference. + /// + /// The of the source . + /// The of the targer . + internal UnresolvedAssociation(AssociationType type, string key, Topic source, string target) { + + /*------------------------------------------------------------------------------------------------------------------------ + | Validate parameters + \-----------------------------------------------------------------------------------------------------------------------*/ + Contract.Requires(type, nameof(type)); + Contract.Requires(key, nameof(key)); + Contract.Requires(source, nameof(source)); + Contract.Requires(target, nameof(target)); + + /*------------------------------------------------------------------------------------------------------------------------ + | Set properties + \-----------------------------------------------------------------------------------------------------------------------*/ + SourceTopic = source; + Key = key; + AssociationType = type; + TargetTopicKey = target; + + } + + /*========================================================================================================================== + | PROPERTY: ASSOCIATION TYPE + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Determines the collection that the association originates from, such as or . + /// + internal AssociationType AssociationType { get; init; } + + /*========================================================================================================================== + | PROPERTY: KEY + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// The key used for the association. + /// + /// + /// This maps to either the , if the association is from , + /// or , if the association is from . + /// + internal string Key { get; init; } + + /*========================================================================================================================== + | PROPERTY: SOURCE TOPIC + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// The that the association should be created on. + /// + internal Topic SourceTopic { get; init; } + + /*========================================================================================================================== + | PROPERTY: TARGET TOPIC KEY + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// The of the target that the association points to. + /// + internal string TargetTopicKey { get; init; } + + } //Enum +} //Namespace \ No newline at end of file From 7021c550509b19224fb17323fa6f1d53b0195680 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 11:46:44 -0800 Subject: [PATCH 59/97] Replace the `Tuple` with the new `UnresolvedAssociation` This simplifies and formalizes the code, making it easier to read and understand. --- .../Interchange/TopicExtensions.cs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 425d7b7..8cef5dc 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -183,7 +183,7 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import /*------------------------------------------------------------------------------------------------------------------------ | Establish cache \-----------------------------------------------------------------------------------------------------------------------*/ - var unresolvedAssociations = new List>(); + var unresolvedAssociations = new List(); /*------------------------------------------------------------------------------------------------------------------------ | Handle first pass @@ -193,13 +193,10 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import /*------------------------------------------------------------------------------------------------------------------------ | Attempt to resolve outstanding assocations \-----------------------------------------------------------------------------------------------------------------------*/ - foreach (var relationship in unresolvedAssociations) { + foreach (var association in unresolvedAssociations) { //Attempt to find the target association - var source = relationship.Item1; - var isRelationship = relationship.Item2; - var key = relationship.Item3; - var target = topic.GetByUniqueKey(relationship.Item4); + var target = topic.GetByUniqueKey(association.TargetTopicKey); //If the association STILL can't be resolved, skip it if (target is null) { @@ -207,13 +204,13 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import } //Wire up relationships - else if (isRelationship) { - source.Relationships.SetValue(key, target); + else if (association.AssociationType is AssociationType.Relationship) { + association.SourceTopic.Relationships.SetValue(association.Key, target); } //Wire up topic references else { - source.References.SetValue(key, target); + association.SourceTopic.References.SetValue(association.Key, target); } } @@ -246,7 +243,7 @@ private static void Import( this Topic topic, TopicData topicData, [NotNull]ImportOptions? options, - List> unresolvedAssociations + List unresolvedAssociations ) { /*------------------------------------------------------------------------------------------------------------------------ @@ -393,7 +390,7 @@ attribute.Value is not null && topic.Relationships.SetValue(relationship.Key, relatedTopic); } else { - unresolvedAssociations.Add(new(topic, true, relationship.Key!, relatedTopicKey)); + unresolvedAssociations.Add(new(AssociationType.Relationship, relatedTopicKey, topic, relatedTopicKey)); } } } @@ -426,7 +423,7 @@ attribute.Value is not null && ); } else { - unresolvedAssociations.Add(new(topic, false, reference.Key, reference.Value)); + unresolvedAssociations.Add(new(AssociationType.Reference, reference.Key, topic, reference.Value)); } } From 402349c565773d251009f94ac6da86b4e42292d7 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 11:48:12 -0800 Subject: [PATCH 60/97] Introduced `IsInternal` polyfill The new `UnresolvedAssociation` type uses `init` accessors instead of `set`. This is a C# 9.0 feature, but only available in .NET 5. Legacy .NET Standard libraries can be retrofitted to support `init`, however, by exposing an `IsExternalInit` polyfill. --- .../Internal/IsExternalInit.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 OnTopic.Data.Transfer/Internal/IsExternalInit.cs diff --git a/OnTopic.Data.Transfer/Internal/IsExternalInit.cs b/OnTopic.Data.Transfer/Internal/IsExternalInit.cs new file mode 100644 index 0000000..2bd828a --- /dev/null +++ b/OnTopic.Data.Transfer/Internal/IsExternalInit.cs @@ -0,0 +1,19 @@ +/*============================================================================================================================== +| Author Ignia, LLC +| Client Ignia, LLC +| Project Topics Library +\=============================================================================================================================*/ + +namespace System.Runtime.CompilerServices { + + /*============================================================================================================================ + | CLASS: IS EXTERNAL INIT + \---------------------------------------------------------------------------------------------------------------------------*/ + /// + /// The class is made available as part of the .NET 5.0 CLR in order to enable init accessors. + /// As this is not available in .NET Standard, however, we must maintain this separate copy until we migrate to .NET 5.0. + /// + internal static class IsExternalInit { + + } //Class +} //Namespace \ No newline at end of file From e75615fe139e2b64392d5784fe8df6a878729f98 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 12:13:23 -0800 Subject: [PATCH 61/97] Multi-target .NET Standard 2.1 and .NET 5 In order to take advantage of new .NET 5 features while continuing to support .NET Core 3.x, we're updating the OnTopic Data Transfer library to multi-target both .NET Standard 2.1 _and_ .NET 5. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index c3d6cdc..f62e867 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -2,7 +2,7 @@ OnTopic.Data.Transfer - netstandard2.1 + netstandard2.1;net5.0 9.0 enable latest From 682b1a8735a2108fb22e7b6738ecb6a71e2c06f2 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 12:22:46 -0800 Subject: [PATCH 62/97] Avoid setters on collection-valued properties Properties with a return type of a collection should not have a setter. That's because collections should be updated directly, using the collection semantics, instead of replaced entirely via the parent property. Unfortunately, we can't remove the setter entirely because it is relied upon for .NET deserialization. We can, however, help mitigate the risk by restricting the setting of the collection to `init`. This still trips the `CA2227` warning in code analysis. I'd consider that a bug. But, regardless, we'll need to maintain the suppression in the `csproj` file. --- OnTopic.Data.Transfer/RelationshipData.cs | 3 +-- OnTopic.Data.Transfer/TopicData.cs | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/OnTopic.Data.Transfer/RelationshipData.cs b/OnTopic.Data.Transfer/RelationshipData.cs index 8d012df..0abce45 100644 --- a/OnTopic.Data.Transfer/RelationshipData.cs +++ b/OnTopic.Data.Transfer/RelationshipData.cs @@ -3,7 +3,6 @@ | Client Ignia, LLC | Project Topics Library \=============================================================================================================================*/ -using System.Collections.Generic; using System.Collections.ObjectModel; namespace OnTopic.Data.Transfer { @@ -36,7 +35,7 @@ public class RelationshipData { /// /// Gets a collection of unique keys associated with related entities. /// - public Collection Relationships { get; set; } = new(); + public Collection Relationships { get; init; } = new(); } //Class } //Namespace \ No newline at end of file diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 4eef795..9afba65 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -88,7 +88,7 @@ public class TopicData { /// Provides a collection of representing the attributes from the associated /// object. /// - public RecordDataCollection Attributes { get; set; } = new(); + public RecordDataCollection Attributes { get; init; } = new(); /*========================================================================================================================== | RELATIONSHIPS @@ -97,7 +97,7 @@ public class TopicData { /// Provides a collection of representing the relationships from the associated object. /// - public RelationshipDataCollection Relationships { get; set; } = new(); + public RelationshipDataCollection Relationships { get; init; } = new(); /*========================================================================================================================== | TOPIC REFERENCES @@ -106,7 +106,7 @@ public class TopicData { /// Provides a collection of representing the topic references from the associated object. /// - public RecordDataCollection References { get; set; } = new(); + public RecordDataCollection References { get; init; } = new(); /*========================================================================================================================== | CHILDREN @@ -115,7 +115,7 @@ public class TopicData { /// Provides a collection of objects representing the children of the associated . /// - public Collection Children { get; set; } = new(); + public Collection Children { get; init; } = new(); } //Class } //Namespace \ No newline at end of file From 68a09cec0b5acb2092555192aee4fba7c958560a Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 12:47:31 -0800 Subject: [PATCH 63/97] Updated test to allow multi-targeting This allows conditions to be introduced for evaluating differences between the .NET Standard 2.1 version and the .NET 5 version. This is particularly relevant with regard to JSON serialization and deserialization, as .NET 5's `System.Text.Json` library has additional annotations which give us more granular control over how to handle these tasks. --- OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index f9e416a..2fde02a 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 9.0 false latest From 960f1bdaca2546286e3e9fc50e6dbd6c9ef2e219 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 12:54:16 -0800 Subject: [PATCH 64/97] Conditionally expose setter for deprecated `DerivedTopicKey` The `DerivedTopicKey` property is deprecated. Unfortunately, however, we still require it in order to deserialize JSON exported by legacy versions of the OnTopic Data Transfer library. Ideally, we'd hide the entire implementation from the public interface, and only support it for deserialization. Since that's not practical, we can instead at least prevent it from being set externally, or serialized if null. --- OnTopic.Data.Transfer/TopicData.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 9afba65..2978c54 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -7,6 +7,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Text.Json; +using System.Text.Json.Serialization; namespace OnTopic.Data.Transfer { @@ -79,7 +80,15 @@ public class TopicData { /// /// [Obsolete("The DerivedTopicKey has been renamed to BaseTopicKey.", false)] - public string? DerivedTopicKey { get; set; } + #if NET5_0 + #pragma warning disable IDE1006 // Naming Styles + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? DerivedTopicKey { get; internal set; } + #pragma warning restore IDE1006 // Naming Styles + #else + public string? DerivedTopicKey { get; set; } + #endif /*========================================================================================================================== | ATTRIBUTES From 156f9d08d0e23ee5702396f2eff25e1a4cb72482 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 13:54:17 -0800 Subject: [PATCH 65/97] Test conditional writing of `DerivedTopicKey` in .NET 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With .NET 5, we added a `[JsonIgnore()]` condition to the `DerivedTopicKey` so that it's not serialized to JSON if the value is `null` (960f1bd)—which it will always be after an `Export()` since the extension method no longer writes the `BaseTopic.Key` to `DerivedTopicKey`. This condition within the unit test evaluates that to ensure it works properly. --- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 644a3ad..8d3f957 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -43,7 +43,11 @@ public void Serialize_TopicData_ReturnsExpectedResults() { $"\"Children\":[]" + $"}}"; + #if NET5_0 + var json = JsonSerializer.Serialize(topicData); + #else var json = JsonSerializer.Serialize(topicData, new() { IgnoreNullValues = true }); + #endif Assert.AreEqual(expected, json); From f83ecbca98c98636d58f2286e80e336e38a75a77 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 14:59:20 -0800 Subject: [PATCH 66/97] Establish `RelationshipDataConverter` to refine (de)serialization The `RelationshipData` object historically contained a `Relationships` collection for values, which created an awkward `TopicData.Relationships[{Relationships:[]]` syntax. We'll be replacing this with `Values` in a subsequent update. To support that, while maintaining backward compatibility, we've introduced a `RelationshipDataConverter`. --- .../Converters/RelationshipDataConverter.cs | 107 ++++++++++++++++++ OnTopic.Data.Transfer/RelationshipData.cs | 5 + 2 files changed, 112 insertions(+) create mode 100644 OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs diff --git a/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs b/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs new file mode 100644 index 0000000..91ca59b --- /dev/null +++ b/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs @@ -0,0 +1,107 @@ +/*============================================================================================================================== +| Author Ignia, LLC +| Client Ignia, LLC +| Project Topics Library +\=============================================================================================================================*/ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using OnTopic.Internal.Diagnostics; + +namespace OnTopic.Data.Transfer.Converters { + + /*============================================================================================================================ + | CLASS: RELATIONSHIP DATA CONVERTER + \---------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Provides instructions for serializing or deserializing a instance. + /// + /// + /// The converter allows backward compatibility with legacy conventions which used Relationships instead of + /// Values. + /// + class RelationshipDataConverter: JsonConverter { + + /*========================================================================================================================== + | METHOD: READ + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Deserializes the data from JSON. + /// + public override RelationshipData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + + /*------------------------------------------------------------------------------------------------------------------------ + | Validate parameters + \-----------------------------------------------------------------------------------------------------------------------*/ + if (reader.TokenType != JsonTokenType.StartObject) { + throw new JsonException(); + } + + /*------------------------------------------------------------------------------------------------------------------------ + | Retrieve data + \-----------------------------------------------------------------------------------------------------------------------*/ + var key = (string?)null; + var values = (string[]?)null; + + while (reader.Read()) { + if (reader.TokenType == JsonTokenType.EndObject) { + break; + } + if (reader.TokenType != JsonTokenType.PropertyName) { + throw new JsonException(); + } + var propertyName = reader.GetString(); + if (propertyName is null) { + continue; + } + if (propertyName.Equals("Key", StringComparison.OrdinalIgnoreCase)) { + key = JsonSerializer.Deserialize(ref reader, options); + } + else if ( + propertyName.Equals("Relationships", StringComparison.OrdinalIgnoreCase) || + propertyName.Equals("Values", StringComparison.OrdinalIgnoreCase) + ) { + values = JsonSerializer.Deserialize(ref reader, options); + } + } + + /*------------------------------------------------------------------------------------------------------------------------ + | Validate data + \-----------------------------------------------------------------------------------------------------------------------*/ + Contract.Assume(key, nameof(key)); + + /*------------------------------------------------------------------------------------------------------------------------ + | Create data + \-----------------------------------------------------------------------------------------------------------------------*/ + var relationship = new RelationshipData() { + Key = key, + Relationships = new(values?? Array.Empty()) + }; + + /*------------------------------------------------------------------------------------------------------------------------ + | Return data + \-----------------------------------------------------------------------------------------------------------------------*/ + return relationship; + + } + + /*========================================================================================================================== + | METHOD: WRITE + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Serializes the data from JSON. + /// + public override void Write(Utf8JsonWriter writer, RelationshipData relationshipData, JsonSerializerOptions options) { + writer.WriteStartObject(); + writer.WritePropertyName("Key"); + writer.WriteStringValue(relationshipData.Key.ToString()); + writer.WriteStartArray("Relationships"); + foreach (var uniqueKey in relationshipData.Relationships) { + writer.WriteStringValue(uniqueKey); + } + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + } +} \ No newline at end of file diff --git a/OnTopic.Data.Transfer/RelationshipData.cs b/OnTopic.Data.Transfer/RelationshipData.cs index 0abce45..d27d9f8 100644 --- a/OnTopic.Data.Transfer/RelationshipData.cs +++ b/OnTopic.Data.Transfer/RelationshipData.cs @@ -4,6 +4,9 @@ | Project Topics Library \=============================================================================================================================*/ using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using OnTopic.Data.Transfer.Converters; namespace OnTopic.Data.Transfer { @@ -19,6 +22,7 @@ namespace OnTopic.Data.Transfer { /// from objects, but in turn greatly simplifies how the serialization process works, and provides /// necessary flexibility in the import process to better account for merging data and handling potential conflicts. /// + [JsonConverter(typeof(RelationshipDataConverter))] public class RelationshipData { /*========================================================================================================================== @@ -27,6 +31,7 @@ public class RelationshipData { /// /// Gets or sets the key of the relationship. /// + [NotNull, DisallowNull] public string? Key { get; set; } /*========================================================================================================================== From b013eca068e13a2b2ed8079d0441de771228a4df Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 15:10:17 -0800 Subject: [PATCH 67/97] Renamed `Relationships` property to `Values` The idea of each item in the `TopicData.Relationships` collection having its own `Relationships` property is confusing. A more consistent identifier with the OnTopic Library would be `Values`, thus exposing `TopicData.Relationships[{Values:[]}]`. This would normally break backward compatibility, except the previous introduction of the `RelationshipDataConverter` handles this scenario for us by operating off of _either_ `Values` _or_ `Relationships`, thus maintaining support for JSON files written previously with the `Relationships` nomenclature. --- .../DeserializationTest.cs | 16 ++++++++-------- OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs | 2 +- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 8 ++++---- .../TopicExtensionsTest.cs | 8 ++++---- .../Converters/RelationshipDataConverter.cs | 6 +++--- .../Interchange/TopicExtensions.cs | 6 +++--- OnTopic.Data.Transfer/RelationshipData.cs | 4 ++-- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 457ff4b..cdf9490 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -107,18 +107,18 @@ public void Deserialize_RelationshipData_ReturnsExpectedResults() { var sourceData = new RelationshipData() { Key = "Test" }; - sourceData.Relationships.Add("Root:Web"); + sourceData.Values.Add("Root:Web"); var json = $"{{" + $"\"Key\":\"{sourceData.Key}\"," + - $"\"Relationships\":[\"Root:Web\"]" + + $"\"Values\":[\"Root:Web\"]" + $"}}"; var relationshipData = JsonSerializer.Deserialize(json); Assert.AreEqual(sourceData.Key, relationshipData.Key); - Assert.AreEqual(sourceData.Relationships.Count, relationshipData.Relationships.Count); - Assert.AreEqual(sourceData.Relationships.FirstOrDefault(), relationshipData.Relationships.FirstOrDefault()); + Assert.AreEqual(sourceData.Values.Count, relationshipData.Values.Count); + Assert.AreEqual(sourceData.Values.FirstOrDefault(), relationshipData.Values.FirstOrDefault()); } @@ -183,7 +183,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { ContentType = "Container" }; - sourceRelationshipData.Relationships.Add("Root:Web"); + sourceRelationshipData.Values.Add("Root:Web"); sourceTopicData.Relationships.Add(sourceRelationshipData); sourceTopicData.Attributes.Add(sourceAttributeData); sourceTopicData.Children.Add(sourceChildTopicData); @@ -202,7 +202,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"\"Relationships\":[" + $"{{" + $"\"Key\":\"{sourceRelationshipData.Key}\"," + - $"\"Relationships\":[\"Root:Web\"]" + + $"\"Values\":[\"Root:Web\"]" + $"}}" + $"]," + $"\"References\":[" + @@ -239,8 +239,8 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { Assert.AreEqual(1, sourceTopicData.Children.Count); Assert.AreEqual(sourceRelationshipData.Key, relationshipData.Key); - Assert.AreEqual(sourceRelationshipData.Relationships.Count, relationshipData.Relationships.Count); - Assert.AreEqual(sourceRelationshipData.Relationships.FirstOrDefault(), relationshipData.Relationships.FirstOrDefault()); + Assert.AreEqual(sourceRelationshipData.Values.Count, relationshipData.Values.Count); + Assert.AreEqual(sourceRelationshipData.Values.FirstOrDefault(), relationshipData.Values.FirstOrDefault()); Assert.AreEqual(sourceReferenceData.Key, referenceData.Key); Assert.AreEqual(sourceReferenceData.Value, referenceData.Value); diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index a56d456..dd5d33e 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -241,7 +241,7 @@ public void ImportAsReplace_TopicDataWithRelationships_DeletesOrphanedRelationsh topic.Relationships.SetValue("Cousin", relatedTopic3); topicData.Relationships.Add(relationshipData); - relationshipData.Relationships.Add(relatedTopic1.GetUniqueKey()); + relationshipData.Values.Add(relatedTopic1.GetUniqueKey()); topic.Import( topicData, diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 8d3f957..567aa8b 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -65,11 +65,11 @@ public void Serialize_RelationshipData_ReturnsExpectedResults() { var relationshipData = new RelationshipData() { Key = "Test" }; - relationshipData.Relationships.Add("Root:Web"); + relationshipData.Values.Add("Root:Web"); var expected = $"{{" + $"\"Key\":\"{relationshipData.Key}\"," + - $"\"Relationships\":[\"Root:Web\"]" + + $"\"Values\":[\"Root:Web\"]" + $"}}"; var json = JsonSerializer.Serialize(relationshipData); @@ -135,7 +135,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { ContentType = "Container" }; - relationshipData.Relationships.Add("Root:Web"); + relationshipData.Values.Add("Root:Web"); topicData.Relationships.Add(relationshipData); topicData.References.Add(referenceData); topicData.Attributes.Add(attributeData); @@ -154,7 +154,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Relationships\":[" + $"{{" + $"\"Key\":\"{relationshipData.Key}\"," + - $"\"Relationships\":[\"Root:Web\"]" + + $"\"Values\":[\"Root:Web\"]" + $"}}" + $"]," + $"\"References\":[" + diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 027c9bc..5c98f43 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -108,7 +108,7 @@ public void Export_TopicWithRelationships_MapsRelationshipDataCollection() { Assert.IsNotNull(topicData); Assert.IsNotNull(childTopicData); Assert.AreEqual(1, childTopicData.Relationships.Count); - Assert.AreEqual("Root:Related", childTopicData.Relationships.FirstOrDefault().Relationships.FirstOrDefault()); + Assert.AreEqual("Root:Related", childTopicData.Relationships.FirstOrDefault().Values.FirstOrDefault()); } @@ -138,7 +138,7 @@ public void ExportWithExternalAssociations_TopicWithRelationships_ExcludesExtern Assert.IsNotNull(topicData); Assert.AreEqual(1, topicData.Relationships.Count); - Assert.AreEqual("Root:Related", topicData.Relationships.FirstOrDefault().Relationships.FirstOrDefault()); + Assert.AreEqual("Root:Related", topicData.Relationships.FirstOrDefault().Values.FirstOrDefault()); } @@ -611,7 +611,7 @@ public void Import_TopicDataWithRelationships_MapsRelationshipCollection() { }; topicData.Relationships.Add(relationshipData); - relationshipData.Relationships.Add(relatedTopic.GetUniqueKey()); + relationshipData.Values.Add(relatedTopic.GetUniqueKey()); topic.Import(topicData); @@ -645,7 +645,7 @@ public void Import_TopicDataWithRelationships_MaintainsExisting() { topic.Relationships.SetValue("Related", relatedTopic1); topicData.Relationships.Add(relationshipData); - relationshipData.Relationships.Add(relatedTopic2.GetUniqueKey()); + relationshipData.Values.Add(relatedTopic2.GetUniqueKey()); topic.Import(topicData); diff --git a/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs b/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs index 91ca59b..48c81cb 100644 --- a/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs +++ b/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs @@ -75,7 +75,7 @@ public override RelationshipData Read(ref Utf8JsonReader reader, Type typeToConv \-----------------------------------------------------------------------------------------------------------------------*/ var relationship = new RelationshipData() { Key = key, - Relationships = new(values?? Array.Empty()) + Values = new(values?? Array.Empty()) }; /*------------------------------------------------------------------------------------------------------------------------ @@ -95,8 +95,8 @@ public override void Write(Utf8JsonWriter writer, RelationshipData relationshipD writer.WriteStartObject(); writer.WritePropertyName("Key"); writer.WriteStringValue(relationshipData.Key.ToString()); - writer.WriteStartArray("Relationships"); - foreach (var uniqueKey in relationshipData.Relationships) { + writer.WriteStartArray("Values"); + foreach (var uniqueKey in relationshipData.Values) { writer.WriteStringValue(uniqueKey); } writer.WriteEndArray(); diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 8cef5dc..204d45c 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -130,10 +130,10 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options options.IncludeExternalAssociations || relatedTopic.GetUniqueKey().StartsWith(options.ExportScope, StringComparison.InvariantCultureIgnoreCase) ) { - relationshipData.Relationships.Add(relatedTopic.GetUniqueKey()); + relationshipData.Values.Add(relatedTopic.GetUniqueKey()); } } - if (relationshipData.Relationships.Count > 0) { + if (relationshipData.Values.Count > 0) { topicData.Relationships.Add(relationshipData); } } @@ -384,7 +384,7 @@ attribute.Value is not null && //Update records based on the source collection foreach (var relationship in topicData.Relationships) { - foreach (var relatedTopicKey in relationship.Relationships) { + foreach (var relatedTopicKey in relationship.Values) { var relatedTopic = topic.GetByUniqueKey(relatedTopicKey); if (relationship.Key is not null && relatedTopic is not null) { topic.Relationships.SetValue(relationship.Key, relatedTopic); diff --git a/OnTopic.Data.Transfer/RelationshipData.cs b/OnTopic.Data.Transfer/RelationshipData.cs index d27d9f8..5f4d5a6 100644 --- a/OnTopic.Data.Transfer/RelationshipData.cs +++ b/OnTopic.Data.Transfer/RelationshipData.cs @@ -35,12 +35,12 @@ public class RelationshipData { public string? Key { get; set; } /*========================================================================================================================== - | PROPERTY: RELATIONSHIPS + | PROPERTY: VALUES \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Gets a collection of unique keys associated with related entities. /// - public Collection Relationships { get; init; } = new(); + public Collection Values { get; init; } = new(); } //Class } //Namespace \ No newline at end of file From cc8412c18cc0790caa5b08bed987daf6164e8944 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 15:41:53 -0800 Subject: [PATCH 68/97] Renamed `RelationshipDataCollection` to `MultiMap` There's nothing about the `RelationshipDataCollection` that _should_ be specific to `TopicData.Relationships`. In fact, it _should_ map to the `TopicMultiMap`, which provides a generic "dictionary of dictionaries" for topics. By renaming `RelationshipDataCollection` to `MultiMap`, we better represent the mapping between those classes, while also providing more flexibility in the future to handle other collections that might use `TopicMultiMap`. As part of this, the underlying `RelationshipData` class is renamed to `KeyValuesPair`, to map to the `KeyValuesPair` in OnTopic, and the `RelationshipDataConverter` is renamed to `KeyValuesPairConverter` to maintain parity with that change. --- .../DeserializationTest.cs | 8 +++--- .../ImportOptionsTest.cs | 2 +- .../SerializationTest.cs | 6 ++--- .../TopicExtensionsTest.cs | 4 +-- ...Converter.cs => KeyValuesPairConverter.cs} | 12 ++++----- .../Interchange/TopicExtensions.cs | 2 +- .../Interchange/UnresolvedAssociation.cs | 2 +- .../{RelationshipData.cs => KeyValuesPair.cs} | 25 +++++++++++-------- ...ationshipDataCollection.cs => MultiMap.cs} | 23 +++++++++++------ OnTopic.Data.Transfer/TopicData.cs | 4 +-- 10 files changed, 50 insertions(+), 38 deletions(-) rename OnTopic.Data.Transfer/Converters/{RelationshipDataConverter.cs => KeyValuesPairConverter.cs} (91%) rename OnTopic.Data.Transfer/{RelationshipData.cs => KeyValuesPair.cs} (59%) rename OnTopic.Data.Transfer/{RelationshipDataCollection.cs => MultiMap.cs} (54%) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index cdf9490..66675a5 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -99,12 +99,12 @@ public void Deserialize_DerivedTopicKey_ReturnsExpectedResults() { | TEST: DESERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a json string and attempts to deserialize it as a class. + /// Creates a json string and attempts to deserialize it as a class. /// [TestMethod] public void Deserialize_RelationshipData_ReturnsExpectedResults() { - var sourceData = new RelationshipData() { + var sourceData = new KeyValuesPair() { Key = "Test" }; sourceData.Values.Add("Root:Web"); @@ -114,7 +114,7 @@ public void Deserialize_RelationshipData_ReturnsExpectedResults() { $"\"Values\":[\"Root:Web\"]" + $"}}"; - var relationshipData = JsonSerializer.Deserialize(json); + var relationshipData = JsonSerializer.Deserialize(json); Assert.AreEqual(sourceData.Key, relationshipData.Key); Assert.AreEqual(sourceData.Values.Count, relationshipData.Values.Count); @@ -165,7 +165,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { UniqueKey = "Root:Test", ContentType = "Container" }; - var sourceRelationshipData= new RelationshipData() { + var sourceRelationshipData= new KeyValuesPair() { Key = "Test" }; var sourceAttributeData = new RecordData() { diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index dd5d33e..779eded 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -232,7 +232,7 @@ public void ImportAsReplace_TopicDataWithRelationships_DeletesOrphanedRelationsh UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType }; - var relationshipData = new RelationshipData() { + var relationshipData = new KeyValuesPair() { Key = "Related" }; diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 567aa8b..e39a14a 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -57,12 +57,12 @@ public void Serialize_TopicData_ReturnsExpectedResults() { | TEST: SERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a , serializes it, and confirms the resulting JSON. + /// Creates a , serializes it, and confirms the resulting JSON. /// [TestMethod] public void Serialize_RelationshipData_ReturnsExpectedResults() { - var relationshipData = new RelationshipData() { + var relationshipData = new KeyValuesPair() { Key = "Test" }; relationshipData.Values.Add("Root:Web"); @@ -118,7 +118,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { UniqueKey = "Root:Test", ContentType = "Container" }; - var relationshipData = new RelationshipData() { + var relationshipData = new KeyValuesPair() { Key = "Test" }; var referenceData = new RecordData() { diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 5c98f43..1c0d6e3 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -606,7 +606,7 @@ public void Import_TopicDataWithRelationships_MapsRelationshipCollection() { UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType }; - var relationshipData = new RelationshipData() { + var relationshipData = new KeyValuesPair() { Key = "Related" }; @@ -638,7 +638,7 @@ public void Import_TopicDataWithRelationships_MaintainsExisting() { UniqueKey = topic.GetUniqueKey(), ContentType = topic.ContentType }; - var relationshipData = new RelationshipData() { + var relationshipData = new KeyValuesPair() { Key = "Related" }; diff --git a/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs b/OnTopic.Data.Transfer/Converters/KeyValuesPairConverter.cs similarity index 91% rename from OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs rename to OnTopic.Data.Transfer/Converters/KeyValuesPairConverter.cs index 48c81cb..12a3228 100644 --- a/OnTopic.Data.Transfer/Converters/RelationshipDataConverter.cs +++ b/OnTopic.Data.Transfer/Converters/KeyValuesPairConverter.cs @@ -11,16 +11,16 @@ namespace OnTopic.Data.Transfer.Converters { /*============================================================================================================================ - | CLASS: RELATIONSHIP DATA CONVERTER + | CLASS: KEY/VALUES PAIR CONVERTER \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// Provides instructions for serializing or deserializing a instance. + /// Provides instructions for serializing or deserializing a instance. /// /// /// The converter allows backward compatibility with legacy conventions which used Relationships instead of /// Values. /// - class RelationshipDataConverter: JsonConverter { + class KeyValuesPairConverter: JsonConverter { /*========================================================================================================================== | METHOD: READ @@ -28,7 +28,7 @@ class RelationshipDataConverter: JsonConverter { /// /// Deserializes the data from JSON. /// - public override RelationshipData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + public override KeyValuesPair Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { /*------------------------------------------------------------------------------------------------------------------------ | Validate parameters @@ -73,7 +73,7 @@ public override RelationshipData Read(ref Utf8JsonReader reader, Type typeToConv /*------------------------------------------------------------------------------------------------------------------------ | Create data \-----------------------------------------------------------------------------------------------------------------------*/ - var relationship = new RelationshipData() { + var relationship = new KeyValuesPair() { Key = key, Values = new(values?? Array.Empty()) }; @@ -91,7 +91,7 @@ public override RelationshipData Read(ref Utf8JsonReader reader, Type typeToConv /// /// Serializes the data from JSON. /// - public override void Write(Utf8JsonWriter writer, RelationshipData relationshipData, JsonSerializerOptions options) { + public override void Write(Utf8JsonWriter writer, KeyValuesPair relationshipData, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WritePropertyName("Key"); writer.WriteStringValue(relationshipData.Key.ToString()); diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index 204d45c..f426604 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -122,7 +122,7 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options | Set relationships \-----------------------------------------------------------------------------------------------------------------------*/ foreach (var relationship in topic.Relationships) { - var relationshipData = new RelationshipData() { + var relationshipData = new KeyValuesPair() { Key = relationship.Key, }; foreach (var relatedTopic in relationship.Values) { diff --git a/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs b/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs index bb4b82c..e89c150 100644 --- a/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs +++ b/OnTopic.Data.Transfer/Interchange/UnresolvedAssociation.cs @@ -74,7 +74,7 @@ internal UnresolvedAssociation(AssociationType type, string key, Topic source, s /// /// /// This maps to either the , if the association is from , - /// or , if the association is from . + /// or , if the association is from . /// internal string Key { get; init; } diff --git a/OnTopic.Data.Transfer/RelationshipData.cs b/OnTopic.Data.Transfer/KeyValuesPair.cs similarity index 59% rename from OnTopic.Data.Transfer/RelationshipData.cs rename to OnTopic.Data.Transfer/KeyValuesPair.cs index 5f4d5a6..115e17e 100644 --- a/OnTopic.Data.Transfer/RelationshipData.cs +++ b/OnTopic.Data.Transfer/KeyValuesPair.cs @@ -6,30 +6,31 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using OnTopic.Collections.Specialized; using OnTopic.Data.Transfer.Converters; namespace OnTopic.Data.Transfer { /*============================================================================================================================ - | CLASS: RELATIONSHIP DATA + | CLASS: KEY/VALUES PAIR \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides an intermediary data transfer object for facilitating the interchange - /// of objects with JSON data. + /// The class provides an intermediary data transfer object for facilitating the interchange + /// of objects with JSON data. /// /// - /// Having a separate class for this serializing topic data introduces some overhead in converting the topic graph to and - /// from objects, but in turn greatly simplifies how the serialization process works, and provides - /// necessary flexibility in the import process to better account for merging data and handling potential conflicts. + /// Unlike the class which is used by the , the data transfer object maps to a collection of strings representing references, thus providing a serializable format for e.g. . /// - [JsonConverter(typeof(RelationshipDataConverter))] - public class RelationshipData { + [JsonConverter(typeof(KeyValuesPairConverter))] + public class KeyValuesPair { /*========================================================================================================================== | KEY \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Gets or sets the key of the relationship. + /// Gets or sets the of the . /// [NotNull, DisallowNull] public string? Key { get; set; } @@ -38,8 +39,12 @@ public class RelationshipData { | PROPERTY: VALUES \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Gets a collection of unique keys associated with related entities. + /// Gets or sets the of the . /// + /// + /// While the collection can contain any value, it is intended to represent references. + /// public Collection Values { get; init; } = new(); } //Class diff --git a/OnTopic.Data.Transfer/RelationshipDataCollection.cs b/OnTopic.Data.Transfer/MultiMap.cs similarity index 54% rename from OnTopic.Data.Transfer/RelationshipDataCollection.cs rename to OnTopic.Data.Transfer/MultiMap.cs index 074b7de..ee53576 100644 --- a/OnTopic.Data.Transfer/RelationshipDataCollection.cs +++ b/OnTopic.Data.Transfer/MultiMap.cs @@ -4,29 +4,36 @@ | Project Topics Library \=============================================================================================================================*/ using System.Collections.ObjectModel; +using OnTopic.Collections.Specialized; using OnTopic.Internal.Diagnostics; namespace OnTopic.Data.Transfer { /*============================================================================================================================ - | CLASS: RELATIONSHIP DATA COLLECTION + | CLASS: MULTIMAP \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides a of objects. + /// The class provides a of objects, thus + /// supporting a many-to-many mapping. /// - public class RelationshipDataCollection: KeyedCollection { + /// + /// The is intended to model the —though instead of a exposing a + /// collection of references, it instead exposes a list of s intended to map to s, thus providing a serializable format for handling e.g. . + /// + public class MultiMap: KeyedCollection { /*========================================================================================================================== | OVERRIDE: GET KEY FOR ITEM \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Method must be overridden for the to identify the appropriate from each object. + /// Method must be overridden for the to identify the appropriate from each object. /// - /// The object from which to extract the key. + /// The object from which to extract the key. /// The key for the specified collection item. - protected override string GetKeyForItem(RelationshipData item) { + protected override string GetKeyForItem(KeyValuesPair item) { Contract.Requires(item, "The item must be available in order to derive its key."); return item.Key?? ""; } diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index 2978c54..fe1e699 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -103,10 +103,10 @@ public class TopicData { | RELATIONSHIPS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Provides a collection of representing the relationships from the associated representing the relationships from the associated object. /// - public RelationshipDataCollection Relationships { get; init; } = new(); + public MultiMap Relationships { get; init; } = new(); /*========================================================================================================================== | TOPIC REFERENCES From b0670d8d769309f99c116a401a1a1ef45c018e34 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 15:48:28 -0800 Subject: [PATCH 69/97] Renamed `RelationshipData` specific tests to use `KeyValuesPair` With the rename of the `RelationshipDataCollection` to `MultiMap` and `RelationshipData` to `KeyValuesPair`, tests (and associated variables) that evaluated the `KeyValuesPair` in isolation should no longer be named with `RelationshipData`, but instead refer to `KeyValuesPair`. This change was not made to e.g. variables within unit tests that work with `KeyValuesPair` as a target for `TopicData.Relationships` since, contextually, they remain relationship specific. --- OnTopic.Data.Transfer.Tests/DeserializationTest.cs | 14 +++++++------- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 12 ++++++------ OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 66675a5..39eff3d 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -96,13 +96,13 @@ public void Deserialize_DerivedTopicKey_ReturnsExpectedResults() { #pragma warning restore CS0618 // Type or member is obsolete /*========================================================================================================================== - | TEST: DESERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS + | TEST: DESERIALIZE: KEY/VALUES PAIR: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a json string and attempts to deserialize it as a class. + /// Creates a JSON string and attempts to deserialize it as a class. /// [TestMethod] - public void Deserialize_RelationshipData_ReturnsExpectedResults() { + public void Deserialize_KeyValuesPair_ReturnsExpectedResults() { var sourceData = new KeyValuesPair() { Key = "Test" @@ -114,11 +114,11 @@ public void Deserialize_RelationshipData_ReturnsExpectedResults() { $"\"Values\":[\"Root:Web\"]" + $"}}"; - var relationshipData = JsonSerializer.Deserialize(json); + var keyValuesPair = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, relationshipData.Key); - Assert.AreEqual(sourceData.Values.Count, relationshipData.Values.Count); - Assert.AreEqual(sourceData.Values.FirstOrDefault(), relationshipData.Values.FirstOrDefault()); + Assert.AreEqual(sourceData.Key, keyValuesPair.Key); + Assert.AreEqual(sourceData.Values.Count, keyValuesPair.Values.Count); + Assert.AreEqual(sourceData.Values.FirstOrDefault(), keyValuesPair.Values.FirstOrDefault()); } diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index e39a14a..aef0618 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -54,25 +54,25 @@ public void Serialize_TopicData_ReturnsExpectedResults() { } /*========================================================================================================================== - | TEST: SERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS + | TEST: SERIALIZE: KEY/VALUES PAIR: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Creates a , serializes it, and confirms the resulting JSON. /// [TestMethod] - public void Serialize_RelationshipData_ReturnsExpectedResults() { + public void Serialize_KeyValuesPair_ReturnsExpectedResults() { - var relationshipData = new KeyValuesPair() { + var keyValuesPair = new KeyValuesPair() { Key = "Test" }; - relationshipData.Values.Add("Root:Web"); + keyValuesPair.Values.Add("Root:Web"); var expected = $"{{" + - $"\"Key\":\"{relationshipData.Key}\"," + + $"\"Key\":\"{keyValuesPair.Key}\"," + $"\"Values\":[\"Root:Web\"]" + $"}}"; - var json = JsonSerializer.Serialize(relationshipData); + var json = JsonSerializer.Serialize(keyValuesPair); Assert.AreEqual(expected, json); diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 1c0d6e3..daa0c23 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -607,7 +607,7 @@ public void Import_TopicDataWithRelationships_MapsRelationshipCollection() { ContentType = topic.ContentType }; var relationshipData = new KeyValuesPair() { - Key = "Related" + Key = "Related" }; topicData.Relationships.Add(relationshipData); @@ -639,7 +639,7 @@ public void Import_TopicDataWithRelationships_MaintainsExisting() { ContentType = topic.ContentType }; var relationshipData = new KeyValuesPair() { - Key = "Related" + Key = "Related" }; topic.Relationships.SetValue("Related", relatedTopic1); From a40dc641f13c6c9703be831d84dd9b61b59ca790 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 15:53:36 -0800 Subject: [PATCH 70/97] Introduced unit test for verifying backward compatibility The `KeyValuesPairConverter` is intended to assure backward compatibility with the legacy `RelationshipData` object which wrote associated unique keys into a `Relationships` array instead of a `Values` array. On deserialization, JSON with the `Relationships` array should be successfully read into the `Values` collection. This unit test verifies that is the case. --- .../DeserializationTest.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 39eff3d..4b3afc5 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text.Json; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OnTopic.Data.Transfer.Converters; namespace OnTopic.Data.Transfer.Tests { @@ -122,6 +123,36 @@ public void Deserialize_KeyValuesPair_ReturnsExpectedResults() { } + /*========================================================================================================================== + | TEST: DESERIALIZE: RELATIONSHIP DATA: RETURNS EXPECTED RESULTS + \-------------------------------------------------------------------------------------------------------------------------*/ + /// + /// Creates a JSON string representing the legacy RelationshipData class (which used a Relationships array), + /// and attempts to deserialize it as a class, ensuring that the properly translates the Relationships array to the + /// collection. + /// + [TestMethod] + public void Deserialize_RelationshipData_ReturnsExpectedResults() { + + var sourceData = new KeyValuesPair() { + Key = "Test" + }; + sourceData.Values.Add("Root:Web"); + + var json = $"{{" + + $"\"Key\":\"{sourceData.Key}\"," + + $"\"Relationships\":[\"Root:Web\"]" + + $"}}"; + + var keyValuesPair = JsonSerializer.Deserialize(json); + + Assert.AreEqual(sourceData.Key, keyValuesPair.Key); + Assert.AreEqual(sourceData.Values.Count, keyValuesPair.Values.Count); + Assert.AreEqual(sourceData.Values.FirstOrDefault(), keyValuesPair.Values.FirstOrDefault()); + + } + /*========================================================================================================================== | TEST: DESERIALIZE: RECORD DATA: RETURNS EXPECTED RESULTS \-------------------------------------------------------------------------------------------------------------------------*/ From eb21063270c5026d75eea1e6042724419de61a11 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 16 Feb 2021 16:16:29 -0800 Subject: [PATCH 71/97] Hard-coded date time values to avoid false negatives in unit tests The JSON serializer/deserializer will truncate trailing `0`s, whereas .NET's string conversion will not. This leads to false negatives in the unit tests. This doesn't affect actual serialization or deserialization, since the ticks are fully represented once converted back to a `DateTime` instance, but it interferes with the unit tests where the `expected` JSON is being manually constructed for exact comparison. To mitigate this, a `lastModified` date is hard-coded into the unit tests, and then formatted as a short string to ensure that it doesn't include the milliseconds. This avoids scenarios where there are discrepancies in the data. --- .../DeserializationTest.cs | 14 ++++++++------ OnTopic.Data.Transfer.Tests/SerializationTest.cs | 16 +++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index 4b3afc5..ef38be7 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -164,13 +164,13 @@ public void Deserialize_RecordData_ReturnsExpectedResults() { var sourceData = new RecordData() { Key = "Test", - LastModified = DateTime.Now + LastModified = new DateTime(2021, 02, 16, 16, 06, 25) }; var json = $"{{" + $"\"Key\":\"{sourceData.Key}\"," + $"\"Value\":null," + - $"\"LastModified\":\"{sourceData.LastModified:o}\"" + + $"\"LastModified\":\"{sourceData.LastModified:s}\"" + $"}}"; @@ -191,6 +191,8 @@ public void Deserialize_RecordData_ReturnsExpectedResults() { [TestMethod] public void Deserialize_TopicGraph_ReturnsExpectedResults() { + var lastModified = new DateTime(2021, 02, 16, 16, 06, 25); + var sourceTopicData = new TopicData() { Key = "Test", UniqueKey = "Root:Test", @@ -201,12 +203,12 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { }; var sourceAttributeData = new RecordData() { Key = "Test", - LastModified = DateTime.Now + LastModified = lastModified }; var sourceReferenceData = new RecordData() { Key = "Test", Value = "Root:Reference", - LastModified = DateTime.Now + LastModified = lastModified }; var sourceChildTopicData = new TopicData() { Key = "Child", @@ -227,7 +229,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"{{" + $"\"Key\":\"{sourceAttributeData.Key}\"," + $"\"Value\":null," + - $"\"LastModified\":\"{sourceAttributeData.LastModified:o}\"" + + $"\"LastModified\":\"{sourceAttributeData.LastModified:s}\"" + $"}}"+ $"]," + $"\"Relationships\":[" + @@ -240,7 +242,7 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { $"{{" + $"\"Key\":\"{sourceReferenceData.Key}\"," + $"\"Value\":\"{sourceReferenceData.Value}\"," + - $"\"LastModified\":\"{sourceReferenceData.LastModified:o}\"" + + $"\"LastModified\":\"{sourceReferenceData.LastModified:s}\"" + $"}}"+ $"]," + $"\"Children\":[" + diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index aef0618..2e12b16 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -87,15 +87,15 @@ public void Serialize_KeyValuesPair_ReturnsExpectedResults() { [TestMethod] public void Serialize_RecordData_ReturnsExpectedResults() { - var recordData = new RecordData() { + var recordData = new RecordData() { Key = "Test", - LastModified = DateTime.Now + LastModified = new DateTime(2021, 02, 16, 16, 06, 25) }; var expected = $"{{" + $"\"Key\":\"{recordData.Key}\"," + $"\"Value\":null," + - $"\"LastModified\":\"{recordData.LastModified:o}\"" + + $"\"LastModified\":\"{recordData.LastModified:s}\"" + $"}}"; var json = JsonSerializer.Serialize(recordData); @@ -113,6 +113,8 @@ public void Serialize_RecordData_ReturnsExpectedResults() { [TestMethod] public void Serialize_TopicGraph_ReturnsExpectedResults() { + var lastModified = new DateTime(2021, 02, 16, 16, 06, 25); + var topicData = new TopicData() { Key = "Test", UniqueKey = "Root:Test", @@ -123,11 +125,11 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { }; var referenceData = new RecordData() { Key = "Test", - LastModified = DateTime.Now + LastModified = lastModified }; var attributeData = new RecordData() { Key = "Test", - LastModified = DateTime.Now + LastModified = lastModified }; var childTopicData = new TopicData() { Key = "Child", @@ -148,7 +150,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"Attributes\":[" + $"{{" + $"\"Key\":\"{attributeData.Key}\"," + - $"\"LastModified\":\"{attributeData.LastModified:o}\"" + + $"\"LastModified\":\"{attributeData.LastModified:s}\"" + $"}}"+ $"]," + $"\"Relationships\":[" + @@ -160,7 +162,7 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() { $"\"References\":[" + $"{{" + $"\"Key\":\"{referenceData.Key}\"," + - $"\"LastModified\":\"{referenceData.LastModified:o}\"" + + $"\"LastModified\":\"{referenceData.LastModified:s}\"" + $"}}"+ $"]," + $"\"Children\":[" + From 2d28cb0f3ed9627bbc25d0e5bb4ad4a45b11e77f Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Wed, 17 Feb 2021 17:20:19 -0800 Subject: [PATCH 72/97] Upgraded to the latest prerelease of OnTopic 5.0.0 --- OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj | 2 +- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index 2fde02a..0ae4c2f 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -20,7 +20,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index f62e867..94b16eb 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 439b41c64b80bc78224bec726691542bdddd7e50 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Wed, 17 Feb 2021 17:23:03 -0800 Subject: [PATCH 73/97] Updated to `System.Json.Text` 5.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides access to .NET 5 attributes, such as `[JsonInclude]` and `[JsonIgore()]` which we were previously relying upon .NET 5 for—but without the need to multi-target .NET 5. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 94b16eb..14dc8bd 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -36,7 +36,7 @@ - + \ No newline at end of file From cffc2d967797e8ca63cf8f09f90c360e25bb449b Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Wed, 17 Feb 2021 17:26:40 -0800 Subject: [PATCH 74/97] Remove multi-targeting of .NET 5 In a previous release, we updated the OnTopic Data Transfer library to multi-target .NET 5 (2f8ca6b). That was premature. The primary driver of this was the ability to use new .NET 5 attributes for (de)serialization. But those can be accessed by simply upgrading the `System.Text.Json` package to 5.0.0 (439b41c); that doesn't actually necessitate the use of .NET 5 itself. Given that, we can undo the multi-targeting previously introduced. --- .../OnTopic.Data.Transfer.Tests.csproj | 2 +- OnTopic.Data.Transfer.Tests/SerializationTest.cs | 4 ---- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- OnTopic.Data.Transfer/TopicData.cs | 12 +++--------- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index 0ae4c2f..19514e6 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net5.0 + netcoreapp3.1 9.0 false latest diff --git a/OnTopic.Data.Transfer.Tests/SerializationTest.cs b/OnTopic.Data.Transfer.Tests/SerializationTest.cs index 2e12b16..df2ced4 100644 --- a/OnTopic.Data.Transfer.Tests/SerializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/SerializationTest.cs @@ -43,11 +43,7 @@ public void Serialize_TopicData_ReturnsExpectedResults() { $"\"Children\":[]" + $"}}"; - #if NET5_0 var json = JsonSerializer.Serialize(topicData); - #else - var json = JsonSerializer.Serialize(topicData, new() { IgnoreNullValues = true }); - #endif Assert.AreEqual(expected, json); diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 14dc8bd..4b119fe 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -2,7 +2,7 @@ OnTopic.Data.Transfer - netstandard2.1;net5.0 + netstandard2.1 9.0 enable latest diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index fe1e699..e231889 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -80,15 +80,9 @@ public class TopicData { /// /// [Obsolete("The DerivedTopicKey has been renamed to BaseTopicKey.", false)] - #if NET5_0 - #pragma warning disable IDE1006 // Naming Styles - [JsonInclude] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? DerivedTopicKey { get; internal set; } - #pragma warning restore IDE1006 // Naming Styles - #else - public string? DerivedTopicKey { get; set; } - #endif + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? DerivedTopicKey { get; internal set; } /*========================================================================================================================== | ATTRIBUTES From 115ab4c10479f2a04e5ff760539a8b54e59de350 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Wed, 17 Feb 2021 17:27:35 -0800 Subject: [PATCH 75/97] Updated documentation based on previous `MultiMap` migration In a previous commit, I introduced a new `MultiMap` type as the basis for `TopicData.Relationships` (bc15631). But I failed to update the documentation as part of that. Fixed. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a82978..97a5dec 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ The `OnTopic.Data.Transfer` assembly includes three basic data transfer classes - `Children` (`Collection`) - `Attributes` (`KeyedCollection`) - `RecordData`: Maps to the `TrackedRecord` class, and represents an individual attribute. - - `Relationships` ([`RelationshipDataCollection`](OnTopic.Data.Transfer/RelationshipDataCollection.cs)) - - `RelationshipData`: Maps to the `NamedTopicCollection`, and represents a relationship key, as well as a list of references to related topics via their `Topic.GetUniqueKey()` value. + - `Relationships` ([`MultiMap`](OnTopic.Data.Transfer/MultiMap.cs)) + - `KeyValuesPair`: Maps to the `KeyValuesPair`, and represents a relationship key, as well as a list of associations to related topics via their `Topic.GetUniqueKey()` value. - `References` (`KeyedCollection`) - `RecordData`: Maps to the `TrackedRecord` class, and represents an individual topic reference. From d1b8f6b0fdad3256921001adc457ae9168f7519e Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Wed, 17 Feb 2021 17:35:54 -0800 Subject: [PATCH 76/97] Configure `release` branches to auto-increment --- GitVersion.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GitVersion.yml b/GitVersion.yml index b84f148..952eda0 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -3,5 +3,7 @@ mode: ContinuousDeployment branches: master: mode: ContinuousDelivery + release: + mode: ContinuousDelivery ignore: sha: [] \ No newline at end of file From e51a2e563be204dfd7693f6f9ed936b45cc50fcf Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:05:56 -0800 Subject: [PATCH 77/97] Merge branch 'maintenance/release-versioning' This undoes the update made in the previous merge (0f071ff). This, it turns out, was based on a misunderstanding of GitVersion's behavior, and with further investigation didn't fix the underlying issue it was targeting. --- GitVersion.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index 952eda0..b84f148 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -3,7 +3,5 @@ mode: ContinuousDeployment branches: master: mode: ContinuousDelivery - release: - mode: ContinuousDelivery ignore: sha: [] \ No newline at end of file From 8ffa4410015be2f674053805ab178999edd5a3ee Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:06:53 -0800 Subject: [PATCH 78/97] Migrate from `GitVersionTask` to `GitVersion.MsBuild` These maintain feature parity, but moving forward only `GitVersion.MsBuild` will continue to be maintained. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 4b119fe..e0a3a87 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 9fd60c69e9a07c5ab707b45b3d2da80cf07f7e38 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:08:04 -0800 Subject: [PATCH 79/97] Introduce Microsoft SourceLink for GitHub This ensures that the symbols published as part of the symbols package can be tied back to specific commits of specific source files on GitHub, and thus should allow implementors to step into OnTopic code when debugging their applications. This is a build-only dependency, and won't introduce additional dependencies for implementors. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index e0a3a87..4aca74a 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -34,6 +34,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 42449fb8751567d73608f979201983b35a06430d Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:10:19 -0800 Subject: [PATCH 80/97] Ensure a symbols package is generated As part of our build process, we ensure that a separate symbols package is generated. As such, this doesn't technically need to be repeated here. But as this is a best practice and one we want to enforce, we're making sure this is explicit. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 4aca74a..88d2cd4 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -17,6 +17,7 @@ ©2021 Ignia, LLC bin\$(Configuration)\ Ignia + true From 026910efea909fd60e4c81e854745a32992f3a78 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:10:35 -0800 Subject: [PATCH 81/97] Ensure any untracked resources are embedded There aren't currently any untracked resources in these projects, but if any are introduced later, we want them to be tracked by SourceLink. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 88d2cd4..50f3754 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -17,6 +17,7 @@ ©2021 Ignia, LLC bin\$(Configuration)\ Ignia + true true From e03fec5ef4aa3b147d08d61be55e6b7772bc9b11 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:10:47 -0800 Subject: [PATCH 82/97] Flag if this is a continuous integration build This helps SourceLink determine that this is a _deterministic_ build. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 50f3754..061cebf 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -31,6 +31,10 @@ 1701;1702;CA2227 + + true + + all From e960130e5aa0ee9eb86080d810618b54049f3cc5 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Sat, 20 Feb 2021 13:10:55 -0800 Subject: [PATCH 83/97] Set neutral/default language for assemblies to English --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 061cebf..ca500db 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -17,6 +17,7 @@ ©2021 Ignia, LLC bin\$(Configuration)\ Ignia + en true true From d65a81adfbe6bf1bf21c50d3e9ee65063df8750e Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 11:40:31 -0800 Subject: [PATCH 84/97] Introduced icon for the NuGet packages For consistency with other projects, the `Icon.png` is placed within the root of the solution, even though it's only needed within the `OnTopic.Data.Transfer` project itself. For now, we're using the Ignia logo as the icon, and have set it to 128x128 per NuGet recommendations. --- Icon.png | Bin 0 -> 5460 bytes OnTopic.Data.Transfer.sln | 1 + .../OnTopic.Data.Transfer.csproj | 5 +++++ 3 files changed, 6 insertions(+) create mode 100644 Icon.png diff --git a/Icon.png b/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..63cd745af81263c82802b40a9955b80d0309aa06 GIT binary patch literal 5460 zcmch5dpwi<|NpgN4&_V{F*8NXHs-K}usP*?D&4ueH#;z=u}wzNkV8ZiIV4f(zLlav zAz2BPLk^v!)j}mDStGWZxnta;i1ZMqb3B{w z8}H#qjSr*ZX^2fWa4Q}GOb|imQsBIZ@JJ4UXN~xlmjGTb-$oIBe)611dGOC(I)07tQi4^C758~KVArMIyQ|# z@F6+=m<;@6jR@g#qX=kpY;3G?tf?`J9gN1}@pv@G1Z`r10ud-qTqKvmLq&3Qe`X-j zIaD??ipyk0!k05rf><$JYXnI3yM~CUUuh#bKYRimL-Qz6Xsj`2S<_FTyZiqPjfnV# z=5T%JfAjsfi8+38QFOEqox_S@Q|TaF_vcg`uES1{;t#R^L9oM4dL;K(IF0%%HY$c4 z{>>&D6-^JPM}RO6s0#awXH*D_%i@Hv{=tM_!@sPEA`sbh3YW$9W3j@2F0y3`f9PqA->O4CXu3oke3Z z;(kI+uqX@&HSxpZ2&Om!=KqC)HAkaxDgO!B-JReP$>CBWsdN{TH3B4M%w*CCG&&WJ zF{R*8SUTMtWlF_iP?k7`DaynQN1>Q8EX;#w7TCX4%2z`0sU3V^P5zzf2-n z1krJ(I15u0-prDM!kFP0s30tkjtat<({MOTj0Fv6`ZJp+n+a|`O8CEKU1miCGvZC` z94sAiL`OW)4vTfb*_&X@94$zAEY==_vv4p$z-d$hgT;=ZfMvprpaj#=QIWw2_}@&3 ztZ)_^v=a0iobm7bE<~aio5f&;g998NCwsVy1JN9dH#bL_7-N^W3`k*FDxJ;TNvAoo zSrPDWrA%P{8}ok9#Q!tR@3Ejwzl{6;B-;IBEczE2XC?=%x40i2!IvKW<0_m9aszh? zfkIv0-Jl=}$QkkD=D#CsYJsN*nVDiyR2&|I!UW-Ps34q~1&U^2f;Xe#EzK;C8+#!}dHyI`;~{`-C~!5W+V>t^^KL=B-t2GhZIi$*NVTy9JM zdoll6>HeRKK`*QMzDVfr#q{_3eH;6ih74-|b_?wC%a^~NFW`q?PbhjM=o1@!{^Z^0 z3FUz3t*$QUjiNiMv)*qWT67+Z?`nX?Ry`bIqxHlj60>pQ=eYE*Q%^v*FKh7=y7e#aoy=nk7Ig<-nM@BmT-mM zjd2(kgBp590KMY9V#dHJv6vTUlzD3cQsrrOUwhNtwM-i#s!-6AmU7w>#!wrHIH$|tC%@BTcj1qcrhqd^t34?**!2GJ z%*U=8wU*9$wzAKZ1!o1>f_yBi8yKfFeqhgRxL|c?Gw}ZWN*=(IUi@IW>W}iC30i<+ zCA(xZe`YjZB**FXw1VM_HYB|rkrbeH6FW8o3XW z-s`J^AGl0$Jdln;B|ce{P0Uk*Z&-RDHn+wFL#PI>(wKXXGWMptQdGD%{2q#zn1sxg z-F;@VdS+lK>9__@vZo^-@V?b*hm^xK74sgCj1c*-G7>gd3*uezSKpLj-vljF)Gp9F zTyWQ_#G`zpo~>WV-P*wvqH}2^Z8r+Gg8tE9e4PQLV=s(+EcvX=saMJ_Nm*2k?}G|o zD5-VOr2Of8`5sbOd3)31>bt{UDL!t4wQVU8T5j4~WV-9mtF@6Eyd{014>6MS8k>=P zcE@ieu)s1UK?k<4+5!4zf%fQ3r66ebbH?+$d1x@nK*9&=SJv>mOpC`px>xq+7a#q) zPCSwvJH9IMkOWHbl7A*I{3x_mm8TGp0%L>(OkHzW^Qit!>=0smcTjh|{vkQ_kF8Su zLr|CXeEGl?fs(tgy%9|cwy7R#l$Cz{q(`OPw_<#sxVh2I4ciOOJfH(QJX+is>IjGhX2aqY_`Y8Ty2*Hxz&meDJ0a*(A>aZgG=_n70JT1J^JWEwhV?IbIzi;9v{WF~OtH>RZyVuOkH zopyW!fwf#onJ<4(DmV4mmgiET=dBLAcg)>$K?*i^Y~QFY+r*y_l7P>&zIzpR1Zd7v z6Z9h$%A+&X=iMKZUo~C0UUbJuQlJ&$DIWH0$azW3uR=t@{Y3{~)RmVUB?eR`EvbThT zy{>%T0hew^>4UbwF?RZyOG&cMl(b=`au3&l@gkYR#7naDYUu6bw46D+8lT?b=5FV` z)^f_WAENK;sM>XI-qm^_V|%@hXXb46XE|l^=7Ke+PMhDd&&nL%&GB2% zyCpwe5?4{(CVn%Y%kNV}5(ME{Bdrp*#2$uqUn41$Khejf=S~D}_1;RDAv)zgl*oSA zbL3&l7=){r3P9-Z87Z^kYKb`?&X3-haAX6&p{(y@78-_?UCigbd+n4ZU%)^H%Nc1) zjbmksvv~s8#n~g_Ep@^BfELw>f?5&eBjqt^Fo?@&gNw2BE&`z;q3G_ zQLkPw=(@SQ$JXma@x?Z9**l81P;#IDH7XvmfoU|)n6qo2nk*MuHIiQ^c~1bJUxb}5 zzTJ|eOs?fRr%LObrj-m`S3Durur3MExxTV_ZH}^tTp;vuJkpN6lEV{weTc*_lQ}A& z&uw9%>D}k&KG}$#GA?q5;+Yy-Y}KA7K8S|m_MY(XX<{?N>(gfxYp9T{7^#LFp!V4I zJ*NZwj)(ac^ar2ub~viT_kYNa4mC=(=zLY9;CGs+P}RWV54wz1Z4%G`0RUDC_U!utVM z6Z<8va1?Y{TDRU;8JwNu<4~P3Y1&;MfGBhEteV2 z(kTr5TwG*Y8KFdmEo`p#NO+&#F9?nGRC&qPW;+OvI5*Ul8HopEJcf-RcAD;9b6;7x z&h~YZ2EPU_VdB~NZp7R==}7ji(V(P|gjaW`DmCTqYpSPb*vNc(R675@G5+lkQUFY& z#~7Xuat$j}EK0WJCA3!CLrY~8antf556$$+#ob!_F3A%9lvi6}iyELj8L&dGeS;O1 zcz54hMSWo-UTs2fur5G__(DbM!?E)H!mr@ZrWZZ_=Yzv;p4lRjL0Z?*Z(7ByL7*|4 zSmIxTRGRAVs)Z|yj76b=VXG=<4ERkLepOS!m5ENOPLxyX5)aF1jrGn`XS zcYgbERp+{zETdXGJCC)xuE)*~zE_)_$5C61U|(c*8%k$sr;A&g>m=b?qrVHg%zwx&lJ;fwLK1X6f4}gBG^*rtLU$Bf`H>iF_O@ zW+{A=Sm<_*?eCS9c2+C=UB$L$q39}1y7rD0M=R2QMXZ01GZ24BNjg`rA6e_pw?p>a zuHT8JCm{2o!z$J9PloZO_Yprbd8s(t8($@5J|9^bUytqxJF%3|u%KtJIsd^E=*rV*4`ixg-+BAt zU|}t3^;+>~BsujYx#7CK4e`;<(?TqiUzep$(j*oVYCn1g9zJVu>57ev=3HORuIW6W z{sMfPSbD+m(cHBI5MYPhhpEO|*7digrTO9&i^nS3{jc03J}t~kgiijJBhlKBlX@8` zfRCuKD-u3E)bxqT_%kXc{YjO6*sexE9f8DkI;c5lAdtn*rbWH3i*Ed>_w296so%z_&YlM^GuPeCyVOapC zL8by&)E#kYn*9A^h&q_Y!1NUy~)Gu7pnH<~#zlFdf z*_T%_8!yN-?yz>aF4ec6E4zeJGWewEWA!MTBrAWiE8hsZck9X6;$zKe;1{xdG29Y& z-$5t-&Xa>38)jz>-7*P}^MQik#LCH5>)+EB6`U+B4}4th;<-luYz$#VmAEr@+sq!F X){-k0F8f`U|Mzrp@F1PDCnx_GF=FiH literal 0 HcmV?d00001 diff --git a/OnTopic.Data.Transfer.sln b/OnTopic.Data.Transfer.sln index 32524d5..8c8b241 100644 --- a/OnTopic.Data.Transfer.sln +++ b/OnTopic.Data.Transfer.sln @@ -10,6 +10,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E838A1C2-B10E-4B70-8939-E1DC47EAFC5B}" ProjectSection(SolutionItems) = preProject GitVersion.yml = GitVersion.yml + Icon.png = Icon.png README.md = README.md EndProjectSection EndProject diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index ca500db..68595c5 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -20,8 +20,13 @@ en true true + Icon.png + + + + https://github.com/Ignia/Topics-Data-Transfer C# .NET CMS DTO From 6492aab7010b6154add6df764d90ecc4af446bcf Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 11:44:42 -0800 Subject: [PATCH 85/97] Automatically determine the `RepositoryUrl` We previously hard-code this, but SourceLink can instead automatically generate the git repository reference. This is needed for SourceLink to provide a reference to the exact code referenced by the symbols package. As part of this, combined two ``s which were both related to NuGet package metadata. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 68595c5..d2291d3 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -14,9 +14,12 @@ Ignia OnTopic Libraries for supporting the import and export of OnTopic entities. + C# .NET CMS DTO ©2021 Ignia, LLC bin\$(Configuration)\ Ignia + true + true en true true @@ -27,12 +30,6 @@ - - https://github.com/Ignia/Topics-Data-Transfer - C# .NET CMS DTO - true - - 1701;1702;CA2227 From cbc66d43db27e7d26023ea770cc5cfa63b5fdac3 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 11:46:12 -0800 Subject: [PATCH 86/97] Configure solution to auto-generate XML documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are diligent about maintaining XML Docs for all types and members. But we don't currently _do_ anything with that documentation. By auto-generating it on compilation, we at least ensure that it will end up being included in NuGet packages—and thus available to IntelliSense for implementers. In addition, this will allow us to use e.g. DocFX in the future as part of our CI/CD process, should we choose. As part of this, accept the default XML output path. This is the same as the one we have configured, and there's no real benefit to being explicit in this case. --- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index d2291d3..7df54b6 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -16,13 +16,13 @@ Libraries for supporting the import and export of OnTopic entities. C# .NET CMS DTO ©2021 Ignia, LLC - bin\$(Configuration)\ Ignia true true en true true + true Icon.png From 89e4d032a776f74a3d3ccb23b627fd1fe0334354 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 11:52:02 -0800 Subject: [PATCH 87/97] Separated out common properties into `Directory.Build.props` This isn't strictly necessary for the OnTopic Data Transfer solution since, currently, it only has one project. By separating out the common properties into the `Directory.Build.props` file, however, we ensure consistency and familiarity with other OnTopic projects, while simultaneously keeping the `csproj` file focused on the properties that are truly specific to it. --- Directory.Build.props | 33 +++++++++++++++++++ OnTopic.Data.Transfer.sln | 1 + .../OnTopic.Data.Transfer.csproj | 25 +------------- 3 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 Directory.Build.props diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..3a70621 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,33 @@ + + + + 9.0 + enable + latest + AllEnabledByDefault + + + + Ignia + OnTopic + ©2021 Ignia, LLC + Ignia + https://github.com/Ignia/Topics-Library + true + en + true + true + true + true + Icon.png + + + + + + + + true + + + \ No newline at end of file diff --git a/OnTopic.Data.Transfer.sln b/OnTopic.Data.Transfer.sln index 8c8b241..193e318 100644 --- a/OnTopic.Data.Transfer.sln +++ b/OnTopic.Data.Transfer.sln @@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OnTopic.Data.Transfer.Tests EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E838A1C2-B10E-4B70-8939-E1DC47EAFC5B}" ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props GitVersion.yml = GitVersion.yml Icon.png = Icon.png README.md = README.md diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 7df54b6..389d394 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -1,43 +1,20 @@  - OnTopic.Data.Transfer netstandard2.1 - 9.0 - enable - latest - AllEnabledByDefault + OnTopic.Data.Transfer OnTopic Data Transfer Library - Ignia - OnTopic Libraries for supporting the import and export of OnTopic entities. C# .NET CMS DTO - ©2021 Ignia, LLC - Ignia - true - true - en - true - true - true - Icon.png - - - - 1701;1702;CA2227 - - true - - all From 1cefc076fab99e5af1f551232dfa9682db84b25f Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:05:54 -0800 Subject: [PATCH 88/97] Ensured all XML Doc type and member references are resolved Some type and member references were not properly resolved. This mostly included missing namespaces and outdated method signature. This resolves CS1574. --- OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs | 3 ++- OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs | 11 ++++++----- OnTopic.Data.Transfer/Interchange/ExportOptions.cs | 4 ++-- OnTopic.Data.Transfer/Interchange/ImportOptions.cs | 2 +- OnTopic.Data.Transfer/Interchange/ImportStrategy.cs | 3 ++- OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 2 +- OnTopic.Data.Transfer/KeyValuesPair.cs | 1 + OnTopic.Data.Transfer/MultiMap.cs | 9 +++++---- OnTopic.Data.Transfer/RecordDataCollection.cs | 8 ++++---- OnTopic.Data.Transfer/TopicData.cs | 6 +++--- 10 files changed, 27 insertions(+), 22 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 7c151a9..7b873cf 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -5,6 +5,7 @@ \=============================================================================================================================*/ using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OnTopic.Attributes; using OnTopic.Data.Transfer.Interchange; namespace OnTopic.Data.Transfer.Tests { @@ -123,7 +124,7 @@ public void Export_TopicWithReferences_ExcludesExternalAssociations() { | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: EXTERNAL TOPIC REFERENCE: EXPORTS UNIQUE KEY \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that references another topic. Confirms + /// Creates a with an arbitrary that references another topic. Confirms /// that it is converted to a if valid, and is otherwise left as is. /// [TestMethod] diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index daa0c23..9f8e65a 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OnTopic.Attributes; using OnTopic.Data.Transfer.Interchange; namespace OnTopic.Data.Transfer.Tests { @@ -224,7 +225,7 @@ public void Export_ExcludesReservedAttributes() { | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: OUT OF SCOPE: SKIPS REFERENCE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that references a + /// Creates a with an arbitrary that references a /// outside of the . Confirms that the reference is maintained as an attribute, but /// not added as a reference. /// @@ -250,7 +251,7 @@ public void ExportWithLegacyTopicReferences_OutOfScope_SkipsReference() { | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: MISSING TOPIC REFERENCE: SKIPS REFERENCE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that references a missing with an arbitrary that references a missing . Confirms that the reference is maintained as an attribute, but not added as a reference. /// [TestMethod] @@ -276,7 +277,7 @@ public void ExportWithLegacyTopicReferences_MissingTopicReference_SkipsReference | TEST: EXPORT WITH LEGACY TOPIC REFERENCES: INVALID TOPIC REFERENCE: EXPORTS ORIGINAL VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with an arbitrary that that contains a non-numeric (i.e., + /// Creates a with an arbitrary that that contains a non-numeric (i.e., /// invalid) topic reference. Confirms that the original value is exported. /// [TestMethod] @@ -460,8 +461,8 @@ public void Import_BaseTopic_MapsNewBaseTopic() { | TEST: IMPORT: INVALID BASE TOPIC: MAINTAINS EXISTING VALUE \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with a that is invalid and ensures that the - /// is not updated. + /// Creates a with a BaseTopic in its that is invalid. + /// Ensures that the is not updated. /// [TestMethod] public void Import_InvalidBaseTopic_MaintainsExistingValue() { diff --git a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs index 698bd09..3a690ac 100644 --- a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs @@ -12,7 +12,7 @@ namespace OnTopic.Data.Transfer.Interchange { | CLASS: EXPORT OPTIONS \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// Provides options for configuring the method. + /// Provides options for configuring the method. /// /// /// When exporting a graph into a new object, there are a few considerations @@ -73,7 +73,7 @@ public class ExportOptions { /// /// /// By default, only the current is exported. Optionally, a caller may choose to also include any - /// nested topics under the current . While these are technically separate , it + /// nested topics under the current . While these are technically separate s, it /// may be desirable to include them since they are so closely related to the current . /// /// diff --git a/OnTopic.Data.Transfer/Interchange/ImportOptions.cs b/OnTopic.Data.Transfer/Interchange/ImportOptions.cs index 59b3d77..c0683d5 100644 --- a/OnTopic.Data.Transfer/Interchange/ImportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ImportOptions.cs @@ -12,7 +12,7 @@ namespace OnTopic.Data.Transfer.Interchange { | CLASS: IMPORT OPTIONS \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// Provides options for configuring the method. + /// Provides options for configuring the method. /// /// /// When importing into an existing object, there are a lot of potential diff --git a/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs b/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs index 2b63ad7..c360bc9 100644 --- a/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs +++ b/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs @@ -4,6 +4,7 @@ | Project Topics Library \=============================================================================================================================*/ using OnTopic.Attributes; +using OnTopic.Collections.Specialized; using OnTopic.Metadata; namespace OnTopic.Data.Transfer.Interchange { @@ -45,7 +46,7 @@ public enum ImportStrategy { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Imports all records unless the is older than the target . + /// "TrackedRecord{T}.LastModified"/>. /// /// /// This is generally the preferred import strategy. It imports all new topics, relationships, and attributes, diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index f426604..fe36c15 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -232,7 +232,7 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import /// This does not address the scenario where implicit topic pointers (i.e., attributes ending in Id) /// cannot be resolved because the target topics haven't yet been saved—and, therefore, the cannot be translated to a . There isn't any obvious way to address this via - /// directly. + /// directly. /// /// /// The target to write data to. diff --git a/OnTopic.Data.Transfer/KeyValuesPair.cs b/OnTopic.Data.Transfer/KeyValuesPair.cs index 115e17e..36b7771 100644 --- a/OnTopic.Data.Transfer/KeyValuesPair.cs +++ b/OnTopic.Data.Transfer/KeyValuesPair.cs @@ -3,6 +3,7 @@ | Client Ignia, LLC | Project Topics Library \=============================================================================================================================*/ +using System; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; diff --git a/OnTopic.Data.Transfer/MultiMap.cs b/OnTopic.Data.Transfer/MultiMap.cs index ee53576..fd3ebd8 100644 --- a/OnTopic.Data.Transfer/MultiMap.cs +++ b/OnTopic.Data.Transfer/MultiMap.cs @@ -3,6 +3,7 @@ | Client Ignia, LLC | Project Topics Library \=============================================================================================================================*/ +using System; using System.Collections.ObjectModel; using OnTopic.Collections.Specialized; using OnTopic.Internal.Diagnostics; @@ -13,8 +14,8 @@ namespace OnTopic.Data.Transfer { | CLASS: MULTIMAP \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides a of objects, thus - /// supporting a many-to-many mapping. + /// The class provides a of + /// objects, thus supporting a many-to-many mapping. /// /// /// The is intended to model the —though instead of a exposing a @@ -28,8 +29,8 @@ public class MultiMap: KeyedCollection { | OVERRIDE: GET KEY FOR ITEM \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Method must be overridden for the to identify the appropriate from each object. + /// Method must be overridden for the to identify the appropriate + /// from each object. /// /// The object from which to extract the key. /// The key for the specified collection item. diff --git a/OnTopic.Data.Transfer/RecordDataCollection.cs b/OnTopic.Data.Transfer/RecordDataCollection.cs index e6d1af5..467e699 100644 --- a/OnTopic.Data.Transfer/RecordDataCollection.cs +++ b/OnTopic.Data.Transfer/RecordDataCollection.cs @@ -12,8 +12,8 @@ namespace OnTopic.Data.Transfer { | CLASS: RECORD DATA COLLECTION \---------------------------------------------------------------------------------------------------------------------------*/ /// - /// The class provides a of - /// objects. + /// The class provides a of objects. /// public class RecordDataCollection: KeyedCollection { @@ -21,8 +21,8 @@ public class RecordDataCollection: KeyedCollection { | OVERRIDE: GET KEY FOR ITEM \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Method must be overridden for the to identify the appropriate - /// from each object. + /// Method must be overridden for the to identify the appropriate from each object. /// /// The object from which to extract the key. /// The key for the specified collection item. diff --git a/OnTopic.Data.Transfer/TopicData.cs b/OnTopic.Data.Transfer/TopicData.cs index e231889..67c5ebf 100644 --- a/OnTopic.Data.Transfer/TopicData.cs +++ b/OnTopic.Data.Transfer/TopicData.cs @@ -68,9 +68,9 @@ public class TopicData { /// /// /// - /// The is deprecated in favor of the new , but legacy data will - /// still reference it in the JSON. This property thus allows backward compatibility, while being marked as deprecated - /// to discourage callers from utilizing it. + /// The is deprecated in favor of storing the BaseTopic in the collection, but legacy data will still reference it in the JSON. This property thus allows backward + /// compatibility, while being marked as deprecated to discourage callers from utilizing it. /// /// /// Unfortunately, .NET 3.x doesn't permit a way to hide this from the public interface or from serialization. As such, From 7f2e76dfcb2cc2a6f827e1d10e70007478694f9e Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:08:24 -0800 Subject: [PATCH 89/97] Resolved poorly formed XML in documentation Resolved CS1570. --- .../ExportOptionsTest.cs | 2 +- .../Interchange/ImportStrategy.cs | 22 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 7b873cf..04f6249 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -100,7 +100,7 @@ public void Export_TopicWithRelationships_ExcludesExternalAssociations() { | TEST: EXPORT: TOPIC WITH REFERENCES: EXCLUDES EXTERNAL ASSOCIATIONS \-------------------------------------------------------------------------------------------------------------------------*/ /// - /// Creates a with several and ensures that the with several and ensures that the collection does not include external associations—i.e., references that refer to s outside of the current export scope. /// diff --git a/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs b/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs index c360bc9..86a9660 100644 --- a/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs +++ b/OnTopic.Data.Transfer/Interchange/ImportStrategy.cs @@ -65,15 +65,14 @@ public enum ImportStrategy { \-------------------------------------------------------------------------------------------------------------------------*/ /// /// Imports all records, even if the is older than the target . + /// cref="TrackedRecord{T}.LastModified"/>. /// /// - /// This will effectively overwrite any attributes that already exist in the database, even if they were - /// modified after the source . For example, if the starts at - /// Root:Configuration, any modifications to out-of-the-box titles, descriptions, visibility, sort order, &c. - /// will be overwritten with the new values. Any attribute values that it overwrites will still be recoverable as part of - /// the attribute versioning schema, with the exception of , which is excluded from - /// versioning. + /// This will effectively overwrite any attributes that already exist in the database, even if they were modified + /// after the source . For example, if the starts at Root: + /// Configuration, any modifications to out-of-the-box titles, descriptions, visibility, sort order, etc. will be + /// overwritten with the new values. Any attribute values that it overwrites will still be recoverable as part of the + /// attribute versioning schema, with the exception of , which is excluded from versioning. /// Overwrite = 3, @@ -86,11 +85,10 @@ public enum ImportStrategy { /// /// /// This will effectively eliminate any and all customizations done within the scope of the import. For example, if - /// the starts at Root:Configuration, this will delete any custom s, metadata registrations, &c. made to the database. This should be used with great - /// care. Any attribute values that it overwrites will still be recoverable as part of - /// the attribute versioning schema, with the exception of , which is excluded from - /// versioning. + /// the starts at Root:Configuration, this will delete any custom s, metadata registrations, etc. made to the database. This should be used with great care. Any + /// attribute values that it overwrites will still be recoverable as part of the attribute versioning schema, with the + /// exception of , which is excluded from versioning. /// Replace = 4 From f4d368b28899b604a87f5f93417bd2cb338da920 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:10:44 -0800 Subject: [PATCH 90/97] Ensured all types, parameters are correctly documented This resolves CS1573, CS1591. --- OnTopic.Data.Transfer/Interchange/ExportOptions.cs | 2 ++ OnTopic.Data.Transfer/Interchange/TopicExtensions.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs index 3a690ac..9295e10 100644 --- a/OnTopic.Data.Transfer/Interchange/ExportOptions.cs +++ b/OnTopic.Data.Transfer/Interchange/ExportOptions.cs @@ -43,6 +43,7 @@ public class ExportOptions { /// public bool IncludeExternalAssociations { get; set; } + /// [Obsolete("The IncludeExternalReferences option has been renamed to IncludeExternalAssociations", true)] public bool IncludeExternalReferences { get; set; } @@ -135,6 +136,7 @@ public bool IncludeNestedTopics { /// public bool TranslateLegacyTopicReferences { get; set; } + /// [Obsolete("The TranslateTopicPointers option has been renamed to TranslateLegacyTopicReferences", true)] public bool TranslateTopicPointers { get; set; } diff --git a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs index fe36c15..633f37f 100644 --- a/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs +++ b/OnTopic.Data.Transfer/Interchange/TopicExtensions.cs @@ -177,6 +177,7 @@ childTopic.ContentType is "List" /// cref="Topic"/> entity. /// /// The source to operate off of. + /// The source graph to import into the . /// An optional object to specify import settings. public static void Import(this Topic topic, TopicData topicData, [NotNull]ImportOptions? options = null) { From 92d953c5152937f872015db17c1417386a0018e1 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:19:23 -0800 Subject: [PATCH 91/97] Resolved potential dereferencing of null references In the context of unit test assertions, this is almost always resolved by using the null-conditional operator (`?.) and updating the reference type to accept nulls. This resolves CS8602. --- .../DeserializationTest.cs | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs index ef38be7..1e94e69 100644 --- a/OnTopic.Data.Transfer.Tests/DeserializationTest.cs +++ b/OnTopic.Data.Transfer.Tests/DeserializationTest.cs @@ -46,12 +46,12 @@ public void Deserialize_TopicData_ReturnsExpectedResults() { var topicData = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, topicData.Key); - Assert.AreEqual(sourceData.UniqueKey, topicData.UniqueKey); - Assert.AreEqual(sourceData.ContentType, topicData.ContentType); - Assert.AreEqual(0, topicData.Relationships.Count); - Assert.AreEqual(0, topicData.Attributes.Count); - Assert.AreEqual(0, topicData.Children.Count); + Assert.AreEqual(sourceData.Key, topicData?.Key); + Assert.AreEqual(sourceData.UniqueKey, topicData?.UniqueKey); + Assert.AreEqual(sourceData.ContentType, topicData?.ContentType); + Assert.AreEqual(0, topicData?.Relationships.Count); + Assert.AreEqual(0, topicData?.Attributes.Count); + Assert.AreEqual(0, topicData?.Children.Count); } @@ -85,13 +85,13 @@ public void Deserialize_DerivedTopicKey_ReturnsExpectedResults() { var topicData = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, topicData.Key); - Assert.AreEqual(sourceData.UniqueKey, topicData.UniqueKey); - Assert.AreEqual(sourceData.ContentType, topicData.ContentType); - Assert.AreEqual(sourceData.DerivedTopicKey, topicData.DerivedTopicKey); - Assert.AreEqual(0, topicData.Relationships.Count); - Assert.AreEqual(0, topicData.Attributes.Count); - Assert.AreEqual(0, topicData.Children.Count); + Assert.AreEqual(sourceData.Key, topicData?.Key); + Assert.AreEqual(sourceData.UniqueKey, topicData?.UniqueKey); + Assert.AreEqual(sourceData.ContentType, topicData?.ContentType); + Assert.AreEqual(sourceData.DerivedTopicKey, topicData?.DerivedTopicKey); + Assert.AreEqual(0, topicData?.Relationships.Count); + Assert.AreEqual(0, topicData?.Attributes.Count); + Assert.AreEqual(0, topicData?.Children.Count); } #pragma warning restore CS0618 // Type or member is obsolete @@ -117,9 +117,9 @@ public void Deserialize_KeyValuesPair_ReturnsExpectedResults() { var keyValuesPair = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, keyValuesPair.Key); - Assert.AreEqual(sourceData.Values.Count, keyValuesPair.Values.Count); - Assert.AreEqual(sourceData.Values.FirstOrDefault(), keyValuesPair.Values.FirstOrDefault()); + Assert.AreEqual(sourceData.Key, keyValuesPair?.Key); + Assert.AreEqual(sourceData.Values.Count, keyValuesPair?.Values.Count); + Assert.AreEqual(sourceData.Values.FirstOrDefault(), keyValuesPair?.Values.FirstOrDefault()); } @@ -147,9 +147,9 @@ public void Deserialize_RelationshipData_ReturnsExpectedResults() { var keyValuesPair = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, keyValuesPair.Key); - Assert.AreEqual(sourceData.Values.Count, keyValuesPair.Values.Count); - Assert.AreEqual(sourceData.Values.FirstOrDefault(), keyValuesPair.Values.FirstOrDefault()); + Assert.AreEqual(sourceData.Key, keyValuesPair?.Key); + Assert.AreEqual(sourceData.Values.Count, keyValuesPair?.Values.Count); + Assert.AreEqual(sourceData.Values.FirstOrDefault(), keyValuesPair?.Values.FirstOrDefault()); } @@ -176,9 +176,9 @@ public void Deserialize_RecordData_ReturnsExpectedResults() { var recordData = JsonSerializer.Deserialize(json); - Assert.AreEqual(sourceData.Key, recordData.Key); - Assert.AreEqual(sourceData.Value, recordData.Value); - Assert.AreEqual(sourceData.LastModified, recordData.LastModified); + Assert.AreEqual(sourceData.Key, recordData?.Key); + Assert.AreEqual(sourceData.Value, recordData?.Value); + Assert.AreEqual(sourceData.LastModified, recordData?.LastModified); } @@ -259,33 +259,33 @@ public void Deserialize_TopicGraph_ReturnsExpectedResults() { var topicData = JsonSerializer.Deserialize(json); - var relationshipData = topicData.Relationships.FirstOrDefault(); - var referenceData = topicData.References.FirstOrDefault(); - var attributeData = topicData.Attributes.FirstOrDefault(); - var childTopicData = topicData.Children.FirstOrDefault(); - - Assert.AreEqual(sourceTopicData.Key, topicData.Key); - Assert.AreEqual(sourceTopicData.UniqueKey, topicData.UniqueKey); - Assert.AreEqual(sourceTopicData.ContentType, topicData.ContentType); + var relationshipData = topicData?.Relationships.FirstOrDefault(); + var referenceData = topicData?.References.FirstOrDefault(); + var attributeData = topicData?.Attributes.FirstOrDefault(); + var childTopicData = topicData?.Children.FirstOrDefault(); + + Assert.AreEqual(sourceTopicData.Key, topicData?.Key); + Assert.AreEqual(sourceTopicData.UniqueKey, topicData?.UniqueKey); + Assert.AreEqual(sourceTopicData.ContentType, topicData?.ContentType); Assert.AreEqual(1, sourceTopicData.Relationships.Count); Assert.AreEqual(1, sourceTopicData.Attributes.Count); Assert.AreEqual(1, sourceTopicData.Children.Count); - Assert.AreEqual(sourceRelationshipData.Key, relationshipData.Key); - Assert.AreEqual(sourceRelationshipData.Values.Count, relationshipData.Values.Count); - Assert.AreEqual(sourceRelationshipData.Values.FirstOrDefault(), relationshipData.Values.FirstOrDefault()); + Assert.AreEqual(sourceRelationshipData.Key, relationshipData?.Key); + Assert.AreEqual(sourceRelationshipData.Values.Count, relationshipData?.Values.Count); + Assert.AreEqual(sourceRelationshipData.Values.FirstOrDefault(), relationshipData?.Values.FirstOrDefault()); - Assert.AreEqual(sourceReferenceData.Key, referenceData.Key); - Assert.AreEqual(sourceReferenceData.Value, referenceData.Value); - Assert.AreEqual(sourceReferenceData.LastModified, referenceData.LastModified); + Assert.AreEqual(sourceReferenceData.Key, referenceData?.Key); + Assert.AreEqual(sourceReferenceData.Value, referenceData?.Value); + Assert.AreEqual(sourceReferenceData.LastModified, referenceData?.LastModified); - Assert.AreEqual(sourceAttributeData.Key, attributeData.Key); - Assert.AreEqual(sourceAttributeData.Value, attributeData.Value); - Assert.AreEqual(sourceAttributeData.LastModified, attributeData.LastModified); + Assert.AreEqual(sourceAttributeData.Key, attributeData?.Key); + Assert.AreEqual(sourceAttributeData.Value, attributeData?.Value); + Assert.AreEqual(sourceAttributeData.LastModified, attributeData?.LastModified); - Assert.AreEqual(sourceChildTopicData.Key, childTopicData.Key); - Assert.AreEqual(sourceChildTopicData.UniqueKey, childTopicData.UniqueKey); - Assert.AreEqual(sourceChildTopicData.ContentType, childTopicData.ContentType); + Assert.AreEqual(sourceChildTopicData.Key, childTopicData?.Key); + Assert.AreEqual(sourceChildTopicData.UniqueKey, childTopicData?.UniqueKey); + Assert.AreEqual(sourceChildTopicData.ContentType, childTopicData?.ContentType); Assert.AreEqual(0, sourceChildTopicData.Relationships.Count); Assert.AreEqual(0, sourceChildTopicData.Children.Count); } From d05225741ff8aa7fc503a2c982d6c15cfef9ba89 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:22:26 -0800 Subject: [PATCH 92/97] Addressed possible null reference arguments where unexpected In context of unit test assertions, this can almost always be resolved by simply updating the generic type parameter to accept nulls. Resolves CS8604. --- .../ExportOptionsTest.cs | 2 +- .../ImportOptionsTest.cs | 10 ++++---- .../TopicExtensionsTest.cs | 24 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs index 04f6249..b647e0b 100644 --- a/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ExportOptionsTest.cs @@ -144,7 +144,7 @@ public void ExportWithLegacyTopicReferences_ExternalTopicReference_ExportsUnique topicData.Attributes.TryGetValue("SomeId", out var someAttribute); - Assert.AreEqual("Root", someAttribute.Value); + Assert.AreEqual("Root", someAttribute?.Value); } diff --git a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs index 779eded..6e2ed60 100644 --- a/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/ImportOptionsTest.cs @@ -82,7 +82,7 @@ public void ImportAsMerge_TopicDataWithAttributes_UpdatesOlderValues() { } ); - Assert.AreEqual("New Value", topic.Attributes.GetValue("Attribute")); + Assert.AreEqual("New Value", topic.Attributes.GetValue("Attribute")); } @@ -120,7 +120,7 @@ public void ImportAsMerge_TopicDataWithAttributes_SkipsNewerValues() { } ); - Assert.AreEqual("Original Value", topic.Attributes.GetValue("Attribute")); + Assert.AreEqual("Original Value", topic.Attributes.GetValue("Attribute")); } @@ -158,7 +158,7 @@ public void ImportAsOverwrite_TopicDataWithAttributes_ReplacesNewerValue() { } ); - Assert.AreEqual("New Value", topic.Attributes.GetValue("Attribute")); + Assert.AreEqual("New Value", topic.Attributes.GetValue("Attribute")); } @@ -207,8 +207,8 @@ public void ImportAsMerge_TopicDataWithReferences_SkipsNewerValues() { } ); - Assert.AreEqual(referencedTopic1, topic.References.GetValue("Reference")); - Assert.AreEqual(referencedTopic1, topic.References.GetValue("OldReference")); + Assert.AreEqual(referencedTopic1, topic.References.GetValue("Reference")); + Assert.AreEqual(referencedTopic1, topic.References.GetValue("OldReference")); } diff --git a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs index 9f8e65a..96db04c 100644 --- a/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs +++ b/OnTopic.Data.Transfer.Tests/TopicExtensionsTest.cs @@ -55,7 +55,7 @@ public void Export_BaseTopic_MapsReferenceData() { var topicData = topic.Export(); Assert.IsNotNull(topicData); - Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.References.FirstOrDefault()?.Value); + Assert.AreEqual(topic.BaseTopic.GetUniqueKey(), topicData.References.FirstOrDefault()?.Value); } @@ -77,8 +77,8 @@ public void Export_TopicWithAttributes_MapsAttributeDataCollection() { Assert.IsNotNull(topicData); Assert.AreEqual(1, topicData.Attributes.Count); - Assert.AreEqual("Attribute", topicData.Attributes.FirstOrDefault().Key); - Assert.AreEqual("Attribute Value", topicData.Attributes.FirstOrDefault().Value); + Assert.AreEqual("Attribute", topicData.Attributes.FirstOrDefault().Key); + Assert.AreEqual("Attribute Value", topicData.Attributes.FirstOrDefault().Value); } @@ -170,7 +170,7 @@ public void Export_TopicWithReferences_MapsReferenceDataCollection() { Assert.IsNotNull(topicData); Assert.IsNotNull(childTopicData); Assert.AreEqual(1, childTopicData.References.Count); - Assert.AreEqual("Root:Referenced", childTopicData.References.FirstOrDefault().Value); + Assert.AreEqual("Root:Referenced", childTopicData.References.FirstOrDefault().Value); } @@ -217,7 +217,7 @@ public void Export_ExcludesReservedAttributes() { var topicData = topic.Export(); Assert.AreEqual(1, topicData.Attributes.Count); - Assert.AreEqual("8", topicData.Attributes.FirstOrDefault().Value); + Assert.AreEqual("8", topicData.Attributes.FirstOrDefault().Value); } @@ -291,7 +291,7 @@ public void ExportWithLegacyTopicReferences_InvalidTopicReference_ExportsOrigina topicData.Attributes.TryGetValue("Rigid", out var rigidAttribute); - Assert.AreEqual("True", rigidAttribute.Value); + Assert.AreEqual("True", rigidAttribute.Value); } @@ -403,7 +403,7 @@ public void Import_BaseTopic_MapsBaseTopic() { topic.Import(topicData); Assert.IsNotNull(topic.BaseTopic); - Assert.AreEqual(baseTopic, topic.BaseTopic); + Assert.AreEqual(baseTopic, topic.BaseTopic); } @@ -453,7 +453,7 @@ public void Import_BaseTopic_MapsNewBaseTopic() { var childTopic = topic.Children.FirstOrDefault(); Assert.IsNotNull(childTopic.BaseTopic); - Assert.AreEqual(baseTopicData.Key, childTopic.BaseTopic?.Key); + Assert.AreEqual(baseTopicData.Key, childTopic.BaseTopic?.Key); } @@ -519,7 +519,7 @@ public void Import_TopicDataWithAttributes_SetsMissingAttributes() { topic.Import(topicData); - Assert.AreEqual("Attribute Value", topic.Attributes.GetValue("Attribute")); + Assert.AreEqual("Attribute Value", topic.Attributes.GetValue("Attribute")); } @@ -552,7 +552,7 @@ public void Import_TopicDataWithAttributes_SkipsNewerAttributes() { topic.Import(topicData); - Assert.AreEqual("Original Value", topic.Attributes.GetValue("Attribute")); + Assert.AreEqual("Original Value", topic.Attributes.GetValue("Attribute")); } @@ -585,7 +585,7 @@ public void Import_TopicDataWithAttributes_SkipsOlderAttribute() { topic.Import(topicData); - Assert.AreNotEqual(topicData.Attributes.FirstOrDefault()?.Value, topic.Attributes.GetValue("Attribute")); + Assert.AreNotEqual(topicData.Attributes.FirstOrDefault()?.Value, topic.Attributes.GetValue("Attribute")); } @@ -912,7 +912,7 @@ public void Import_TopicDataWithInvalidLegacyTopicReference_ImportsOriginalValue topic.Import(topicData); - Assert.AreEqual("6", topic.Attributes.GetValue("InitialBid")); + Assert.AreEqual("6", topic.Attributes.GetValue("InitialBid")); } From 81031679761ef580c49336255d7ab82ebdc09deb Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:36:52 -0800 Subject: [PATCH 93/97] Updated to Microsoft Test SDK This only impacts the build and test process, and has no impact on downstream consumers. All unit tests continue to execute as expected after this update. --- OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index 19514e6..2921233 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -13,7 +13,7 @@ - + From 6ab6bad0c54e9e579783dff0ee232c9c283646ee Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:37:49 -0800 Subject: [PATCH 94/97] Removed Coverlet While we'd like to use Coverlet, we previously ran into notable bugs on their web application interface, thus preventing us from adopting it. Until we have time to investigate further, we're removing it. --- .../OnTopic.Data.Transfer.Tests.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index 2921233..ef68939 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -16,10 +16,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - From f1590bcfd26050be9acd337c1e628d6598efce09 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 23 Feb 2021 12:40:13 -0800 Subject: [PATCH 95/97] Updated to OnTopic 5.0.0 Beta 4 This will correspond with the release of OnTopic Data Transfer 2.0.0 Beta 4. This isn't expected to have any impact on the Data Transfer library itself, as the changes since Beta 1 are outside the scope of the Data Transfer dependencies. --- OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj | 2 +- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index ef68939..099b7dc 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 389d394..1b55882 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From fa663356c5130e6c30c0362100dfa167bd230a0a Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 2 Mar 2021 15:52:09 -0800 Subject: [PATCH 96/97] Updated to latest version of Microsoft Test Framework This only affects the unit tests, and has no downstream impact on implementers. Ensured all unit tests continue to operate correctly. --- .../OnTopic.Data.Transfer.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index 099b7dc..1c97e5a 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -14,8 +14,8 @@ - - + + From 1afe071a2ab5f64bcd52de70386ce11175c426b5 Mon Sep 17 00:00:00 2001 From: JeremyCaney Date: Tue, 2 Mar 2021 16:31:08 -0800 Subject: [PATCH 97/97] Updated to OnTopic 5.0.0 RTM Updated to the final release version of OnTopic 5.0.0. --- OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj | 2 +- OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj index 1c97e5a..fc7dacd 100644 --- a/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj +++ b/OnTopic.Data.Transfer.Tests/OnTopic.Data.Transfer.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj index 1b55882..9f7b09f 100644 --- a/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj +++ b/OnTopic.Data.Transfer/OnTopic.Data.Transfer.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - +