Skip to content
8 changes: 7 additions & 1 deletion uSync.BackOffice/Models/SyncMergeOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using uSync.BackOffice.SyncHandlers.Interfaces;
using uSync.Core.Roots.Models;

namespace uSync.BackOffice.Models;

Expand Down Expand Up @@ -28,4 +29,9 @@ public SyncMergeOptions(SyncUpdateCallback? callback)
/// Callback use to pass info to the UI.
/// </summary>
public SyncUpdateCallback? UpdateCallback { get; set; }
}

/// <summary>
/// what type of merging are we going to do.
/// </summary>
public SyncMergeStrategy MergeStrategy { get; set; } = SyncMergeStrategy.Magic;
}
45 changes: 39 additions & 6 deletions uSync.BackOffice/Services/ISyncFileService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Linq;

using uSync.BackOffice.Models;
using uSync.Core.Roots.Models;
using uSync.Core.Tracking;

namespace uSync.BackOffice.Services;
Expand Down Expand Up @@ -91,10 +93,18 @@ public interface ISyncFileService
/// </summary>
Task<List<XElement>> GetAllNodesAsync(string[] filePaths);

/// <summary>
/// legacy merge (fancy) - will be removed in v19
/// </summary>
[Obsolete("Will be removed in v19 - pass the options for greater control")]
XElement? GetDifferences(List<XElement> nodes, ISyncTrackerBase? trackerBase)
=> GetDifferences(nodes, trackerBase, new SyncFileMergeOptions());


/// <summary>
/// get a XML representation of the differences between two files
/// </summary>
XElement? GetDifferences(List<XElement> nodes, ISyncTrackerBase? trackerBase);
XElement? GetDifferences(List<XElement> nodes, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options);

/// <summary>
/// list the folders inside a folder
Expand Down Expand Up @@ -135,18 +145,40 @@ public interface ISyncFileService
/// <returns></returns>
Task<XElement> LoadXElementAsync(string file);

/// <summary>
/// legacy merge (fancy) - will be removed in v19
/// </summary>
[Obsolete("Will be removed in v19 - pass the options for greater control")]
Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension)
=> MakeSingleExportFromFolders(folders, itemType, trackerBase, fileName, extension, new SyncFileMergeOptions());

/// <summary>
/// merge all the files in the given folders into a single xml node, that can be bulk imported
/// </summary>
Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension);
Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension, SyncFileMergeOptions options);

/// <summary>
/// legacy merge (fancy) - will be removed in v19
/// </summary>
[Obsolete("Will be removed in v19 - pass the options for greater control")]
Task<XElement?> MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase)
=> MergeFilesAsync(filenames, trackerBase, new SyncFileMergeOptions());

/// <summary>
/// merge a list of files into a single XElement
/// </summary>
/// <remarks>
/// depending on the tracker this can do clever things like merge bits of doctypes together.
/// </remarks>
Task<XElement?> MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase);
Task<XElement?> MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options);

/// <summary>
/// legacy merge (fancy) - will be removed in v19
/// </summary>
[Obsolete("Will be removed in v19 - pass the options for greater control")]
Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase)
=> MergeFoldersAsync(folders, extension, trackerBase, new SyncFileMergeOptions());


/// <summary>
/// Merge a number of uSync folders into a single 'usync source'
Expand All @@ -161,7 +193,8 @@ public interface ISyncFileService
/// the doctype tracker merges properties so you can have
/// property level root values for doctypes.
/// </remarks>
Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase);
Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase,
SyncFileMergeOptions options);


/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion uSync.BackOffice/Services/ISyncService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using uSync.BackOffice.Models;
using uSync.BackOffice.SyncHandlers.Interfaces;
using uSync.BackOffice.SyncHandlers.Models;
using uSync.Core.Roots.Models;

namespace uSync.BackOffice;

