Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle navigation updates in cache refeshers #17161

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 105 additions & 2 deletions src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
private readonly IDomainService _domainService;
private readonly IDocumentUrlService _documentUrlService;
private readonly IDocumentNavigationQueryService _documentNavigationQueryService;
private readonly IDocumentNavigationManagementService _documentNavigationManagementService;
private readonly IContentService _contentService;
private readonly IIdKeyMap _idKeyMap;
private readonly IPublishedSnapshotService _publishedSnapshotService;

Expand All @@ -40,7 +42,9 @@
eventAggregator,
factory,
StaticServiceProvider.Instance.GetRequiredService<IDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>()
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationManagementService>(),
StaticServiceProvider.Instance.GetRequiredService<IContentService>()
)
{

Expand All @@ -55,14 +59,18 @@
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory,
IDocumentUrlService documentUrlService,
IDocumentNavigationQueryService documentNavigationQueryService)
IDocumentNavigationQueryService documentNavigationQueryService,
IDocumentNavigationManagementService documentNavigationManagementService,
IContentService contentService)
: base(appCaches, serializer, eventAggregator, factory)
{
_publishedSnapshotService = publishedSnapshotService;
_idKeyMap = idKeyMap;
_domainService = domainService;
_documentUrlService = documentUrlService;
_documentNavigationQueryService = documentNavigationQueryService;
_documentNavigationManagementService = documentNavigationManagementService;
_contentService = contentService;

Check notice on line 73 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

ℹ Getting worse: Constructor Over-Injection

ContentCacheRefresher increases from 9 to 11 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
}

#region Indirect
Expand Down Expand Up @@ -126,6 +134,7 @@

HandleRouting(payload);

HandleNavigation(payload);
_idKeyMap.ClearCache(payload.Id);
if (payload.Key.HasValue)
{
Expand Down Expand Up @@ -172,6 +181,100 @@
base.Refresh(payloads);
}

