Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
41 changes: 36 additions & 5 deletions src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;

Expand All @@ -9,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Sync;
public class SyncBootStateAccessor : ISyncBootStateAccessor
{
private readonly ICacheInstructionService _cacheInstructionService;
private readonly LastSyncedFileManager _lastSyncedFileManager;
private readonly ILastSyncedManager _lastSyncedManager;
private readonly ILogger<SyncBootStateAccessor> _logger;
private GlobalSettings _globalSettings;

Expand All @@ -19,24 +21,53 @@ public class SyncBootStateAccessor : ISyncBootStateAccessor

public SyncBootStateAccessor(
ILogger<SyncBootStateAccessor> logger,
LastSyncedFileManager lastSyncedFileManager,
IOptionsMonitor<GlobalSettings> globalSettings,
ICacheInstructionService cacheInstructionService)
ICacheInstructionService cacheInstructionService,
ILastSyncedManager lastSyncedManager)
{
_logger = logger;
_lastSyncedFileManager = lastSyncedFileManager;
_lastSyncedManager = lastSyncedManager;
_globalSettings = globalSettings.CurrentValue;
_cacheInstructionService = cacheInstructionService;

globalSettings.OnChange(x => _globalSettings = x);
}

[Obsolete("Please use the constructor without LastSyncedFileManager. Scheduled for removal in Umbraco 18.")]
public SyncBootStateAccessor(
ILogger<SyncBootStateAccessor> logger,
LastSyncedFileManager lastSyncedFileManager,
IOptionsMonitor<GlobalSettings> globalSettings,
ICacheInstructionService cacheInstructionService,
ILastSyncedManager lastSyncedManager)
: this(
logger,
globalSettings,
cacheInstructionService,
lastSyncedManager)
{
}

[Obsolete("Please use the constructor with ILastSyncedManager. Scheduled for removal in Umbraco 18.")]
public SyncBootStateAccessor(
ILogger<SyncBootStateAccessor> logger,
LastSyncedFileManager lastSyncedFileManager,
IOptionsMonitor<GlobalSettings> globalSettings,
ICacheInstructionService cacheInstructionService)
: this(
logger,
globalSettings,
cacheInstructionService,
StaticServiceProvider.Instance.GetRequiredService<ILastSyncedManager>())
{
}

public SyncBootState GetSyncBootState()
=> LazyInitializer.EnsureInitialized(
ref _syncBootState,
ref _syncBootStateReady,
ref _syncBootStateLock,
() => InitializeColdBootState(_lastSyncedFileManager.LastSyncedId));
() => InitializeColdBootState(_lastSyncedManager.GetLastSyncedExternalAsync().GetAwaiter().GetResult() ?? -1));

private SyncBootState InitializeColdBootState(int lastId)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Cms.Infrastructure.Sync;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;

namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Sync;

[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class SyncBootStateAccessorTest : UmbracoIntegrationTest
{
private ILastSyncedManager LastSyncedManager => GetRequiredService<ILastSyncedManager>();

private SyncBootStateAccessor CreateSyncBootStateAccessor() => new(
GetRequiredService<ILogger<SyncBootStateAccessor>>(),
GetRequiredService<IOptionsMonitor<GlobalSettings>>(),
GetRequiredService<ICacheInstructionService>(),
LastSyncedManager);

[Test]
public void Returns_ColdBoot_When_No_Last_Synced_Id()
{
// Arrange - the database is fresh with no last synced id saved
var syncBootStateAccessor = CreateSyncBootStateAccessor();

// Act
var result = syncBootStateAccessor.GetSyncBootState();

// Assert
Assert.AreEqual(SyncBootState.ColdBoot, result);
}

[Test]
public async Task Returns_WarmBoot_When_Last_Synced_Id_Exists()
{
// Arrange - create a cache instruction (ID is auto-incremented starting at 1)
// and save the last synced external id matching it
using (var scope = ScopeProvider.CreateScope())
{
var repo = new CacheInstructionRepository((IScopeAccessor)ScopeProvider);
repo.Add(new CacheInstruction(0, DateTime.Now, "{}", "Test", 1));
scope.Complete();
}

// The auto-incremented ID will be 1 in a fresh database
await LastSyncedManager.SaveLastSyncedExternalAsync(1);

var syncBootStateAccessor = CreateSyncBootStateAccessor();

// Act
var result = syncBootStateAccessor.GetSyncBootState();

// Assert
Assert.AreEqual(SyncBootState.WarmBoot, result);
}
}
Loading