Expand Down Expand Up @@ -158,7 +159,8 @@ public interface ISyncService
Task FinishBulkProcessAsync(HandlerActions action, IEnumerable<uSyncAction> actions);

/// <summary>
/// merge the given folders in single 'production' files for each handler.
/// Merge the given folders in single 'production' files for each handler.
/// [Obsolete: Use overload with SyncFileMergeOptions for greater control]
/// </summary>
Task<int> MergeExportFolder(string[] paths, IEnumerable<HandlerConfigPair> handlers);

Expand Down
32 changes: 22 additions & 10 deletions uSync.BackOffice/Services/SyncFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
using System.Xml.Linq;

using Umbraco.Cms.Core.Extensions;

using uSync.Core;
using uSync.Core.Roots.Models;
using uSync.Core.Tracking;

namespace uSync.BackOffice.Services;
Expand Down Expand Up @@ -324,9 +324,9 @@ public void CopyFolder(string source, string target)
}
}

public async Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string filename, string extension)
public async Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string filename, string extension, SyncFileMergeOptions options)
{
var merged = await MergeFoldersAsync(folders, extension, trackerBase);
var merged = await MergeFoldersAsync(folders, extension, trackerBase, options);

var megaNode = new XElement(itemType + "s");
int count = 0;
Expand Down Expand Up @@ -407,7 +407,7 @@ static string GetShortFileName(string file)
#region roots

/// <inheritdoc/>
public async Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase)
public async Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options)
{
var elements = new Dictionary<string, OrderedNodeInfo>();
var cleanElements = new Dictionary<string, OrderedNodeInfo>();
Expand Down Expand Up @@ -436,7 +436,7 @@ public async Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folde
if (elements.TryGetValue(item.Key, out var value))
{
// merge these files.
item.Value.SetNode(MergeNodes(value.Node, item.Value.Node, trackerBase));
item.Value.SetNode(MergeNodes(value.Node, item.Value.Node, trackerBase, options));
item.Value.SetFileName($"{uSyncConstants.MergedFolderName}/{Path.GetFileName(item.Value.FileName)}");
}
}
Expand Down Expand Up @@ -468,7 +468,7 @@ public async Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folde
}

/// <inheritdoc/>
public async Task<XElement?> MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase)
public async Task<XElement?> MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options)
{
if (filenames.Length == 0) return null;
var latest = await LoadXElementSafeAsync(filenames[0]);
Expand All @@ -478,13 +478,20 @@ public async Task<IEnumerable<OrderedNodeInfo>> MergeFoldersAsync(string[] folde
{
var node = await LoadXElementSafeAsync(filenames[n]);
if (node is null) continue;
latest = MergeNodes(latest, node, trackerBase);
latest = MergeNodes(latest, node, trackerBase, options);
}
return latest;
}

private static XElement MergeNodes(XElement source, XElement target, ISyncTrackerBase? trackerBase)
=> trackerBase is null ? target : trackerBase.MergeFiles(source, target) ?? target;
private static XElement MergeNodes(XElement source, XElement target, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options)
{
if (trackerBase is ISyncTrackerOptionsBase optionsBase)
{
return optionsBase.MergeFiles(source, target, options) ?? target;
}

return trackerBase is null ? target : trackerBase.MergeFiles(source, target) ?? target;
}

private async Task<IEnumerable<KeyValuePair<string, OrderedNodeInfo>>> GetFolderItemsAsync(string folder, string extension)
{
Expand Down Expand Up @@ -525,7 +532,7 @@ private async Task<IEnumerable<KeyValuePair<string, OrderedNodeInfo>>> GetFolder
}

/// <inheritdoc/>
public XElement? GetDifferences(List<XElement> nodes, ISyncTrackerBase? trackerBase)
public XElement? GetDifferences(List<XElement> nodes, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options)
{
try
{
Expand All @@ -534,6 +541,11 @@ private async Task<IEnumerable<KeyValuePair<string, OrderedNodeInfo>>> GetFolder
if (trackerBase is null)
return SyncRootMergerHelper.GetDifferencesByFileContents(nodes);

if (trackerBase is ISyncTrackerOptionsBase optionsBase)
{
return optionsBase.GetDifferences(nodes, options);
}

return trackerBase?.GetDifferences(nodes);
}
catch (Exception ex)
Expand Down
14 changes: 12 additions & 2 deletions uSync.BackOffice/Services/SyncService_Files.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;

using uSync.BackOffice.Configuration;
using uSync.BackOffice.SyncHandlers.Models;
using uSync.Core.Roots.Models;

using CoreConstants = uSync.Core.uSyncConstants;

namespace uSync.BackOffice;

Expand Down Expand Up @@ -130,7 +133,14 @@ public async Task<int> MergeExportFolder(string[] paths, IEnumerable<HandlerConf
var targetFileName = Path.Combine(_uSyncConfig.Settings.ProductionFolder,
handler.Handler.DefaultFolder + "." + _uSyncConfig.Settings.DefaultExtension);

totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, baseTracker, targetFileName, _uSyncConfig.Settings.DefaultExtension);
var handlerMergeOptions = new SyncFileMergeOptions
{
MergeStrategy = handler.Settings.GetSetting<SyncMergeStrategy>(
CoreConstants.DefaultSettings.MergeStrategy,
CoreConstants.DefaultSettings.MergeStrategy_Default)
};

totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, baseTracker, targetFileName, _uSyncConfig.Settings.DefaultExtension, handlerMergeOptions);
}