private void HandleNavigation(JsonPayload payload)
{
if (payload.Key is null)
{
return;
}

if(payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
{
_documentNavigationManagementService.MoveToBin(payload.Key.Value);
_documentNavigationManagementService.RemoveFromBin(payload.Key.Value);
}
if(payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
{
_documentNavigationManagementService.RebuildAsync();
_documentNavigationManagementService.RebuildBinAsync();
}
if(payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode))
{
IContent? content = _contentService.GetById(payload.Id);

if (content is null)
{
return;
}

HandleNavigationForSingleContent(content);
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
{
IContent? content = _contentService.GetById(payload.Id);

if (content is null)
{
return;
}

IEnumerable<IContent> descendants = _contentService.GetPagedDescendants(content.Id, 0, int.MaxValue, out _);
foreach (IContent descendant in content.Yield().Concat(descendants))
{
HandleNavigationForSingleContent(descendant);
}
}
}

Check warning on line 228 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Complex Method

HandleNavigation has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 228 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Bumpy Road Ahead

HandleNavigation has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

private void HandleNavigationForSingleContent(IContent content)
{

//First creation
if(ExistsInNavigation(content.Key) is false && ExistsInNavigationBin(content.Key) is false)
{
_documentNavigationManagementService.Add(content.Key, GetParentKey(content));
if (content.Trashed)
{
// If created as trashed, move to bin
_documentNavigationManagementService.MoveToBin(content.Key);
}
}else if(ExistsInNavigation(content.Key) is true && ExistsInNavigationBin(content.Key) is false)
{
if (content.Trashed)
{
// It must have been trashed
_documentNavigationManagementService.MoveToBin(content.Key);
}
else
{
// it most have been saved. Check if parent is different
if (_documentNavigationQueryService.TryGetParentKey(content.Key, out var oldParentKey))
{
Guid? newParentKey = GetParentKey(content);
if (oldParentKey != newParentKey)
{
_documentNavigationManagementService.Move(content.Key, newParentKey);
}
}
}
}
else if (ExistsInNavigation(content.Key) is false && ExistsInNavigationBin(content.Key) is true)
{
if (content.Trashed is false)
{
// It must have been restored
_documentNavigationManagementService.RestoreFromBin(content.Key, GetParentKey(content));
}
}
}

Check warning on line 270 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Complex Method

HandleNavigationForSingleContent has a cyclomatic complexity of 12, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 270 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Bumpy Road Ahead

HandleNavigationForSingleContent has 4 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

Check warning on line 270 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Deep, Nested Complexity

HandleNavigationForSingleContent has a nested complexity depth of 5, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.

private Guid? GetParentKey(IContent content) => (content.ParentId == -1) ? null : _idKeyMap.GetKeyForId(content.ParentId, UmbracoObjectTypes.Document).Result;

private bool ExistsInNavigation(Guid contentKey) => _documentNavigationQueryService.TryGetParentKey(contentKey, out _);

private bool ExistsInNavigationBin(Guid contentKey) => _documentNavigationQueryService.TryGetParentKeyInBin(contentKey, out _);

private void HandleRouting(JsonPayload payload)
{
if(payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
Expand Down
42 changes: 0 additions & 42 deletions src/Umbraco.Core/Factories/NavigationFactory.cs

This file was deleted.

114 changes: 0 additions & 114 deletions src/Umbraco.Core/Services/ContentService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection;

Check notice on line 1 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ Getting better: Lines of Code in a Single File

The lines of code decreases from 2302 to 2214, improve code health by reducing it to 1000. The number of Lines of Code in a single file. More Lines of Code lowers the code health.

Check notice on line 1 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ Getting better: Number of Functions in a Single Module

The number of functions decreases from 109 to 108, threshold = 75. This file contains too many functions. Beyond a certain threshold, more functions lower the code health.

Check notice on line 1 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ Getting better: Overall Code Complexity

The mean cyclomatic complexity decreases from 4.21 to 4.16, threshold = 4. This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.

Check notice on line 1 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

ℹ Getting worse: Primitive Obsession

The ratio of primitive types in function arguments increases from 50.00% to 50.16%, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
Expand Down Expand Up @@ -35,7 +35,6 @@
private readonly IShortStringHelper _shortStringHelper;
private readonly ICultureImpactFactory _cultureImpactFactory;
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly IDocumentNavigationManagementService _documentNavigationManagementService;
private readonly PropertyEditorCollection _propertyEditorCollection;
private IQuery<IContent>? _queryNotTrashed;

Expand All @@ -54,22 +53,20 @@
Lazy<IPropertyValidationService> propertyValidationService,
IShortStringHelper shortStringHelper,
ICultureImpactFactory cultureImpactFactory,
IUserIdKeyResolver userIdKeyResolver,
IDocumentNavigationManagementService documentNavigationManagementService,
PropertyEditorCollection propertyEditorCollection)
: base(provider, loggerFactory, eventMessagesFactory)
{
_documentRepository = documentRepository;
_entityRepository = entityRepository;
_auditRepository = auditRepository;
_contentTypeRepository = contentTypeRepository;
_documentBlueprintRepository = documentBlueprintRepository;
_languageRepository = languageRepository;
_propertyValidationService = propertyValidationService;
_shortStringHelper = shortStringHelper;
_cultureImpactFactory = cultureImpactFactory;
_userIdKeyResolver = userIdKeyResolver;

Check notice on line 69 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ Getting better: Constructor Over-Injection

ContentService decreases from 15 to 14 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
_documentNavigationManagementService = documentNavigationManagementService;
_propertyEditorCollection = propertyEditorCollection;
_logger = loggerFactory.CreateLogger<ContentService>();
}
Expand Down Expand Up @@ -103,7 +100,6 @@
shortStringHelper,
cultureImpactFactory,
userIdKeyResolver,
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationManagementService>(),
StaticServiceProvider.Instance.GetRequiredService<PropertyEditorCollection>())
{
}
Expand Down Expand Up @@ -136,7 +132,6 @@
shortStringHelper,
cultureImpactFactory,
StaticServiceProvider.Instance.GetRequiredService<IUserIdKeyResolver>(),
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationManagementService>(),
StaticServiceProvider.Instance.GetRequiredService<PropertyEditorCollection>())
{
}
Expand Down Expand Up @@ -1062,19 +1057,7 @@
// in this particular case, determining which cultures have changed works with the above with names since it will
// have always changed if it's been saved in the back office but that's not really fail safe.
_documentRepository.Save(content);

Check notice on line 1060 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ Getting better: Complex Method

Save decreases in cyclomatic complexity from 13 to 12, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
// Updates in-memory navigation structure - we only handle new items, other updates are not a concern
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.Save-with-contentSchedule",
() =>
{
_documentNavigationManagementService.Add(content.Key, GetParent(content)?.Key);
if (content.Trashed)
{
_documentNavigationManagementService.MoveToBin(content.Key);
}
});

