Skip to content

Commit f2cb109

Browse files
committed
Introduced new GetTopicByUniqueKey() extension method
Originally, the `OnTopic.Querying` class was focused on querying the current `Topic` and its descendants. Increasingly, however, we're identifying needs to work with the entire in-memory topic graph starting from an arbitrary position within the tree. This is particularly useful when it's difficult or inappropriate to inject an `ITopicRepository` such that we have access from the root. As part of this, we're introducing the new `GetTopicByUniqueKey()` extension method, which allows any topic to be retrieved based on its absolute (or root relative) path in the topic graph, starting from any arbitrary position. This is useful for e.g. populating strongly typed topic references for `Relationships`, `IncomingRelationships`, and `DerivedTopic` , as well as metadata references for e.g. the associated `ContentTypeDescriptor`, even though it may not otherwise be appropriate to maintain a dependency on `ITopicRepository`. Includes unit tests for validating the functionality.
1 parent 0bddfb0 commit f2cb109

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

OnTopic.Tests/TopicQueryingTest.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,47 @@ public void GetRootTopic_ReturnsRootTopic() {
6464

6565
}
6666

67+
/*==========================================================================================================================
68+
| TEST: GET BY UNIQUE KEY: VALID KEY: RETURNS TOPIC
69+
\-------------------------------------------------------------------------------------------------------------------------*/
70+
/// <summary>
71+
/// Given a deeply nested <see cref="Topic"/>, returns the expected <see cref="Topic"/>.
72+
/// </summary>
73+
[TestMethod]
74+
public void GetByUniqueKey_ValidKey_ReturnsTopic() {
75+
76+
var parentTopic = TopicFactory.Create("ParentTopic", "Page", 1);
77+
var childTopic = TopicFactory.Create("ChildTopic", "Page", 5, parentTopic);
78+
var grandChildTopic = TopicFactory.Create("GrandChildTopic", "Page", 20, childTopic);
79+
var greatGrandChildTopic1 = TopicFactory.Create("GreatGrandChildTopic1", "Page", 7, grandChildTopic);
80+
var greatGrandChildTopic2 = TopicFactory.Create("GreatGrandChildTopic2", "Page", 7, grandChildTopic);
81+
82+
var foundTopic = greatGrandChildTopic1.GetByUniqueKey("ParentTopic:ChildTopic:GrandChildTopic:GreatGrandChildTopic2");
83+
84+
Assert.ReferenceEquals(greatGrandChildTopic2, foundTopic);
85+
86+
}
87+
88+
/*==========================================================================================================================
89+
| TEST: GET BY UNIQUE KEY: INVALID KEY: RETURNS NULL
90+
\-------------------------------------------------------------------------------------------------------------------------*/
91+
/// <summary>
92+
/// Given an invalid <c>UniqueKey</c>, the <see cref="TopicExtensions.GetByUniqueKey(Topic, String)"/> returns
93+
/// <c>null</c>.
94+
/// </summary>
95+
[TestMethod]
96+
public void GetByUniqueKey_InvalidKey_ReturnsNull() {
97+
98+
var parentTopic = TopicFactory.Create("ParentTopic", "Page", 1);
99+
var childTopic = TopicFactory.Create("ChildTopic", "Page", 5, parentTopic);
100+
var grandChildTopic = TopicFactory.Create("GrandChildTopic", "Page", 20, childTopic);
101+
var greatGrandChildTopic = TopicFactory.Create("GreatGrandChildTopic", "Page", 7, grandChildTopic);
102+
103+
var foundTopic = greatGrandChildTopic.GetByUniqueKey("ParentTopic:ChildTopic:GrandChildTopic:GreatGrandChildTopic2");
104+
105+
Assert.IsNull(foundTopic);
106+
107+
}
108+
67109
} //Class
68110
} //Namespace

OnTopic/Querying/TopicExtensions.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,5 +171,49 @@ public static Topic GetRootTopic(this Topic topic) {
171171

172172
}
173173

174+
/*==========================================================================================================================
175+
| METHOD: GET BY UNIQUE KEY
176+
\-------------------------------------------------------------------------------------------------------------------------*/
177+
/// <summary>
178+
/// Retrieves a <see cref="Topic"/> with the specified <paramref name="uniqueKey"/>, if available.
179+
/// </summary>
180+
/// <param name="topic">The instance of the <see cref="Topic"/> to operate against; populated automatically by .NET.</param>
181+
/// <param name="uniqueKey">The <see cref="Topic.GetUniqueKey()"/> of the <see cref="Topic"/> to return.</param>
182+
/// <returns>A <see cref="Topic"/> with the specified <paramref name="uniqueKey"/>, if found.</returns>
183+
public static Topic? GetByUniqueKey(this Topic topic, string uniqueKey) {
184+
185+
/*------------------------------------------------------------------------------------------------------------------------
186+
| Validate contracts
187+
\-----------------------------------------------------------------------------------------------------------------------*/
188+
Contract.Requires(topic, "The topic parameter must be specified.");
189+
Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(uniqueKey), "The unique key must be specified.");
190+
191+
/*------------------------------------------------------------------------------------------------------------------------
192+
| Find lowest common root
193+
\-----------------------------------------------------------------------------------------------------------------------*/
194+
var currentTopic = (Topic?)topic.GetRootTopic();
195+
196+
/*------------------------------------------------------------------------------------------------------------------------
197+
| Process keys
198+
\-----------------------------------------------------------------------------------------------------------------------*/
199+
if (uniqueKey.StartsWith(currentTopic!.Key + ":", StringComparison.InvariantCultureIgnoreCase)) {
200+
uniqueKey = uniqueKey.Substring(currentTopic!.Key.Length + 1);
201+
}
202+
var keys = uniqueKey.Split(new char[] {':'}, StringSplitOptions.RemoveEmptyEntries);
203+
204+
/*------------------------------------------------------------------------------------------------------------------------
205+
| Navigate to the specific path
206+
\-----------------------------------------------------------------------------------------------------------------------*/
207+
foreach (var key in keys) {
208+
currentTopic = currentTopic?.Children?.GetTopic(key);
209+
}
210+
211+
/*------------------------------------------------------------------------------------------------------------------------
212+
| Return topic
213+
\-----------------------------------------------------------------------------------------------------------------------*/
214+
return currentTopic;
215+
216+
}
217+
174218
} //Class
175219
} //Namespace

0 commit comments

Comments
 (0)