Skip to content

Commit 18f676b

Browse files
committed
Merge branch 'feature/empty-repository-support' into develop
This update builds off of OnTopic Library 4.3.0 and the OnTopic Data Transfer 1.2.0 library in order to provide support for connecting to and bootstrapping empty repositories. Notably, this includes the ability to dynamically add or remove content type and attribute descriptors. Not only does this allow some configuration changes made via the **OnTopic Editor** to be immediately available to the interface, it also allows new or reference configurations to be imported using the **OnTopic Data Transfer** library integration. Finally, this update takes advantage of many performance and reliability updates to the underlying services, particularly regarding recursive saves (as done during `Import()`). This includes the following updates: - The editor will now tolerate binding to topics with missing content type descriptors (06f065b, 34cdeec). This allows it to display a default `Root` topic even if the `Container` content type hasn't yet been created. Of course, no editor form will be present in these scenarios. - Allow creation of the root topic on `Import()` (c51a141). Previously, the editor assumed that the root topic already existed. - Ensured implicit topic references (i.e., attributes ending in 'Id' and pointing to a `Topic.Id`) can be resolved based on a single `Import()` by rearranging the `Import()` and `Save()` logic (a01e92e). - Changed the implicit default root from `Web` to `Root`; this not only allows support for an empty database (where `Web` won't yet exist), but also fixes issues when querying the `/JSON` service where it would only query the `Web` branch, thus making it impossible to reference e.g. the `Configuration` branch with a `QueryableTopicListAttribute` or `TopicReferenceAttribute` (1069b34). - Updated to use newly created `Topic.IsNew` property for detecting if topic references are valid (9a79cc8). - Updated various dependencies, including client-side dependencies (4b996cc).
2 parents c696e7a + 4b996cc commit 18f676b

8 files changed

Lines changed: 250 additions & 94 deletions

File tree

OnTopic.Editor.AspNetCore.Host/OnTopic.Editor.AspNetCore.Host.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="OnTopic" Version="4.2.0" />
11-
<PackageReference Include="OnTopic.ViewModels" Version="4.2.0" />
12-
<PackageReference Include="OnTopic.AspNetCore.Mvc" Version="4.2.0" />
13-
<PackageReference Include="OnTopic.Data.Caching" Version="4.2.0" />
14-
<PackageReference Include="OnTopic.Data.Sql" Version="4.2.0" />
10+
<PackageReference Include="OnTopic" Version="4.3.0" />
11+
<PackageReference Include="OnTopic.ViewModels" Version="4.3.0" />
12+
<PackageReference Include="OnTopic.AspNetCore.Mvc" Version="4.3.0" />
13+
<PackageReference Include="OnTopic.Data.Caching" Version="4.3.0" />
14+
<PackageReference Include="OnTopic.Data.Sql" Version="4.3.0" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

OnTopic.Editor.AspNetCore/Components/ContentTypeListViewComponent.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ public IViewComponentResult Invoke(
9090
);
9191
}
9292