if (contentSchedule != null)
{
_documentRepository.PersistContentSchedule(content, contentSchedule);
Expand Down Expand Up @@ -1138,18 +1121,6 @@
content.WriterId = userId;

_documentRepository.Save(content);

// Updates in-memory navigation structure - we only handle new items, other updates are not a concern
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.Save",
() =>
{
_documentNavigationManagementService.Add(content.Key, GetParent(content)?.Key);
if (content.Trashed)
{
_documentNavigationManagementService.MoveToBin(content.Key);
}
});
}

scope.Notifications.Publish(
Expand Down Expand Up @@ -2339,26 +2310,6 @@
}

DoDelete(content);

if (content.Trashed)
{
// Updates in-memory navigation structure for recycle bin items
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.DeleteLocked-trashed",
() => _documentNavigationManagementService.RemoveFromBin(content.Key));
}
else
{
// Updates in-memory navigation structure for both documents and recycle bin items
// as the item needs to be deleted whether it is in the recycle bin or not
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.DeleteLocked",
() =>
{
_documentNavigationManagementService.MoveToBin(content.Key);
_documentNavigationManagementService.RemoveFromBin(content.Key);
});
}
}

// TODO: both DeleteVersions methods below have an issue. Sort of. They do NOT take care of files the way
Expand Down Expand Up @@ -2632,34 +2583,7 @@
PerformMoveContentLocked(descendant, userId, trash);
}
}
while (total > pageSize);

Check notice on line 2586 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ No longer an issue: Bumpy Road Ahead

PerformMoveLocked is no longer above the threshold for logical blocks with deeply nested code. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

if (parentId == Constants.System.RecycleBinContent)
{
// Updates in-memory navigation structure for both document items and recycle bin items
// as we are moving to recycle bin
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.PerformMoveLocked-to-recycle-bin",
() => _documentNavigationManagementService.MoveToBin(content.Key));
}
else
{
if (cameFromRecycleBin)
{
// Updates in-memory navigation structure for both document items and recycle bin items
// as we are restoring from recycle bin
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.PerformMoveLocked-restore",
() => _documentNavigationManagementService.RestoreFromBin(content.Key, parent?.Key));
}
else
{
// Updates in-memory navigation structure
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.PerformMoveLocked",
() => _documentNavigationManagementService.Move(content.Key, parent?.Key));
}
}
}

private void PerformMoveContentLocked(IContent content, int userId, bool? trash)
Expand Down Expand Up @@ -2863,21 +2787,7 @@
}
}
}

Check notice on line 2790 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ Getting better: Complex Method

Copy decreases in cyclomatic complexity from 15 to 13, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check notice on line 2790 in src/Umbraco.Core/Services/ContentService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

✅ No longer an issue: Bumpy Road Ahead

Copy is no longer above the threshold for logical blocks with deeply nested code. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.
if (navigationUpdates.Count > 0)
{
// Updates in-memory navigation structure
UpdateInMemoryNavigationStructure(
"Umbraco.Cms.Core.Services.ContentService.Copy",
() =>
{
foreach (Tuple<Guid, Guid?> update in navigationUpdates)
{
_documentNavigationManagementService.Add(update.Item1, update.Item2);
}
});
}

// not handling tags here, because
// - tags should be handled by the content repository
// - a copy is unpublished and therefore has no impact on tags in DB
Expand Down Expand Up @@ -3822,28 +3732,4 @@

#endregion

/// <summary>
/// Enlists an action in the current scope context to update the in-memory navigation structure
/// when the scope completes successfully.
/// </summary>
/// <param name="enlistingActionKey">The unique key identifying the action to be enlisted.</param>
/// <param name="updateNavigation">The action to be performed for updating the in-memory navigation structure.</param>
/// <exception cref="NullReferenceException">Thrown when the scope context is null and therefore cannot be used.</exception>
private void UpdateInMemoryNavigationStructure(string enlistingActionKey, Action updateNavigation)
{
IScopeContext? scopeContext = ScopeProvider.Context;

if (scopeContext is null)
{
throw new NullReferenceException($"The {nameof(scopeContext)} is null and cannot be used.");
}

scopeContext.Enlist(enlistingActionKey, completed =>
{
if (completed)
{
updateNavigation();
}
});
}
}
Loading
Loading