Skip to content

Commit 2cfbc52

Browse files
committed
Merge commit 'feature/TopicReferences' into develop
Introduced support for `Topic.References` via a `TopicData.References` property (aac6394), which is populated via the `Export()` extension method (f156c25), and read via the `Import()` extension method (6d5938b), while honoring the new `DeleteUnmatchedReferences` import option (79f64c1). Finally, unified the handling of unresolved associations to work with _both_ `Topic.Relationships`—as it was originally intended for—as well as `Topic.References` (6d22172).
2 parents 4705b29 + 6d22172 commit 2cfbc52

File tree

4 files changed

+102
-15
lines changed

4 files changed

+102
-15
lines changed

OnTopic.Data.Transfer.Tests/SerializationTest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public void Serialize_TopicData_ReturnsExpectedResults() {
4141
$"\"ContentType\":\"{topicData.ContentType}\"," +
4242
$"\"Attributes\":[]," +
4343
$"\"Relationships\":[]," +
44+
$"\"References\":[]," +
4445
$"\"Children\":[]" +
4546
$"}}";
4647

@@ -149,13 +150,15 @@ public void Serialize_TopicGraph_ReturnsExpectedResults() {
149150
$"\"Relationships\":[\"Root:Web\"]" +
150151
$"}}" +
151152
$"]," +
153+
$"\"References\":[]," +
152154
$"\"Children\":[" +
153155
$"{{" +
154156
$"\"Key\":\"{childTopicData.Key}\"," +
155157
$"\"UniqueKey\":\"{childTopicData.UniqueKey}\"," +
156158
$"\"ContentType\":\"{childTopicData.ContentType}\"," +
157159
$"\"Attributes\":[]," +
158160
$"\"Relationships\":[]," +
161+
$"\"References\":[]," +
159162
$"\"Children\":[]" +
160163
$"}}" +
161164
$"]" +

OnTopic.Data.Transfer/Interchange/ImportOptions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class ImportOptions {
2828
\-------------------------------------------------------------------------------------------------------------------------*/
2929
private bool? _deleteUnmatchedAttributes;
3030
private bool? _deleteUnmatchedRelationships;
31+
private bool? _deleteUnmatchedReferences;
3132
private bool? _deleteUnmatchedChildren;
3233
private bool? _deleteUnmatchedNestedTopics;
3334
private bool? _overwriteContentType;
@@ -77,6 +78,21 @@ public bool DeleteUnmatchedRelationships {
7778
set => _deleteUnmatchedRelationships = value;
7879
}
7980

81+
/*==========================================================================================================================
82+
| DELETE UNMATCHED REFERENCES
83+
\-------------------------------------------------------------------------------------------------------------------------*/
84+
/// <summary>
85+
/// Delete references in the target database which don't exist in the source <see cref="TopicData"/> graph.
86+
/// </summary>
87+
/// <remarks>
88+
/// This will delete any existing references that aren't <i>also</i> represented in the source <see cref="TopicData"/>
89+
/// graph.
90+
/// </remarks>
91+
public bool DeleteUnmatchedReferences {
92+
get => _deleteUnmatchedReferences?? Strategy is ImportStrategy.Replace;
93+
set => _deleteUnmatchedReferences = value;
94+
}
95+
8096
/*==========================================================================================================================
8197
| DELETE UNMATCHED CHILDREN
8298
\-------------------------------------------------------------------------------------------------------------------------*/

OnTopic.Data.Transfer/Interchange/TopicExtensions.cs

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,25 @@ public static TopicData Export(this Topic topic, [NotNull]ExportOptions? options
100100
);
101101
}
102102

103+
/*------------------------------------------------------------------------------------------------------------------------
104+
| Set topic references
105+
\-----------------------------------------------------------------------------------------------------------------------*/
106+
foreach (var reference in topic.References) {
107+
if (
108+
!options.IncludeExternalReferences &&
109+
!(reference.Value?.GetUniqueKey().StartsWith(options.ExportScope, StringComparison.InvariantCultureIgnoreCase)?? true)
110+
) {
111+
continue;
112+
}
113+
topicData.References.Add(
114+
new() {
115+
Key = reference.Key,
116+
Value = reference.Value?.GetUniqueKey(),
117+
LastModified = reference.LastModified
118+
}
119+
);
120+
}
121+
103122
/*------------------------------------------------------------------------------------------------------------------------
104123
| Set relationships
105124
\-----------------------------------------------------------------------------------------------------------------------*/
@@ -165,24 +184,25 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import
165184
/*------------------------------------------------------------------------------------------------------------------------
166185
| Establish cache
167186
\-----------------------------------------------------------------------------------------------------------------------*/
168-
var unresolvedRelationships = new List<Tuple<Topic, string, string>>();
187+
var unresolvedAssociations = new List<Tuple<Topic, bool, string, string>>();
169188

170189
/*------------------------------------------------------------------------------------------------------------------------
171190
| Handle first pass
172191
\-----------------------------------------------------------------------------------------------------------------------*/
173-
topic.Import(topicData, options, unresolvedRelationships);
192+
topic.Import(topicData, options, unresolvedAssociations);
174193

175194
/*------------------------------------------------------------------------------------------------------------------------
176-
| Attempt to resolve outstanding relationships
195+
| Attempt to resolve outstanding assocations
177196
\-----------------------------------------------------------------------------------------------------------------------*/
178-
foreach (var relationship in unresolvedRelationships) {
197+
foreach (var relationship in unresolvedAssociations) {
179198

180-
//Attempt to find the target relationship
199+
//Attempt to find the target association
181200
var source = relationship.Item1;
182-
var target = topic.GetByUniqueKey(relationship.Item3);
183-
var key = relationship.Item2;
201+
var isRelationship = relationship.Item2;
202+
var key = relationship.Item3;
203+
var target = topic.GetByUniqueKey(relationship.Item4);
184204

185-
//If the relationship STILL can't be resolved, skip it
205+
//If the association STILL can't be resolved, skip it
186206
if (target is null) {
187207
continue;
188208
}
@@ -193,10 +213,15 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import
193213
}
194214

195215
//Wire up relationships
196-
else {
216+
else if (isRelationship) {
197217
source.Relationships.SetValue(key, target);
198218
}
199219

220+
//Wire up topic references
221+
else {
222+
source.References.SetValue(key, target);
223+
}
224+
200225
}
201226

202227
}
@@ -219,13 +244,15 @@ public static void Import(this Topic topic, TopicData topicData, [NotNull]Import
219244
/// this via <see cref="Import"/> directly.
220245
/// </para>
221246
/// </remarks>
222-
/// <param name="topic">The source <see cref="Topic"/> to operate off of.</param>
247+
/// <param name="topic">The target <see cref="Topic"/> to write data to.</param>
248+
/// <param name="topicData">The source <see cref="TopicData"/> to import data from.</param>
223249
/// <param name="options">An optional <see cref="ImportOptions"/> object to specify import settings.</param>
250+
/// <param name="unresolvedAssociations">A list of associations that could not be resolved on the first </param>
224251
private static void Import(
225252
this Topic topic,
226253
TopicData topicData,
227254
[NotNull]ImportOptions? options,
228-
List<Tuple<Topic, string, string>> unresolvedRelationships
255+
List<Tuple<Topic, bool, string, string>> unresolvedAssociations
229256
) {
230257

231258
/*------------------------------------------------------------------------------------------------------------------------
@@ -266,7 +293,7 @@ List<Tuple<Topic, string, string>> unresolvedRelationships
266293
topic.BaseTopic = target;
267294
}
268295
else {
269-
unresolvedRelationships.Add(new(topic, "DerivedTopic", topicData.BaseTopicKey));
296+
unresolvedAssociations.Add(new(topic, false, "DerivedTopic", topicData.BaseTopicKey));
270297
}
271298
}
272299

@@ -349,11 +376,44 @@ List<Tuple<Topic, string, string>> unresolvedRelationships
349376
topic.Relationships.SetValue(relationship.Key, relatedTopic);
350377
}
351378
else {
352-
unresolvedRelationships.Add(new(topic, relationship.Key!, relatedTopicKey));
379+
unresolvedAssociations.Add(new(topic, true, relationship.Key!, relatedTopicKey));
353380
}
354381
}
355382
}
356383

384+
385+
/*------------------------------------------------------------------------------------------------------------------------
386+
| Set topic references
387+
\-----------------------------------------------------------------------------------------------------------------------*/
388+
389+
//First delete any unmatched records, if appropriate
390+
if (options.DeleteUnmatchedReferences) {
391+
var unmatchedReferences = topic.References.Where(a1 =>
392+
!topicData.Attributes.Any(a2 => a1.Key == a2.Key)
393+
);
394+
foreach (var reference in unmatchedReferences.ToArray()) {
395+
topic.References.Remove(reference);
396+
};
397+
}
398+
399+
//Update records based on the source collection
400+
foreach (var reference in topicData.References) {
401+
if (useCustomMergeRules(reference)) continue;
402+
var matchedReference = topic.References.FirstOrDefault(a => a.Key == reference.Key);
403+
if (matchedReference is not null && isStrategy(ImportStrategy.Add)) continue;
404+
if (matchedReference?.LastModified >= reference.LastModified && isStrategy(ImportStrategy.Merge)) continue;
405+
var referencedTopic = topic.GetByUniqueKey(reference.Key);
406+
if (reference.Value is null || referencedTopic != null) {
407+
topic.References.SetValue(
408+
reference.Key,
409+
referencedTopic
410+
);
411+
}
412+
else {
413+
unresolvedAssociations.Add(new(topic, false, reference.Key, reference.Value));
414+
}
415+
}
416+
357417
/*------------------------------------------------------------------------------------------------------------------------
358418
| Recurse over children
359419
\-----------------------------------------------------------------------------------------------------------------------*/
@@ -376,7 +436,7 @@ topic.ContentType is not "List" && options.DeleteUnmatchedChildren
376436
if (childTopic is null) {
377437
childTopic = TopicFactory.Create(childTopicData.Key, childTopicData.ContentType, topic);
378438
}
379-
childTopic.Import(childTopicData, options, unresolvedRelationships);
439+
childTopic.Import(childTopicData, options, unresolvedAssociations);
380440
}
381441

382442
/*------------------------------------------------------------------------------------------------------------------------

OnTopic.Data.Transfer/TopicData.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Collections.ObjectModel;
88
using System.Diagnostics.CodeAnalysis;
99
using System.Text.Json;
10-
using System.Text.Json.Serialization;
1110

1211
namespace OnTopic.Data.Transfer {
1312

@@ -118,6 +117,15 @@ public string? BaseTopicKey {
118117
/// </summary>
119118
public RelationshipDataCollection Relationships { get; set; } = new();
120119

120+
/*==========================================================================================================================
121+
| TOPIC REFERENCES
122+
\-------------------------------------------------------------------------------------------------------------------------*/
123+
/// <summary>
124+
/// Provides a collection of <see cref="AttributeData"/> representing the topic references from the associated <see
125+
/// cref="Topic"/> object.
126+
/// </summary>
127+
public AttributeDataCollection References { get; set; } = new();
128+
121129
/*==========================================================================================================================
122130
| CHILDREN
123131
\-------------------------------------------------------------------------------------------------------------------------*/

0 commit comments

Comments
 (0)