Skip to content

Commit

Permalink
Fix: ensure bulk notifications fire and post import runs as required. (
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinJump authored Feb 13, 2025
1 parent 9eb8d84 commit 4e75e64
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 68 deletions.
17 changes: 17 additions & 0 deletions uSync.BackOffice/Models/SyncFinalActionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;

using uSync.BackOffice.SyncHandlers.Models;

namespace uSync.BackOffice.Models;

public class SyncFinalActionRequest

Check warning on line 8 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest'

Check warning on line 8 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest'
{
public required Guid RequestId { get; set; }

Check warning on line 10 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.RequestId'

Check warning on line 10 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.RequestId'
public HandlerActions HandlerAction { get; set; } = HandlerActions.None;

Check warning on line 11 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.HandlerAction'

Check warning on line 11 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.HandlerAction'
public required SyncActionOptions ActionOptions { get; set; }

Check warning on line 12 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.ActionOptions'

Check warning on line 12 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.ActionOptions'
public IEnumerable<uSyncAction> Actions { get; set; } = [];

Check warning on line 13 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.Actions'

Check warning on line 13 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.Actions'
public string? Username { get; set; }

Check warning on line 14 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.Username'

Check warning on line 14 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.Username'
public uSyncCallbacks? Callbacks { get; set; }

Check warning on line 15 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.Callbacks'

Check warning on line 15 in uSync.BackOffice/Models/SyncFinalActionRequest.cs

View workflow job for this annotation

GitHub Actions / build-project

Missing XML comment for publicly visible type or member 'SyncFinalActionRequest.Callbacks'
}

27 changes: 24 additions & 3 deletions uSync.BackOffice/Services/ISyncActionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,18 @@ SyncActionResult ImportHandler(SyncActionOptions options, uSyncCallbacks? callba
[Obsolete("use ImportPostAsync will be removed in v16")]
SyncActionResult ImportPost(SyncActionOptions options, uSyncCallbacks? callbacks)
=> ImportPostAsync(options, callbacks).Result;


/// <summary>
/// run an export based on the options provided
/// </summary>
[Obsolete("use ImportPostAsync with finalActionRequest , will be removed in v16")]
Task<SyncActionResult> ImportPostAsync(SyncActionOptions options, uSyncCallbacks? callbacks)
=> ImportPostAsync(new SyncFinalActionRequest { RequestId = Guid.NewGuid(), ActionOptions = options, Callbacks = callbacks });

/// <summary>
/// run an export based on the options provided
/// </summary>
Task<SyncActionResult> ImportPostAsync(SyncActionOptions options, uSyncCallbacks? callbacks);
Task<SyncActionResult> ImportPostAsync(SyncFinalActionRequest request);

/// <summary>
/// run a report for a given handler based on the options provided.
Expand Down Expand Up @@ -85,7 +92,21 @@ SyncActionResult ReportHandler(SyncActionOptions options, uSyncCallbacks? callba
/// <summary>
/// finish the bulk process
/// </summary>
Task FinishProcessAsync(HandlerActions action, IEnumerable<uSyncAction> actions, string username);
[Obsolete("use FinishProcessAsync with FinalActionRequest will be removed in v16")]
Task FinishProcessAsync(HandlerActions action, IEnumerable<uSyncAction> actions, string username)
=> FinishProcessAsync(new SyncFinalActionRequest
{
HandlerAction = action,
RequestId = Guid.NewGuid(),
ActionOptions = new(),
Actions = actions,
Username = username
});

/// <summary>
/// finish the bulk process
/// </summary>
Task<SyncActionResult> FinishProcessAsync(SyncFinalActionRequest request);

/// <summary>
/// returns the export folder zipped up as a stream
Expand Down
21 changes: 12 additions & 9 deletions uSync.BackOffice/Services/SyncActionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,14 @@ public async Task<SyncActionResult> ImportHandlerAsync(SyncActionOptions options
return new SyncActionResult(actions);
}

public async Task<SyncActionResult> ImportPostAsync(SyncActionOptions options, uSyncCallbacks? callbacks)
public async Task<SyncActionResult> ImportPostAsync(SyncFinalActionRequest request)
{
var actions = await _uSyncService.PerformPostImportAsync(
options.GetFoldersOrDefault(_uSyncConfig.GetFolders()),
options.GetSetOrDefault(_uSyncConfig.Settings.DefaultSet),
options.Actions);

callbacks?.Update?.Invoke("Import Complete", 1, 1);
request.ActionOptions.GetFoldersOrDefault(_uSyncConfig.GetFolders()),
request.ActionOptions.GetSetOrDefault(_uSyncConfig.Settings.DefaultSet),
request.Actions);

request.Callbacks?.Update?.Invoke("Post Import Complete", 1, 1);
return new SyncActionResult(actions.Where(x => x.Change > Core.ChangeType.NoChange).ToList());
}

Expand Down Expand Up @@ -185,12 +184,16 @@ public async Task StartProcessAsync(HandlerActions action)
=> await _uSyncService.StartBulkProcessAsync(action);

/// <inheritdoc/>
public async Task FinishProcessAsync(HandlerActions action, IEnumerable<uSyncAction> actions, string username)
public async Task<SyncActionResult> FinishProcessAsync(SyncFinalActionRequest request)
{
await _uSyncService.FinishBulkProcessAsync(action, actions);
await _uSyncService.FinishBulkProcessAsync(request.HandlerAction, request.Actions);

_logger.LogInformation("{user} finished {action} process ({changes} changes)",
username, action, actions.Count());
request.Username, request.HandlerAction, request.Actions.Count());

request.Callbacks?.Update?.Invoke("Process completed", 1, 1);

return new SyncActionResult(request.Actions.ToList());
}

public Stream GetExportFolderAsStream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Net.Mime;
using System.Text;

using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;

using uSync.Backoffice.Management.Api.Models;
Expand All @@ -22,16 +23,22 @@ public class uSyncPerformActionController : uSyncControllerBase
private readonly ISyncManagementService _managementService;
private readonly ITemporaryFileService _temporaryFileService;

public uSyncPerformActionController(ISyncManagementService managementService, ITemporaryFileService temporaryFileService)
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;

public uSyncPerformActionController(
ISyncManagementService managementService,
ITemporaryFileService temporaryFileService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_managementService = managementService;
_temporaryFileService = temporaryFileService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

[HttpPost("Perform")]
[ProducesResponseType(typeof(PerformActionResponse), 200)]
public async Task<PerformActionResponse> PerformAction(PerformActionRequest model)
=> await _managementService.PerformActionAsync(model);
=> await _managementService.PerformActionAsync(model, _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser);

Check warning on line 41 in uSync.Backoffice.Management.Api/Controllers/Actions/uSyncPerformActionController.cs

View workflow job for this annotation

GitHub Actions / build-project

Dereference of a possibly null reference.


[HttpPost("Download")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public class PerformActionRequest
public string Action { get; set; } = HandlerActions.Report.ToString();
public int StepNumber { get; set; }
public uSyncOptions? Options { get; set; }

public string? Username { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using uSync.Backoffice.Management.Api.Models;
using Umbraco.Cms.Core.Models.Membership;

using uSync.Backoffice.Management.Api.Models;
using uSync.BackOffice;
using uSync.BackOffice.Models;
using uSync.BackOffice.SyncHandlers.Models;
Expand All @@ -9,6 +11,11 @@ public interface ISyncManagementService
Stream CompressExportFolder();
List<SyncActionGroup> GetActions();
Func<SyncActionOptions, uSyncCallbacks, Task<SyncActionResult>> GetHandlerMethodAsync(HandlerActions action);
Task<PerformActionResponse> PerformActionAsync(PerformActionRequest actionRequest);

[Obsolete("Pass in IUser for better logging, will be removed in v16")]
Task<PerformActionResponse> PerformActionAsync(PerformActionRequest actionRequest)
=> PerformActionAsync(actionRequest, null);

Task<PerformActionResponse> PerformActionAsync(PerformActionRequest actionRequest, IUser? user);
UploadImportResult UnpackStream(Stream stream);
}
125 changes: 73 additions & 52 deletions uSync.Backoffice.Management.Api/Services/uSyncManagementService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.SignalR;

using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Extensions;

using uSync.Backoffice.Management.Api.Extensions;
Expand Down Expand Up @@ -155,39 +156,10 @@ public List<SyncActionGroup> GetActions()
}

return actionGroups;



// List<SyncActionGroup> actions = [
// new SyncActionGroup
// {
// GroupName = "Settings",
// Icon = "icon-settings-alt",
// Key = "settings",
// Buttons = defaultButtons
// },
// new SyncActionGroup
// {
// GroupName = "Content",
// Icon = "icon-documents",
// Key = "content",
// Buttons = defaultButtons
// },
// new SyncActionGroup
// {
// GroupName = "Everything",
// Icon = "icon-paper-plane-alt",
// Key = "all",
// Buttons = everythingButtons
// }
//];

// return actions;

}


public async Task<PerformActionResponse> PerformActionAsync(PerformActionRequest actionRequest)
public async Task<PerformActionResponse> PerformActionAsync(PerformActionRequest actionRequest, IUser? user)
{
if (Enum.TryParse(actionRequest.Action, out HandlerActions action) is false)
throw new ArgumentException($"Invalid action {actionRequest.Action}");
Expand All @@ -197,6 +169,8 @@ public async Task<PerformActionResponse> PerformActionAsync(PerformActionRequest

if (action == HandlerActions.Export && string.IsNullOrWhiteSpace(actionRequest.RequestId))
{
await _syncActionService.StartProcessAsync(action);

// first step in an export.
if (actionRequest.Options?.Clean is true)
{
Expand All @@ -214,50 +188,84 @@ public async Task<PerformActionResponse> PerformActionAsync(PerformActionRequest
}
uSyncCallbacks callbacks = hubClient?.Callbacks() ?? new uSyncCallbacks(null, null);

if (actionRequest.StepNumber >= handlers.Count)
{
var finalActions = _syncManagementCache.GetCachedActions(requestId);

// when complete we clean out our action cache.
_syncManagementCache.Clear(requestId);

callbacks?.Update?.Invoke("Finished", 1, 1);
var handlerOptions = new SyncActionOptions()
{
Folders = _configService.GetFolders(),
Set = actionRequest.Options?.Set ?? _configService.Settings.DefaultSet,
Force = actionRequest.Options?.Force ?? false,
Actions = new List<uSyncAction>(),
};

if (actionRequest.StepNumber >= handlers.Count)
{
var actions = await PerformFinalSteps(requestId, action, handlerOptions, callbacks, user?.Username);
// finished.
return new PerformActionResponse
{
{
RequestId = requestId.ToString(),
Actions = finalActions.Select(x => x.ToActionView()),
Actions = actions.Select(x => x.ToActionView()),
Complete = true,
Status = GetSummaries(handlers, actionRequest.StepNumber, finalActions)
Status = GetSummaries(action, handlers, actionRequest.StepNumber + 1, actions)
};
}

var currentHandler = handlers[actionRequest.StepNumber];
handlerOptions.Handler = handlers[actionRequest.StepNumber].Alias;
var method = GetHandlerMethodAsync(action);


var handlerOptions = new SyncActionOptions()
{
Folders = _configService.GetFolders(),
Set = actionRequest.Options?.Set ?? _configService.Settings.DefaultSet,
Force = actionRequest.Options?.Force ?? false,
Actions = new List<uSyncAction>(),
Handler = currentHandler.Alias
};

var results = await method(handlerOptions, callbacks);
_syncManagementCache.CacheItems(requestId, results.Actions, false);

return new PerformActionResponse
{
RequestId = requestId.ToString(),
Actions = results.Actions.Select(x => x.ToActionView()),
Status = GetSummaries(handlers, actionRequest.StepNumber, results.Actions.ToList()),
Status = GetSummaries(action, handlers, actionRequest.StepNumber, results.Actions.ToList()),
Complete = false
};
}

private async Task<List<uSyncAction>> PerformFinalSteps(Guid requestId, HandlerActions action, SyncActionOptions options, uSyncCallbacks callbacks, string? username)
{
var finalActions = _syncManagementCache.GetCachedActions(requestId);
var finalSteps = GetFinalStep(action);

var request = new SyncFinalActionRequest
{
RequestId = requestId,
HandlerAction = action,
ActionOptions = options,
Actions = finalActions,
Callbacks = callbacks,
Username = username ?? ""
};

foreach (var step in finalSteps)
{
var result = await step(request);
}

// when complete we clean out our action cache.
_syncManagementCache.Clear(requestId);

callbacks?.Update?.Invoke("Finished", 1, 1);
return finalActions;
}

private IEnumerable<Func<SyncFinalActionRequest, Task<SyncActionResult>>> GetFinalStep(HandlerActions action)
{
var steps = new List<Func<SyncFinalActionRequest, Task<SyncActionResult>>>();

if (action == HandlerActions.Import)
{
steps.Add(_syncActionService.ImportPostAsync);
}

steps.Add(_syncActionService.FinishProcessAsync);
return steps;
}

private Guid GetRequestId(PerformActionRequest actionRequest)
{
Guid requestId;
Expand All @@ -268,7 +276,7 @@ private Guid GetRequestId(PerformActionRequest actionRequest)
return requestId;
}

private IEnumerable<SyncHandlerSummary> GetSummaries(List<SyncHandlerView> handlers, int step, List<uSyncAction> actions)
private IEnumerable<SyncHandlerSummary> GetSummaries(HandlerActions action, List<SyncHandlerView> handlers, int step, List<uSyncAction> actions)
{
var nextStep = step + 1;
for (int n = 0; n < handlers.Count; n++)
Expand All @@ -285,6 +293,19 @@ private IEnumerable<SyncHandlerSummary> GetSummaries(List<SyncHandlerView> handl
InError = handlerActions.Any(x => x.Change >= Core.ChangeType.Fail)
};
}

if (action == HandlerActions.Import)
{
yield return new SyncHandlerSummary
{
Name = $"Post Import",
Icon = "icon-directions",
Status = nextStep == handlers.Count ? HandlerStatus.Processing :
nextStep > handlers.Count ? HandlerStatus.Complete : HandlerStatus.Pending,
Changes = 0,
InError = false
};
}
}


Expand Down

0 comments on commit 4e75e64

Please sign in to comment.