Skip to content

Commit 5908080

Browse files
feat: Add cancel operation functionality for map rotations with user confirmation
1 parent 1b640ae commit 5908080

2 files changed

Lines changed: 85 additions & 2 deletions

File tree

src/XtremeIdiots.Portal.Web/Controllers/MapRotationsController.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,4 +723,72 @@ public async Task<IActionResult> GetSyncProgress(string instanceId, Cancellation
723723
_ => Json(new { status = "error" })
724724
};
725725
}
726+
727+
[HttpPost]
728+
[ValidateAntiForgeryToken]
729+
public async Task<IActionResult> CancelOperation(Guid operationId, Guid assignmentId, CancellationToken cancellationToken = default)
730+
{
731+
return await ExecuteWithErrorHandlingAsync(async () =>
732+
{
733+
var assignmentResponse = await repositoryApiClient.MapRotations.V1.GetServerAssignment(assignmentId, cancellationToken).ConfigureAwait(false);
734+
735+
if (assignmentResponse.IsNotFound || assignmentResponse.Result?.Data is null)
736+
return NotFound();
737+
738+
var assignment = assignmentResponse.Result.Data;
739+
740+
var rotationResponse = await repositoryApiClient.MapRotations.V1.GetMapRotation(assignment.MapRotationId, cancellationToken).ConfigureAwait(false);
741+
742+
if (rotationResponse.IsNotFound || rotationResponse.Result?.Data is null)
743+
return NotFound();
744+
745+
var rotation = rotationResponse.Result.Data;
746+
747+
var authResult = await CheckAuthorizationAsync(
748+
authorizationService,
749+
rotation.GameType,
750+
AuthPolicies.ManageMapRotations,
751+
nameof(CancelOperation),
752+
"MapRotation").ConfigureAwait(false);
753+
754+
if (authResult != null)
755+
return authResult;
756+
757+
var updateResult = await repositoryApiClient.MapRotations.V1.UpdateAssignmentOperation(
758+
operationId, AssignmentOperationStatus.Cancelled, "Manually cancelled by user").ConfigureAwait(false);
759+
760+
if (updateResult.IsSuccess)
761+
{
762+
// Also reset the assignment state so the user can retry
763+
var deploymentReset = assignment.DeploymentState is DeploymentState.Syncing or DeploymentState.Removing
764+
? DeploymentState.Failed
765+
: (DeploymentState?)null;
766+
767+
var activationReset = assignment.ActivationState is ActivationState.Activating or ActivationState.Deactivating
768+
? ActivationState.Inactive
769+
: (ActivationState?)null;
770+
771+
if (deploymentReset.HasValue || activationReset.HasValue)
772+
{
773+
var resetDto = new UpdateMapRotationServerAssignmentDto(assignmentId)
774+
{
775+
DeploymentState = deploymentReset,
776+
ActivationState = activationReset,
777+
LastError = "Operation cancelled by user",
778+
LastErrorAt = DateTime.UtcNow
779+
};
780+
781+
await repositoryApiClient.MapRotations.V1.UpdateServerAssignment(resetDto, cancellationToken).ConfigureAwait(false);
782+
}
783+
784+
this.AddAlertSuccess("Operation cancelled successfully.");
785+
}
786+
else
787+
{
788+
this.AddAlertDanger("Failed to cancel operation. Please try again.");
789+
}
790+
791+
return RedirectToAction(nameof(AssignmentStatus), new { id = assignmentId });
792+
}, nameof(CancelOperation)).ConfigureAwait(false);
793+
}
726794
}

src/XtremeIdiots.Portal.Web/Views/MapRotations/AssignmentStatus.cshtml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<th>Duration</th>
166166
<th>Instance</th>
167167
<th>Error</th>
168+
<th></th>
168169
</tr>
169170
</thead>
170171
<tbody>
@@ -176,9 +177,10 @@
176177
<td colspan="3"><span class="text-muted">Operation is being initialized...</span></td>
177178
<td><code class="small" title="@pendingInstanceId">@(pendingInstanceId.Length > 20 ? pendingInstanceId[..20] + "" : pendingInstanceId)</code></td>
178179
<td></td>
180+
<td></td>
179181
</tr>
180182
<tr class="progress-detail-row">
181-
<td colspan="7" class="p-0 border-0">
183+
<td colspan="8" class="p-0 border-0">
182184
<div class="px-3 py-2 bg-light">
183185
<div class="d-flex justify-content-between mb-1">
184186
<span class="progress-label">Waiting for orchestration to start...</span>
@@ -254,11 +256,24 @@
254256
<span class="text-danger" title="@op.Error">@(op.Error.Length > 60 ? op.Error[..60] + "..." : op.Error)</span>
255257
}
256258
</td>
259+
<td>
260+
@if (isInProgress)
261+
{
262+
<form policy="@AuthPolicies.ManageMapRotations" asp-action="CancelOperation" method="post" class="d-inline">
263+
@Html.AntiForgeryToken()
264+
<input type="hidden" name="operationId" value="@op.MapRotationAssignmentOperationId" />
265+
<input type="hidden" name="assignmentId" value="@Model.Assignment.MapRotationServerAssignmentId" />
266+
<button type="submit" class="btn btn-outline-danger btn-sm" onclick="return confirm('Cancel this operation? The assignment state will be reset so you can retry.')">
267+
<i class="fa-solid fa-ban"></i> Cancel
268+
</button>
269+
</form>
270+
}
271+
</td>
257272
</tr>
258273
@if (isInProgress)
259274
{
260275
<tr class="progress-detail-row">
261-
<td colspan="7" class="p-0 border-0">
276+
<td colspan="8" class="p-0 border-0">
262277
<div class="px-3 py-2 bg-light">
263278
@if (string.IsNullOrEmpty(op.DurableFunctionInstanceId))
264279
{

0 commit comments

Comments
 (0)