Skip to content

Commit

Permalink
Moved the migration lookups db calls into their own repository class. (
Browse files Browse the repository at this point in the history
…#7)

Added a new class that extends MigrationLookupsRepository, which will store migration relations into the Umbraco Runtime Cache.

Added a new app setting to disable this feature.
  • Loading branch information
OwainJ authored Dec 31, 2024
1 parent e55b21d commit 937e96d
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 79 deletions.
5 changes: 5 additions & 0 deletions src/Method4.UmbracoMigrator.Target.Core/Composer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Method4.UmbracoMigrator.Target.Core.MigrationSteps.Tasks;
using Method4.UmbracoMigrator.Target.Core.Models.DataModels;
using Method4.UmbracoMigrator.Target.Core.Options;
using Method4.UmbracoMigrator.Target.Core.Repositories;
using Method4.UmbracoMigrator.Target.Core.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -38,6 +39,10 @@ public void Compose(IUmbracoBuilder builder)
builder.Services.AddTransient<IMigrationContentFactory, MigrationContentFactory>();
builder.Services.AddTransient<IMigrationMediaFactory, MigrationMediaFactory>();

// Repositories
builder.Services.AddTransient<MigrationLookupsRepository>();
builder.Services.AddTransient<MigrationLookupsRepositoryWithCache>();

// Services
builder.Services.AddTransient<IMigratorBlobService, MigratorBlobService>();
builder.Services.AddTransient<IMigratorFileService, MigratorFileService>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
namespace Method4.UmbracoMigrator.Target.Core.Options
using System.ComponentModel;

namespace Method4.UmbracoMigrator.Target.Core.Options
{
public class MigratorTargetSettings
{
public bool EnableMediaRedirectGeneration { get; set; }
/// <summary>
/// For use with the Skybrud Redirects package.
/// Enables the UniqueMediaPathScheme IMediaPathScheme.
/// This will automatically generate a re-direct if a migrated media item's file is changed.
/// Please see the documentation for more information.
/// </summary>
[DefaultValue(false)]
public bool EnableMediaRedirectGeneration { get; set; } = false;

/// <summary>
/// The property alias of the Media File Upload property.
/// </summary>
[DefaultValue("umbracoFile")]
public string? MediaFileUploadPropertyAlias { get; set; }

/// <summary>
/// Enable the Runtime Caching on the MigrationLookups repository
/// </summary>
[DefaultValue(true)]
public bool EnableMigrationLookupTableRuntimeCaching { get; set; } = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Method4.UmbracoMigrator.Target.Core.Models.DataModels;

namespace Method4.UmbracoMigrator.Target.Core.Repositories;

public interface IMigrationLookupsRepository
{
NodeRelation? Get(string columnName, string value);
void Insert(string newId, string oldId, string newKey, string oldKey);
int Count();
void DeleteAll();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Method4.UmbracoMigrator.Target.Core.CustomDbTables.NPoco;
using Method4.UmbracoMigrator.Target.Core.Models.DataModels;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Infrastructure.Scoping;

namespace Method4.UmbracoMigrator.Target.Core.Repositories
{
public partial class MigrationLookupsRepository : IMigrationLookupsRepository
{
private readonly IUmbracoMapper _umbracoMapper;
private readonly IScopeProvider _scopeProvider;
private readonly ILogger<MigrationLookupsRepository> _logger;
private const string TableName = "MigrationLookups";

public MigrationLookupsRepository(IUmbracoMapper umbracoMapper, IScopeProvider scopeProvider, ILogger<MigrationLookupsRepository> logger)
{
_umbracoMapper = umbracoMapper;
_scopeProvider = scopeProvider;
_logger = logger;
}

public virtual NodeRelation? Get(string columnName, string value)
{
using var scope = _scopeProvider.CreateScope();
var queryResult = scope.Database.Fetch<NodeRelationLookupPoco>($"SELECT * From {TableName} WHERE {columnName} = @0", value);
scope.Complete();

var foundLookup = queryResult.FirstOrDefault(); // There should only ever be 1
if (foundLookup == null)
{
_logger.LogDebug("Lookup Not found for {lookupType}: {lookupValue}", columnName, value);
return null;
}

var relation = _umbracoMapper.Map<NodeRelation>(foundLookup);
if (relation == null)
{
_logger.LogError("Unable to map NodeRelationLookupPoco to NodeRelation for {lookupType}: {value}", columnName, value);
throw new Exception($"Lookup for {columnName}: '{value}' could not be mapped, result from the Umbraco Mapper was null");
}

return relation;
}

public virtual void Insert(string newId, string oldId, string newKey, string oldKey)
{
var relationLookup = new NodeRelationLookupPoco()
{
NewId = newId,
OldId = oldId,
NewKey = newKey,
OldKey = oldKey
};

try
{
using var scope = _scopeProvider.CreateScope();
scope.Database.Insert<NodeRelationLookupPoco>(relationLookup);
scope.Complete();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to store new migration node relation");
throw;
}
}

public virtual int Count()
{
using var scope = _scopeProvider.CreateScope();
var queryResult = scope.Database.Fetch<NodeRelationLookupPoco>($"SELECT * From {TableName}");
scope.Complete();

return queryResult.Count;
}

public virtual void DeleteAll()
{
_logger.LogInformation("Deleting all Key relations");
try
{
using var scope = _scopeProvider.CreateScope();
var queryResult = scope.Database.Execute($"DELETE FROM {TableName}");
scope.Complete();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to delete all relations from the {tableName} table", TableName);
throw;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using Method4.UmbracoMigrator.Target.Core.Models.DataModels;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Extensions;

namespace Method4.UmbracoMigrator.Target.Core.Repositories
{
public class MigrationLookupsRepositoryWithCache : MigrationLookupsRepository
{
private readonly IAppPolicyCache _runtimeCache;
private readonly ILogger<MigrationLookupsRepositoryWithCache> _logger;

private const string CacheKey = "MigrationRelationLookup";
private const int CacheTimeoutDays = 7;

public MigrationLookupsRepositoryWithCache(
AppCaches appCaches,
IUmbracoMapper umbracoMapper,
IScopeProvider scopeProvider,
ILogger<MigrationLookupsRepository> logger,
ILogger<MigrationLookupsRepositoryWithCache> logger2)
: base(umbracoMapper, scopeProvider, logger)
{
_runtimeCache = appCaches.RuntimeCache;
_logger = logger2;
}

public override NodeRelation? Get(string columnName, string value)
{
var relation = _runtimeCache.GetCacheItem<NodeRelation>($"{CacheKey}_{columnName}_{value}");
if (relation != null) { return relation; }

relation = base.Get(columnName, value);
if (relation == null) { return relation; }

InsertIntoRuntimeCache(relation);
return relation;
}

public override void Insert(string newId, string oldId, string newKey, string oldKey)
{
base.Insert(newId, oldId, newKey, oldKey);
var relationLookup = base.Get("NewId", newId);
if (relationLookup != null)
{
InsertIntoRuntimeCache(relationLookup);
}
}

public override void DeleteAll()
{
base.DeleteAll();
try
{
_runtimeCache.ClearOfType<NodeRelation>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to remove Migration Relations from the Umbraco runtime cache");
throw;
}
}

/// <summary>
/// Adds the node relation to the runtime cache if it doesn't exist.
/// </summary>
/// <param name="relation"></param>
private void InsertIntoRuntimeCache(NodeRelation relation)
{
var newIdCacheKey = $"{CacheKey}_NewId_{relation.NewId}";
var oldIdCacheKey = $"{CacheKey}_OldId_{relation.OldId}";
var newKeyCacheKey = $"{CacheKey}_NewKey_{relation.NewKeyAsString}";
var oldKeyCacheKey = $"{CacheKey}_OldKey_{relation.OldKeyAsString}";

try
{
if (_runtimeCache.GetCacheItem<NodeRelation>(newIdCacheKey) == null)
{
_runtimeCache.GetCacheItem(
newIdCacheKey,
() => relation,
TimeSpan.FromDays(CacheTimeoutDays));
}

if (_runtimeCache.GetCacheItem<NodeRelation>(oldIdCacheKey) == null)
{
_runtimeCache.GetCacheItem(
oldIdCacheKey,
() => relation,
TimeSpan.FromDays(CacheTimeoutDays));
}

if (_runtimeCache.GetCacheItem<NodeRelation>(newKeyCacheKey) == null)
{
_runtimeCache.GetCacheItem(
newKeyCacheKey,
() => relation,
TimeSpan.FromDays(CacheTimeoutDays));
}

if (_runtimeCache.GetCacheItem<NodeRelation>(oldKeyCacheKey) == null)
{
_runtimeCache.GetCacheItem(
oldKeyCacheKey,
() => relation,
TimeSpan.FromDays(CacheTimeoutDays));
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to insert Migration Relation into the Umbraco runtime cache");
throw;
}
}
}
}
Loading

0 comments on commit 937e96d

Please sign in to comment.