93+
/*------------------------------------------------------------------------------------------------------------------------
94+
| Get content type
95+
>-------------------------------------------------------------------------------------------------------------------------
96+
| If the database is uninitialized, the content type won't be found. In that case, return an empty view, which will
97+
| effectively hide the component.
98+
\-----------------------------------------------------------------------------------------------------------------------*/
99+
var contentTypes = _topicRepository.GetContentTypeDescriptors();
100+
var actualTopic = _topicRepository.Load(currentTopic.Id);
101+
var actualContentType = contentTypes.GetTopic(currentTopic.ContentType);
102+
103+
if (actualContentType == null) {
104+
return View(viewModel);
105+
}
106+
93107
/*------------------------------------------------------------------------------------------------------------------------
94108
| Get permitted content types for container
95109
>-------------------------------------------------------------------------------------------------------------------------
@@ -98,10 +112,6 @@ public IViewComponentResult Invoke(
98112
| to organize a specific type of content. For example, a Container called "Forms" might be used exclusively to organized
99113
| Form topics.
100114
\-----------------------------------------------------------------------------------------------------------------------*/
101-
var contentTypes = _topicRepository.GetContentTypeDescriptors();
102-
var actualTopic = _topicRepository.Load(currentTopic.Id);
103-
var actualContentType = contentTypes.GetTopic(currentTopic.ContentType);
104-
105115
if (actualContentType.Key.Equals("Container", StringComparison.InvariantCultureIgnoreCase)) {
106116
viewModel.TopicList.AddRange(
107117
actualTopic

OnTopic.Editor.AspNetCore/Controllers/EditorController.cs

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,9 @@ protected Topic CurrentTopic {
104104
/// <returns>The Content Type associated with the current request.</returns>
105105
protected ContentTypeDescriptor GetContentType(string contentType) => _topicRepository
106106
.GetContentTypeDescriptors()
107-
.Where(t => t.Key.Equals(contentType))
108-
.First();
107+
.Where(t => t.Key.Equals(contentType?? ""))
108+
.FirstOrDefault()??
109+
(ContentTypeDescriptor)TopicFactory.Create(contentType, "ContentTypeDescriptor");
109110

110111
/*==========================================================================================================================
111112
| GET EDITOR VIEW MODEL
@@ -685,7 +686,18 @@ public async Task<IActionResult> Import(IFormFile jsonFile, [Bind(Prefix = "Impo
685686
/*------------------------------------------------------------------------------------------------------------------------
686687
| IDENTIFY TARGET TOPIC
687688
\-----------------------------------------------------------------------------------------------------------------------*/
688-
var target = TopicRepository.Load(topicData.UniqueKey);
689+
var uniqueKey = topicData.UniqueKey;
690+
var target = TopicRepository.Load(uniqueKey);
691+
692+
//Create target if it doesn't exist
693+
if (target == null) {
694+
var parentKey = uniqueKey.Substring(0, uniqueKey.LastIndexOf(":", StringComparison.InvariantCulture));
695+
var parent = TopicRepository.Load(parentKey);
696+
697+
if (parent != null) {
698+
target = TopicFactory.Create(topicData.Key, topicData.ContentType, parent);
699+
}
700+
}
689701

690702
if (target == null) {
691703
ModelState.AddModelError(
@@ -698,17 +710,12 @@ public async Task<IActionResult> Import(IFormFile jsonFile, [Bind(Prefix = "Impo
698710
/*------------------------------------------------------------------------------------------------------------------------
699711
| INDEX TOPICS IN SCOPE
700712
\-----------------------------------------------------------------------------------------------------------------------*/
701-
var topics = target.FindAll(t => t.Id >= 0).ToList();
713+
var topics = target.FindAll(t => !t.IsNew).ToList();
702714

703715
/*------------------------------------------------------------------------------------------------------------------------
704-
| IMPORT INTO TOPIC GRAPH
705-
>-------------------------------------------------------------------------------------------------------------------------
706-
| ### HACK JJC20200123: Because the graph may include references to objects that won't be created until later in the
707-
| import, we need to import the topic data twice. The first will ensure all objects are created. The second will ensure
708-
| all references are restored.
716+
| INITIAL IMPORT
709717
\-----------------------------------------------------------------------------------------------------------------------*/
710718
target.Import(topicData, options);
711-
target.Import(topicData, options);
712719

713720
/*------------------------------------------------------------------------------------------------------------------------
714721
| DELETE UNMATCHED TOPICS
@@ -718,21 +725,41 @@ public async Task<IActionResult> Import(IFormFile jsonFile, [Bind(Prefix = "Impo
718725
| removed topics during a recursive save and, therefore, the deletions aren't persited to the database. To mitigate this,
719726
| we evaluate the topic graph after the save, and then delete any orphans.
720727
\-----------------------------------------------------------------------------------------------------------------------*/
721-
var unmatchedTopics = topics.Except(target.FindAll(t => t.Id >= 0));
728+
var unmatchedTopics = topics.Except(target.FindAll(t => !t.IsNew));
722729

723730
foreach (var unmatchedTopic in unmatchedTopics) {
724731
TopicRepository.Delete(unmatchedTopic);
725732
}
726733

727734
/*------------------------------------------------------------------------------------------------------------------------
728-
| SAVE
735+
| SET SAVE SCOPE
736+
>-------------------------------------------------------------------------------------------------------------------------
737+
| ### HACK JJC20200519: If the parent hasn't been saved, then it should be set to the target to be saved. This should only
738+
| happen when working with an empty database, in which case the Root topic will be autogenerated by TopicRepositoryBase.
739+
| Otherwise, Save() will generate an error since the parent ID won't be found.
740+
\-----------------------------------------------------------------------------------------------------------------------*/
741+
var saveRoot = target;
742+
if (target.Parent.IsNew) {
743+
saveRoot = target.Parent;
744+
}
745+
746+
/*------------------------------------------------------------------------------------------------------------------------
747+
| INITIAL SAVE
748+
\-----------------------------------------------------------------------------------------------------------------------*/
749+
TopicRepository.Save(saveRoot, topicData.Children.Count > 0);
750+
751+
/*------------------------------------------------------------------------------------------------------------------------
752+
| RESOLVE TOPIC REFERENCES
729753
>-------------------------------------------------------------------------------------------------------------------------
730-
| ### HACK JJC20200123: Because the graph may include references to objects that won't be saved until later in the import,
731-
| we need to save the topic tree twice. The first will ensure all objects have an TopicId. The second will ensure all
732-
| saved references refer to the correct TopicId.
754+
| ### HACK JJC20200522: When the first Import() is done, topic references may be pointing to objects that haven't yet been
755+
| imported (i.e., they occur later in the graph traversal). Likewise, when the first Save() is done, those same topic
756+
| references have not yet been saved, and so they can't be resolved to a valid TopicID. To mitigate this, we do a second
757+
| Import() followed by a second Save(). This shouldn't impact the items that have already been imported, but it will
758+
| ensure that topic references are resolved. This includes relationships, derived topics, and topic pointers from
759+
| attribute types such as TokenizedTopicList, TopicList, and TopicReference.
733760
\-----------------------------------------------------------------------------------------------------------------------*/
734-
TopicRepository.Save(target, topicData.Children.Count > 0);
735-
TopicRepository.Save(target, topicData.Children.Count > 0);
761+
target.Import(topicData, options);
762+
TopicRepository.Save(saveRoot, topicData.Children.Count > 0);
736763

737764
/*------------------------------------------------------------------------------------------------------------------------
738765
| RETURN JSON

OnTopic.Editor.AspNetCore/EditorServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ this IEndpointRouteBuilder routes
6060
name: "TopicEditor",
6161
areaName: "Editor",
6262
pattern: "OnTopic/{action}/{**path}",
63-
defaults: new { controller = "Editor", action="Edit", path = "Root/Web/" }
63+
defaults: new { controller = "Editor", action="Edit", path = "Root" }
6464
);
6565

6666
} // Class

OnTopic.Editor.AspNetCore/OnTopic.Editor.AspNetCore.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@
2626

2727
<ItemGroup>
2828
<FrameworkReference Include="Microsoft.AspNetCore.App" />
29-
<PackageReference Include="GitVersionTask" Version="5.2.4">
29+
<PackageReference Include="GitVersionTask" Version="5.3.5">
3030
<PrivateAssets>all</PrivateAssets>
3131
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3232
</PackageReference>
33-
<PackageReference Include="OnTopic" Version="4.2.0" />
34-
<PackageReference Include="OnTopic.Data.Transfer" Version="1.1.0" />
35-
<PackageReference Include="OnTopic.ViewModels" Version="4.2.0" />
36-
<PackageReference Include="OnTopic.AspNetCore.Mvc" Version="4.2.0" />
33+
<PackageReference Include="OnTopic" Version="4.3.0" />
34+
<PackageReference Include="OnTopic.Data.Transfer" Version="1.2.0" />
35+
<PackageReference Include="OnTopic.ViewModels" Version="4.3.0" />
36+
<PackageReference Include="OnTopic.AspNetCore.Mvc" Version="4.3.0" />
3737
</ItemGroup>
3838

3939
<ItemGroup>

0 commit comments

Comments
 (0)