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

Fix: ensure bulk notifications fire and post import runs as required. #706

Merged
merged 1 commit into from
Feb 13, 2025
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
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 @@
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
Loading