return totalMerged;
Expand Down
30 changes: 23 additions & 7 deletions uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
using uSync.Core;
using uSync.Core.Dependency;
using uSync.Core.Models;
using uSync.Core.Roots.Models;
using uSync.Core.Serialization;
using uSync.Core.Tracking;

using CoreConstants = uSync.Core.uSyncConstants;

namespace uSync.BackOffice.SyncHandlers;

/// <summary>
Expand Down Expand Up @@ -346,8 +349,13 @@ public async Task<IReadOnlyList<OrderedNodeInfo>> FetchAllNodesAsync(string[] fo
/// </summary>
protected virtual async Task<IReadOnlyList<OrderedNodeInfo>> GetMergedItemsAsync(string[] folders, SyncMergeOptions options)
{
var fileMergeOptions = new SyncFileMergeOptions
{
MergeStrategy = options.MergeStrategy
};

var baseTracker = trackers.FirstOrDefault() as ISyncTrackerBase;
return [.. (await syncFileService.MergeFoldersAsync(folders, uSyncConfig.Settings.DefaultExtension, baseTracker))];
return [.. (await syncFileService.MergeFoldersAsync(folders, uSyncConfig.Settings.DefaultExtension, baseTracker, fileMergeOptions))];
}

/// <summary>
Expand All @@ -360,14 +368,14 @@ protected virtual async Task<IReadOnlyList<OrderedNodeInfo>> GetMergedItemsAsync
/// <summary>
/// given a file path, will give you the merged values across all folders.
/// </summary>
protected virtual async Task<XElement?> GetMergedNodeAsync(string filePath)
protected virtual async Task<XElement?> GetMergedNodeAsync(string filePath, SyncFileMergeOptions options)
{
var allFiles = uSyncConfig.GetFolders()
.Select(x => syncFileService.GetAbsPath($"{x}/{this.DefaultFolder}/{filePath}"))
.ToArray();

var baseTracker = trackers.FirstOrDefault() as ISyncTrackerBase;
return await syncFileService.MergeFilesAsync(allFiles, baseTracker);
return await syncFileService.MergeFilesAsync(allFiles, baseTracker, options);
}


Expand Down Expand Up @@ -434,7 +442,7 @@ virtual public async Task<IEnumerable<uSyncAction>> ImportAsync(string file, Han

if (file.InvariantStartsWith($"{uSyncConstants.MergedFolderName}/"))
{
var node = await GetMergedNodeAsync(file.Substring(uSyncConstants.MergedFolderName.Length + 1));
var node = await GetMergedNodeAsync(file.Substring(uSyncConstants.MergedFolderName.Length + 1), new SyncFileMergeOptions());
if (node is not null)
return await ImportElementAsync(node, file, config, options);
else
Expand Down Expand Up @@ -859,7 +867,7 @@ public async Task<IEnumerable<uSyncAction>> ExportAsync(Udi udi, string[] folder

return [uSyncAction.Fail(nameof(udi), this.handlerType, this.ItemType, ChangeType.Fail, $"Item not found {udi}",
new KeyNotFoundException(nameof(udi)))];

}

/// <summary>
Expand Down Expand Up @@ -926,7 +934,15 @@ protected virtual async Task<SyncAttempt<XElement>> Export_DoExportAsync(TObject
if (nodes.Count > 0)
{
nodes.Add(attempt.Item);
var differences = syncFileService.GetDifferences(nodes, trackers.FirstOrDefault());

var mergeOptions = new SyncFileMergeOptions
{
MergeStrategy = config.GetSetting<SyncMergeStrategy>(
CoreConstants.DefaultSettings.MergeStrategy,
CoreConstants.DefaultSettings.MergeStrategy_Default)
};

var differences = syncFileService.GetDifferences(nodes, trackers.FirstOrDefault(), mergeOptions);
if (differences is not null && differences.HasElements)
{
if (config.FullFileOnDifference)
Expand Down Expand Up @@ -1309,7 +1325,7 @@ public virtual async Task<IEnumerable<uSyncAction>> ReportElementSingleAsync(XEl
{
return [uSyncActionHelper<TObject>
.ReportActionFail(Path.GetFileName(node.GetAlias()), $"format error {fex.Message}")];

}
}

Expand Down
5 changes: 3 additions & 2 deletions uSync.Core/Roots/Configs/BlockGridConfigMerger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Umbraco.Cms.Core;

using uSync.Core.Extensions;
using uSync.Core.Roots.Models;

namespace uSync.Core.Roots.Configs;

Expand Down Expand Up @@ -30,14 +31,14 @@ public virtual object GetMergedConfig(string root, string target)
return MergeJsonProperties(rootConfig, targetConfig, "_");
}

public virtual object GetDifferenceConfig(string root, string target)
public virtual object GetDifferenceConfig(string root, string target, SyncFileMergeOptions options)
{
var rootConfig = root.DeserializeJson<JsonObject>();
var targetConfig = target.DeserializeJson<JsonObject>();

if (targetConfig is null) return target;
if (rootConfig is null) return target;

return GetJsonPropertyDifferences(rootConfig, targetConfig, "_");
return GetJsonPropertyDifferences(rootConfig, targetConfig, "_", options);
}
}
5 changes: 3 additions & 2 deletions uSync.Core/Roots/Configs/BlockListConfigMerger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Umbraco.Cms.Core;

using uSync.Core.Extensions;
using uSync.Core.Roots.Models;

namespace uSync.Core.Roots.Configs;

Expand Down Expand Up @@ -30,15 +31,15 @@ public virtual object GetMergedConfig(string root, string target)
return MergeJsonProperties(rootConfig, targetConfig, "_");
}

public virtual object GetDifferenceConfig(string root, string target)
public virtual object GetDifferenceConfig(string root, string target, SyncFileMergeOptions options)
{
var rootConfig = root.DeserializeJson<JsonObject>();
var targetConfig = target.DeserializeJson<JsonObject>();

if (targetConfig is null) return target;
if (rootConfig is null) return target;

return GetJsonPropertyDifferences(rootConfig, targetConfig, "_");
return GetJsonPropertyDifferences(rootConfig, targetConfig, "_", options);
}

}
Loading